如何将 ARKit SCNNode 保持在原位

How to keep ARKit SCNNode in place

嘿,我正在想办法。如何保持一个简单的节点到位。当我在 ARKit 中四处走动时

代码:

 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    if let planeAnchor = anchor as? ARPlaneAnchor {

    if planeDetected == false { // Bool only allows 1 plane to be added
            planeDetected = true
        self.addPlane(node: node, anchor: planeAnchor)
    }

    }
}

这将添加 SCNNode

    func addPlane(node: SCNNode, anchor: ARPlaneAnchor) {

    // We add the anchor plane here

    let showDebugVisuals = Bool()
    let plane = Plane(anchor, showDebugVisuals)
    planes[anchor] = plane
    node.addChildNode(plane)



    // We add our custom SCNNode here 

    let scene = SCNScene(named: "art.scnassets/PlayerModel.scn")!
    let Body = scene.rootNode.childNode(withName: "Body", recursively: true)!

    Body.position = SCNVector3.positionFromTransform(anchor.transform)
    Body.movabilityHint = .movable
    wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)

    wrapperNode.addChildNode(Body)

    scnView.scene.rootNode.addChildNode(wrapperNode)

我尝试添加一个 Plane/Anchor 节点并将 "Body" 节点放入其中,但它仍然移动。我还以为跟update函数有关系呢

 func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
 }

最有可能的位置设置

wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)

我查看了互联网上的每个源/项目文件/视频,但没有人对这个简单的问题有简单的解决方案。

这里可能会发生两种 "moving around"。


一个是 ARKit 不断完善其对设备在现实世界中的位置如何映射到您放置虚拟内容的抽象坐标的估计 space。例如,假设您放置了一个虚拟对象在 (0, 0, -0.5),然后将您的设备向左移动整整 10 厘米。仅当 ARKit 精确跟踪移动时,虚拟对象才会出现在物理 space 中。但是视觉惯性里程计并不是一门精确的科学,所以 ARKit 可能认为你向左移动了 10.5 厘米——在这种情况下,你的虚拟对象看起来 "slip" 向右移动了 5 毫米,即使尽管它在 ARKit/SceneKit 坐标 space 中的位置保持不变。

除了希望 Apple 制造具有更好的传感器、更好的相机或更好的设备 CPUs/GPUs 并改进世界跟踪科学之外,您对此无能为力。 (在适当的时候,这可能是一个安全的选择,尽管这可能对您当前的项目没有帮助。)


由于您还处理平面检测,因此还有另一个问题。 ARKit 不断完善其对检测到的平面所在位置的估计。所以,即使飞机在现实世界中的位置没有改变,它在 ARKit/SceneKit 坐标 space 中的位置是。

这种移动通常是一件好事 — 如果您希望虚拟对象看起来锚定在真实世界的表面上,您需要确定该表面的位置。随着平面检测更加确定表面的位置,您会看到一些移动,但在短时间后,当您为平面锚定的虚拟对象移动相机时,您应该看到的 "slip" 比那些只是浮动的对象少在世界 space.


不过,在您的代码中,您没有利用平面检测来使您的自定义内容(来自 "PlayerModel.scn")固定在平面锚点上:

wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)
wrapperNode.addChildNode(Body)
scnView.scene.rootNode.addChildNode(wrapperNode)

此代码使用平面锚点的 初始 位置来定位 wrapperNode 在世界 space 中(因为您正在使它成为根节点)。如果您改为将 wrapperNode 设为平面锚点节点(您在 renderer(_:didAdd:for:) 中收到的那个节点)的子节点,它将在 ARKit 改进其对平面位置的估计时保持与平面的连接。最初你会得到更多的运动,但随着平面检测 "settles",你的虚拟对象将 "slip" 减少。

(当你使节点成为平面的子节点时,你不需要设置它的位置 - 位置为零意味着它就在平面所在的位置。无论如何,你只需要设置它的相对位置到飞机——即 above/below/along 多远。)

要保持​​ SCNNode 就位,您可以在获得所需结果后禁用 sceneView 平面检测。

let configuration = ARWorldTrackingConfiguration(); configuration.planeDetection = [] self.sceneView.session.run(configuration)

这是因为 ARKit 不断地重新估计检测到的平面的位置,导致您的 SCNNode 移动。