使用ARAnchor插入节点和直接插入节点有什么区别?

What's the difference between using ARAnchor to insert a node and directly insert a node?

在 ARKit 中,我找到了两种在 hitTest 之后插入节点的方法

  1. 插入一个 ARAnchor 然后在 renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?

    中创建节点
     let anchor = ARAnchor(transform:hit.worldTransform)
     sceneView.session.add(anchor:anchor)
    
  2. 直接插入节点

     node.position = SCNVector3(hit.worldTransform.columns.3.x, hit.worldTransform.columns.3.y, hit.worldTransform.columns.3.z)
     sceneView.scene.rootNode.addChildNode(node)
    

两者都对我有用,但为什么要这样或那样呢?

SCNVector3 只是 "a representation of a three-component vector." SCNVector3 docs.

使用 ARAnchor 时,您可以访问三分量向量,而且您还可以 "to track the positions and orientations of real or virtual objects relative to the camera" ARAnchor docs。这就是为什么您使用会话而不是使用场景来添加锚点。

查看文档,您可以看到 API 方面的差异 :)

希望对您有所帮助。

更新: 从 iOS 11.3(又名 "ARKit 1.5")开始, 添加之间的区别ARAnchor 到会话(然后通过 ARSCNViewDelegate 回调将 SceneKit 内容与其关联),然后将内容放入 SceneKit space.

当您向会话添加锚点时,您是在告诉 ARKit 世界上的某个点 space 与您的应用相关。然后 ARKit 可以做一些额外的工作来确保它的世界坐标 space 与现实世界准确对齐,至少在该点附近。

因此,如果您想让虚拟内容 "attached" 出现在某些现实世界的兴趣点上,例如将物体放在 table 或墙上,您应该看到更少 "drift" 由于世界跟踪不准确,如果你给那个对象一个锚点而不是你只是把它放在 SceneKit space 中。如果该对象从一个静态位置移动到另一个静态位置,您将需要删除原始锚点,然后在新位置添加一个。

此外,在 iOS 11.3 中,您可以 opt in 到 "relocalization",一个帮助 ARKit 在会话被中断后恢复会话的过程(通过 phone 调用,切换应用程序等)。该会话在尝试弄清楚如何将您之前所在的位置映射到您现在所在的位置时仍然有效,这可能会导致 world-space 锚点的位置在重新定位成功后发生变化。

(另一方面,如果你只是让 space 漂浮在空中的入侵者,那么完美匹配的世界 space 就不那么重要了,因此你不会真的看到基于锚定和非基于锚定的定位之间有很大区别。)

请参阅 Apple 的 Handling 3D Interaction and UI Controls in Augmented Reality 文章/示例代码中 "Use anchors to improve tracking quality around virtual objects" 的内容。

此答案的其余部分在历史上与 iOS 11.0-11.2.5 保持相关并解释了一些上下文,因此我将其留在下面...


首先考虑在没有 SceneKit 的情况下使用ARAnchor

  • 如果您正在使用 ARSKView,您需要一种方法来参考 3D(真实世界)中的位置/方向 space,因为 SpriteKit 不是 3D。您需要 ARAnchor 来跟踪 3D 中的位置,以便它们可以映射到 2D 中。

  • 如果您使用 Metal(或 GL,出于某种奇怪的原因)构建自己的引擎...那不是 3D 场景描述 API — 它是 GPU 编程 API — 所以它并没有真正的世界概念 space。您可以使用 ARAnchor 作为 ARKit 的世界概念 space 和您构建的任何内容之间的桥梁。

所以在某些情况下您需要 ARAnchor 因为这是引用 3D 位置的唯一明智方式。 (当然,如果你使用平面检测,你需要 ARPlaneAnchor 因为 ARKit 实际上会移动那些相对于场景 space 因为它改进了它对平面位置的估计。)


有了 ARSCNView,SceneKit 已经有了一个 3D 世界坐标 space,ARKit 完成了使 space 与现实世界匹配的所有工作 space ARKit映射出来。因此,给定描述世界 space 中位置(和方向等)的 float4x4 变换,您可以:

  • 创建一个 ARAnchor,将其添加到会话中,并响应 ARSCNViewDelegate 回调为每个锚点提供 SceneKit 内容,ARKit 将为您添加到场景中并在场景中定位。
  • 创建一个 SCNNode,设置其 simdTransform,并将其添加为场景 rootNode 的子项。

只要您有 运行 ARSession,这两种方法之间就没有区别 — 它们是说同一件事的等价方式。所以如果你喜欢用 SceneKit 的方式做事,那没有错。 (如果你愿意,你甚至可以使用 SCNVector3SCNMatrix4 而不是 SIMD 类型,但是如果你还从 ARKit APIs 获取 SIMD 类型,你将不得不来回转换.)


这些方法的唯一不同之处在于重置会话时。如果世界跟踪失败,您将恢复中断的会话,and/or 您重新开始一个会话,"world space" 可能不再像您在场景中放置内容时那样与现实世界保持一致。

在这种情况下,您可以让 ARKit 从会话中删除锚点——请参阅 run(_:options:) method and ARSession.RunOptions。 (是的,所有这些,因为此时您不能再相信它们中的任何一个是有效的。)如果您使用锚点和委托回调将内容放置在场景中,ARKit 将破坏所有内容。 (您会收到它被删除的委托回调。)如果您使用 SceneKit API 放置内容,它会保留在场景中(但很可能在错误的位置)。

因此,使用哪种取决于您希望如何处理会话失败和中断(除此之外没有真正的区别)。