AR参考图像平面在iOS Swift中位置不正确?

我正在使用ar参考图像处理名片档案信息。 当参考检测到它将显示公司首席执行官详细信息,地址,照片和团队成员信息等。最初检测到图像,它将使用runAction amintion向右移动。

我的问题是检测到的,飞机位置稳定,它在这里和那里移动。 如何使用参考图像拟合平面位置。

这是我的结果截图:[![在此输入图像描述] [1]] [1]

这是我使用的代码:

var weboverlayview: CALayer? var loadWeb: UIWebView? override func viewDidLoad() { super.viewDidLoad() // Set the view's delegate sceneView.delegate = self // Show statistics such as fps and timing information sceneView.showsStatistics = true sceneView.autoenablesDefaultLighting = true let ARScene = SCNScene() sceneView.scene = ARScene } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = [.vertical, .horizontal] configuration.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) // Run the view's session sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Pause the view's session sceneView.session.pause() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Release any cached data, images, etc that aren't in use. } func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { let anchorNode = SCNNode() anchorNode.name = "anchor" sceneView.scene.rootNode.addChildNode(anchorNode) return anchorNode } func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { var labelNode:SCNNode? var companyLabelNode: SCNNode? var addressLabelNode: SCNNode? var webaddressLabelNode: SCNNode? var maillabelNode: SCNNode? var mobileLabelNode:SCNNode? var teamLabelNode:SCNNode? guard let imageAnchor = anchor as? ARImageAnchor else {return} if let imageName = imageAnchor.referenceImage.name { print(imageName) if imageName == "card"{ let plane = SCNPlane(width: 20,height: 24) plane.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75) plane.cornerRadius = 0.25 let planeNodee = SCNNode(geometry: plane) planeNodee.eulerAngles.x = -.pi / 2 planeNodee.runAction(SCNAction.moveBy(x: -5, y: 0, z: 19, duration: 0.75)) labelNode = self.addLabel(text: "Gowdhaman Kandasamy \nFounder and CEO", anchor: imageAnchor) labelNode?.runAction(SCNAction.moveBy(x: -1.3, y: 1, z: 16.8, duration: 0.75)) companyLabelNode = self.addLabel(text: "CZ Smart Mobility", anchor: imageAnchor) companyLabelNode?.runAction(SCNAction.moveBy(x: 1.5, y: 1, z: 22, duration: 0.75)) addressLabelNode = self.addAddressLabel(text: "Official Address:\n\n1st floor, TBI Office,\nDr.col JEPPIAR Research Park,\nResearch and development center,\nSathyabama University,\nChennai-600119\nTamil nadu, India.", anchor: imageAnchor) addressLabelNode?.runAction(SCNAction.moveBy(x: -4.8, y: 1, z: 16.8, duration: 0.75)) let userImagePlane = SCNPlane(width: 3.5, height: 3.5) userImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "gow") userImagePlane.cornerRadius = 0.25 let userPlaneNode = SCNNode(geometry: userImagePlane) userPlaneNode.eulerAngles.x = -.pi/2 userPlaneNode.runAction(SCNAction.moveBy(x: -1, y: 1, z: 9.5, duration: 0.75)) let webImagePlane = SCNPlane(width: 1, height: 1) webImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "web") webImagePlane.cornerRadius = 0.25 let webPlanenode = SCNNode(geometry: webImagePlane) webPlanenode.eulerAngles.x = -.pi/2 webPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 12.5, duration: 0.75)) webaddressLabelNode = addAddressLabel(text: "www.czsm.co.in", anchor: imageAnchor) webaddressLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 18.2, duration: 0.75)) let mailImagePlane = SCNPlane(width: 1, height: 1) mailImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mail") mailImagePlane.cornerRadius = 0.25 let mailPlanenode = SCNNode(geometry: mailImagePlane) mailPlanenode.eulerAngles.x = -.pi/2 mailPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 17.5, duration: 0.75)) maillabelNode = addAddressLabel(text: "gowdhaman@czsm.co.in", anchor: imageAnchor) maillabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 23.2, duration: 0.75)) let mobileImagePlane = SCNPlane(width: 1, height: 1) mobileImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mobile") mobileImagePlane.cornerRadius = 0.25 let mobilePlanenode = SCNNode(geometry: mobileImagePlane) mobilePlanenode.eulerAngles.x = -.pi/2 mobilePlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 23.9, duration: 0.75)) mobileLabelNode = addAddressLabel(text: "+919941123110", anchor: imageAnchor) mobileLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 29.7, duration: 0.75)) /************Team members*************/ teamLabelNode = self.addLabel(text: "Team Members", anchor: imageAnchor) teamLabelNode?.runAction(SCNAction.moveBy(x: -9.8, y: 1, z: 22, duration: 0.75)) let sivaImagePlane = SCNPlane(width: 2.7, height: 2.7) sivaImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic2") sivaImagePlane.cornerRadius = 0.25 let sivaPlanenode = SCNNode(geometry: sivaImagePlane) sivaPlanenode.eulerAngles.x = -.pi/2 sivaPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75)) let parameshImagePlane = SCNPlane(width: 2.7, height: 2.7) parameshImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic4") parameshImagePlane.cornerRadius = 0.25 let parameshPlanenode = SCNNode(geometry: parameshImagePlane) parameshPlanenode.eulerAngles.x = -.pi/2 parameshPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75)) node.addChildNode(planeNodee) node.addChildNode(labelNode!) node.addChildNode(companyLabelNode!) node.addChildNode(userPlaneNode) node.addChildNode(addressLabelNode!) node.addChildNode(webPlanenode) node.addChildNode(webaddressLabelNode!) node.addChildNode(mailPlanenode) node.addChildNode(maillabelNode!) node.addChildNode(mobilePlanenode) node.addChildNode(mobileLabelNode!) node.addChildNode(teamLabelNode!) node.addChildNode(sivaPlanenode) node.addChildNode(parameshPlanenode) // node.addChildNode(czwebPlaneNode) self.sceneView.scene.rootNode.addChildNode(node) } } } func webButton() { } func addLabel(text: String, anchor: ARImageAnchor) -> SCNNode { let plane = SCNPlane(width: 10, height: 4) let planeNode = SCNNode(geometry: plane) planeNode.eulerAngles.x = (-.pi)/2 planeNode.eulerAngles.y = (-.pi)/2 // planeNode.eulerAngles.z = (-.pi)/2 let skScene = SKScene(size: CGSize(width: 400, height: 100)) skScene.backgroundColor = UIColor.clear let substrings: [String] = text.components(separatedBy: "\n") for aSubstring in substrings { let lbl = SKLabelNode(text: aSubstring) lbl.fontSize = 18 lbl.numberOfLines = 1 lbl.fontColor = UIColor.white lbl.fontName = "Avenir-medium" let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize print("yname::::\(y)") lbl.position = CGPoint(x: 0, y: y) lbl.horizontalAlignmentMode = .left lbl.yScale *= -1 skScene.addChild(lbl) } let material = SCNMaterial() material.isDoubleSided = false material.diffuse.contents = skScene plane.materials = [material] return planeNode } func addCompanyLabel(text: String, anchor: ARImageAnchor) -> SCNNode { let plane1 = SCNPlane(width: 10, height: 4) let planeNode1 = SCNNode(geometry: plane1) planeNode1.eulerAngles.x = (-.pi)/2 planeNode1.eulerAngles.y = (-.pi)/2 // planeNode.eulerAngles.z = (-.pi)/2 let skScene1 = SKScene(size: CGSize(width: 400, height: 100)) skScene1.backgroundColor = UIColor.clear let substrings: [String] = text.components(separatedBy: "\n") for aSubstring in substrings { let lbl1 = SKLabelNode(text: aSubstring) lbl1.fontSize = 20 lbl1.numberOfLines = 1 lbl1.fontColor = UIColor.white lbl1.fontName = "Avenir-medium" let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl1.fontSize print("ycompanname::::\(y)") lbl1.position = CGPoint(x: 0, y: y) lbl1.horizontalAlignmentMode = .left lbl1.yScale *= -1 skScene1.addChild(lbl1) } let material = SCNMaterial() material.isDoubleSided = false material.diffuse.contents = skScene1 plane1.materials = [material] return planeNode1 } func addAddressLabel(text: String, anchor: ARImageAnchor) -> SCNNode { let plane = SCNPlane(width: 10, height: 4) let planeNode = SCNNode(geometry: plane) planeNode.eulerAngles.x = (-.pi)/2 planeNode.eulerAngles.y = (-.pi)/2 // planeNode.eulerAngles.z = (-.pi)/2 let skScene = SKScene(size: CGSize(width: 500, height: 200)) skScene.backgroundColor = UIColor.clear let substrings: [String] = text.components(separatedBy: "\n") for aSubstring in substrings { let lbl = SKLabelNode(text: aSubstring) lbl.fontSize = 20 // lbl.numberOfLines = 1 lbl.fontColor = UIColor.white lbl.fontName = "Avenir-medium" let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize print("yaddress::::\(y)") lbl.position = CGPoint(x: 0, y: y) lbl.horizontalAlignmentMode = .left lbl.yScale *= -1 skScene.addChild(lbl) } let material = SCNMaterial() material.isDoubleSided = false material.diffuse.contents = skScene plane.materials = [material] return planeNode } func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { // guard let planeAnchor = anchor as? ARPlaneAnchor else {return} // let planeGeometry = planeAnchor.geometry // guard let device = MTLCreateSystemDefaultDevice() else {return} // let plane = ARSCNPlaneGeometry(device: device) // plane?.update(from: planeGeometry) // node.geometry = plane // node.geometry?.firstMaterial?.diffuse.contents = UIColor.black // node.geometry?.firstMaterial?.transparency = 1 // node.geometry?.firstMaterial?.fillMode = SCNFillMode.lines } } 

像这个ar参考平面的例外结果适合卡https://www.facebook.com/oscarfalmer/videos/10156651667309345/

您需要考虑的第一件事是您是否要使用ARWorldTrackingConfigurationARImageTrackingConfigurationIOS12 )。

