26

嘿,我想弄清楚。如何保留一个简单的节点。当我在 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)

我尝试添加一个平面/锚节点并将“身体”节点放入其中,但它仍然移动。我想也许它与更新功能有关。

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

或者 很可能是位置设置

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

我浏览了互联网上的每个源/项目文件/视频,没有人能简单地解决这个简单的问题。

4

2 回答 2

31

这里可能发生两种“四处走动”。


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

除了希望 Apple 制造具有更好传感器、更好摄像头或更好 CPU/GPU 的设备并改进世界跟踪科学之外,你真的无能为力。(在时间充裕的情况下,这可能是一个安全的选择,尽管这可能对您当前的项目没有帮助。)


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

这种运动通常是一件好事——如果你想让你的虚拟对象看起来锚定在现实世界的表面上,你想确定那个表面在哪里。当平面检测更确定表面的位置时,您会看到一些移动,但在短时间内,您应该看到更少的“滑动”,因为您移动相机以获得平面锚定的虚拟对象,而不是那些只是漂浮在世界中的对象空间。


但是,在您的代码中,您没有利用平面检测来使您的自定义内容(来自“PlayerModel.scn”)坚持到平面锚点:

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

此代码使用平面锚点的初始位置wrapperNode在世界空间中定位(因为您将其设为根节点的子节点)。如果您改为创建wrapperNode平面锚节点的子节点(您在 中收到的节点renderer(_:didAdd:for:)),它将保持连接到平面,因为 ARKit 会改进其对平面位置的估计。最初你会得到更多的运动,但随着平面检测“稳定”,你的虚拟对象将“滑动”更少。

(当你让节点成为平面的子节点时,你不需要设置它的位置——零的位置意味着它就在平面所在的位置。无论如何,你只需要设置它相对于平面的位置——即上面/下面/沿着它多远。)

于 2017-07-12T23:57:57.653 回答
0

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

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

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

于 2019-10-01T08:30:52.730 回答