如何让ARKit的SCNNode保持位置不变

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)

我尝试添加一个 平面/锚点 节点并把 "Body" 节点放在其中,但它仍然会移动。我想也许与更新函数有关。

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

或者 最有可能是位置设置

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

我已经查阅了互联网上的所有资源、项目文件和视频,但没有人提供一个简单的解决方案来解决这个简单的问题。

我已经查阅了互联网上的所有资源、项目文件和视频,但没有人提供一个简单的解决方案来解决这个简单的问题。


没有简单的解决方案。你应该使用表面检测以获得更好的结果,但物体仍然会有一些移动。 - Vasilii Muravev
好的,谢谢您确认这并不是一个简单的解决方案。 - Hunter
2个回答

31

这里可能发生了两种“移动”。其一,ARKit不断地调整设备在现实世界中的位置,以匹配你放置虚拟内容时使用的抽象坐标空间。例如,假设你将一个虚拟对象放在(0, 0, -0.5),然后将设备向左移动10厘米。只有当ARKit精确地跟踪到移动时,虚拟对象才会看起来锚定在物理空间中。但是,视觉惯性导航并不是一门精确的科学,因此ARKit可能会认为你向左移动了10.5厘米——在这种情况下,即使虚拟对象在ARKit/SceneKit坐标空间中的位置保持不变,它也会看起来向右“滑动”5毫米。

你无法真正解决这个问题,除非希望苹果制造更好的传感器、更好的相机或更好的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会不断完善对平面位置的估计。虽然最初会有一点点移动,但是随着平面检测的“稳定”,您的虚拟对象将“滑动”减少。

(当您将节点作为飞机的子级时,您不需要设置其位置——零位置意味着它就在平面上。如果有必要,您只需要相对于平面设置其位置——即离它有多远/高/靠近。)


1
Apple Dev在这个项目中提供了更多详细的示例,链接如下: https://github.com/gao0122/ARKit-Example-by-Apple 它很好地解释了所有内容,而且简单易懂。 - mychar
@rickster非常感谢您的深入回复。我也遇到了与OP相同的问题,当我离开3D模型1-3米时,有时会出现20厘米的漂移。这是预期的吗?为什么宜家Place的家具物品不会移动那么多呢?谢谢! - aviggiano
@aviggiano,我在我们的AR项目中也遇到了这样的问题。只是想确认一下,你是否找到了解决方案? - a.palo
@a.palo 不好意思,我没有找到解决方案,但是由于我不再开发AR应用程序,所以我停止了尝试。 - aviggiano
嘿@Rickster,非常感谢你详细的回答!我和我的团队陷入了一个问题几天啊 :/ 我发布了一个有趣的问题:https://stackoverflow.com/questions/63662318/arkit-apply-filter-cifilter-to-a-specific-part-vertex-of-an-arfaceanchor 希望得到你的建议! - Roi Mulia

0
为了保持SCNNode的位置不变,您可以在获得所需结果后禁用sceneView平面检测。 let configuration = ARWorldTrackingConfiguration(); configuration.planeDetection = [] self.sceneView.session.run(configuration)
原因是ARKit会不断重新估计检测到平面的位置,导致您的SCNNode移动。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接