如果您使用ARImageTrackingConfiguration ,则无法使用PlaneDetection,因为这是一个Image Only跟踪配置:

只有当这些图像在相机视野中时,才能将虚拟内容锚定到已知图像。 通过图像检测进行世界跟踪,您可以使用已知图像将虚拟内容添加到3D世界,并在图像不再可见时继续跟踪该内容在世界空间中的位置。

如果您希望您的内容始终保持固定在图像上(在相机视野中),这将是您最好的选择,因为:

它以六个自由度(6DOF)跟踪它们的运动:具体地说,三个旋转轴(滚动,俯仰和偏转)和三个平移轴(在x,y和z中的运动)。

另一方面,如果你想检测ARPlaneAnchors以及ARImageAnchors ,但是不要担心任何与你的ARImageAnchor相关的内容不会不断跟踪,那么你应该使用ARWorldTrackingConfiguration

正如@Trinca还说,您需要确保为图像提供的测量结果尽可能准确,因为ARKit使用这些测量值来返回图像的physicalSizephysicalWidth ,这样可以更准确地放置虚拟内容(例如,如果您指定比实际生活中图像的实际尺寸更大的尺寸,您将无法准确对齐虚拟内容)。

在此处输入图像描述

在创建名片或任何imageTarget时,我们必须确保在ARReferenceImage Settings框中准确设置我们的尺寸:

在此处输入图像描述

然后我们可以检查我们的imageTarget是否被检测到:

 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { //1. Check We Have Detected An ARImageAnchor & Check It's The One We Want guard let validImageAnchor = anchor as? ARImageAnchor, let targetName = validImageAnchor.referenceImage.name, targetName == "TargetCard" else { return} //2. Check To See The Detected Size Of Our Business Card (Should By 5cm*3cm) let businessCardWidth = validImageAnchor.referenceImage.physicalSize.width let businessCardHeight = validImageAnchor.referenceImage.physicalSize.height print( """ We Have Detected Business Card With Name \(targetName) \(targetName)'s Width Is \(businessCardWidth) \(targetName)'s Height Is \(businessCardHeight) """) } 

在检查了我们检测到的大小是否准确之后,我们可以放置我们喜欢的与此相关的任何内容。

而不是以编程方式执行所有操作,实现所需结果的更简单方法是创建SCNScene

更新:

正如您要求的示例项目,我为每个人创建了一个完整的工作示例,可以在这里下载: ARKit名片

如果没有详细介绍每个Class ,我将为您提供基本的详细信息。

我们将使用SCNScene作为可重用模板,该模板包含一系列SCNNode ,这些SCNNode用作按钮,可以在按下时执行不同的操作。

基本模板如下所示:

在此处输入图像描述

BusinessCard节点使用A BusinessCardData Struct初始化,如下所示:

 typealias SocialLinkData = (link: String, type: SocialLink) /// The Information For The Business Card Node & Contact Details struct BusinessCardData{ var firstName: String var surname: String var position: String var company: String var address: BusinessAddress var website: SocialLinkData var phoneNumber: String var email: String var stackOverflowAccount: SocialLinkData var githubAccount: SocialLinkData } /// The Associates Business Address struct BusinessAddress{ var street: String var city: String var state: String var postalCode: String var coordinates: (latittude: Double, longtitude: Double) } /// The Type Of Social Link /// /// - Website: Business Website /// - StackOverFlow: StackOverFlow Account /// - GitHub: Github Account enum SocialLink: String{ case Website case StackOverFlow case GitHub } 

因此,所提供的所有数据都映射到模板中的每个SCNNode ,并有助于执行必要的function。

通过使用struct我们可以创建多个交互式名片,例如:

 //-------------------------- //MARK: - ARSessionDelegate //-------------------------- extension ViewController: ARSCNViewDelegate{ func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { //1. Check We Have A Valid Image Anchor guard let imageAnchor = anchor as? ARImageAnchor else { return } //2. Get The Detected Reference Image let referenceImage = imageAnchor.referenceImage //3. Load Our Business Card if let matchedBusinessCardName = referenceImage.name, matchedBusinessCardName == "BlackMirrorz"{ //4. Create Our Business Card let businessCardData = BusinessCardData(firstName: "Josh", surname: "Robbins", position: "Software Engineer", company: "BlackMirrorz", address: BusinessAddress(street: "1 Infinite Loop", city: "Cupertino", state: "CA", postalCode: "95015", coordinates: (latittude: 37.3349, longtitude: -122.0090201)), website: SocialLinkData(link: "https://www.blackmirrorz.tech", type: .Website), phoneNumber: "+821076337633", email: "josh.robbins@blackmirroz.tech", stackOverflowAccount: SocialLinkData(link: "https://stackoverflow.com/users/8816868/josh-robbins", type: .StackOverFlow), githubAccount: SocialLinkData(link: "https://github.com/BlackMirrorz", type: .GitHub)) //5. Assign It To The Business Card Node let businessCard = BusinessCard(data: businessCardData, cardType: .noProfileImage) businessCardPlaced = true node.addChildNode(businessCard) } } } 

由于设计已经布局,我们不需要进行任何复杂的计算。 一切都为我们完成!

用户的交互使用以下图标完成: 在此处输入图像描述

  • StackOverFlow Button显示滑出WKWebView以显示用户StackOverFlow帐户。
  • GitHub Button显示滑出WKWebView以显示用户GitHub帐户。
  • Internet Button显示滑出WKWebView以显示用户网站。
  • Phone Button允许用户拨打商务电话号码。
  • SMS Button提供MFMessageComposeViewController允许用户向业务发送文本消息。
  • Email Button提供MFMailComposeViewController允许用户通过电子邮件发送业务。
  • Contact Button创建CNMutableContact并将业务保存为用户设备上的新联系人。
  • Location Button显示幻灯片MKMapView以显示用户“企业位置”。

由于将WKWebView呈现为SCNMaterial ,我不得不考虑其他方式来允许内容完全交互。

因此我使用了SideMenu的神话般的存储库SideMenu ,它可以在这里找到: SideMenu

这允许用户仍然体验ARKit同时允许几乎分割屏幕效果:

在此处输入图像描述

一如既往,希望它能帮助您和其他有兴趣学习ARKit的人…

我有类似的问题,这是因为设置参考图像的大小时出错。

如果您手动将它们导入“AR资源组”,则在右侧面板上键入宽度和高度时,您需要确保设置“米”或“厘米”。

如果您从服务器加载这些图像而不是将它们作为参考图像,请记住ARKit使用的默认度量标准是“米”。 在这种情况下,如果您将宽度设置为20.0,ARKit将考虑20米而不是20厘米,在跟踪图像平面时会给您带来非常不准确的行为。

希望能帮助到你。