您已经完成了大部分工作 - 您的代码将一个平面放置在检测到的图像上方,因此很明显,那里正在发生一些事情,成功地将平面的中心位置设置为图像锚点的中心位置。也许您的第一步应该是更好地理解您拥有的代码......
ARPlaneAnchor
has a center
(and extent
)因为在 ARKit 最初检测到平面后,平面可以有效地生长。当你第一次获得平面锚时,它transform
告诉您一小块水平(或垂直)平面的位置和方向。仅此一点就足以让您将一些虚拟内容放置在那一小块表面的中间。
随着时间的推移,ARKit 会找出更多相同平坦表面的位置,因此平面锚点的extent
变大。但你可能一开始会检测到桌子的一端,然后识别出更多远端——这意味着平坦的表面并不以检测到的第一个补丁为中心。而不是改变transform
主播的新情况,ARKit告诉你center
(这是关系到进行变换)。
An ARImageAnchor
不会增长——ARKit 要么立即检测到整个图像,要么根本不检测到图像。因此,当您检测到图像时,锚点的transform
告诉您图像中心的位置和方向。 (如果你想知道大小/范围,你可以从physicalSize
检测到的参考图像的,就像示例代码一样。)
因此,要将一些 SceneKit 内容放置在ARImageAnchor
(或任何其他ARAnchor
子类),您可以:
只需将其添加为子节点即可SCNNode
ARKit 在该委托方法中为您创建。如果您不做任何事情来改变它们,它的位置和方向将与拥有它的节点的位置和方向相匹配。 (这就是您引用的 Apple 示例代码的作用。)
-
将其放置在世界空间中(即,作为场景的子级)rootNode
),使用锚点transform
获取位置或方向或两者。
(You can extract the translation — that is, relative position — from a transform matrix: grab the first three elements of the last column; e.g. transform.columns.3
is a float4
vector whose xyz elements are your position and whose w element is 1.)
不过,您链接到的演示视频并没有将事物放入 3D 空间中,而是将 2D UI 元素放在屏幕上,其位置跟踪锚点在世界空间中相对于 3D 相机的移动。
你可以很容易地得到kind效果(第一近似值)通过使用ARSKView
(ARKit+SpriteKit)而不是ARSCNView
(ARKit+SceneKit)。这使您可以将 2D 精灵与世界空间中的 3D 位置相关联,然后ARSKView
自动移动和缩放它们,使它们看起来保持附着在这些 3D 位置上。这是一种常见的 3D 图形技巧,称为“广告牌”,其中 2D 精灵始终保持直立并面向相机,但会四处移动和缩放以匹配 3D 视角。
如果这就是您正在寻找的效果,也有一个应用程序(示例代码)。这通过 ARKit 实时使用视觉 https://developer.apple.com/documentation/arkit/using_vision_in_real_time_with_arkit示例主要是关于其他主题,但它does展示如何使用ARSKView
显示与关联的标签ARAnchor
职位。 (正如您在上面所看到的,放置内容以匹配锚点位置是相同的,无论哪种ARAnchor
您正在使用的子类。)这是他们代码中的关键部分:
func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
// ... irrelevant bits omitted...
let label = TemplateLabelNode(text: labelText)
node.addChild(label)
}
也就是说,只需执行ARSKView
didAdd
delegate 方法,并添加您想要的任何 SpriteKit 节点作为 ARKit 提供的子节点。
然而,演示视频不仅仅是精灵广告牌:它与绘画相关的标签不仅保持固定在 2D 方向,而且保持固定的 2D 尺寸(也就是说,它们不会像广告牌精灵那样缩放以模拟透视) 。更重要的是,它们似乎是 UIKit 控件,具有全套继承的交互行为,而不仅仅是 2D 图像(与 SpriteKit 类似)。
Apple 的 API 并未提供“开箱即用”的直接方法,但不难想象将 API 各个部分组合在一起以获得这种结果的某些方法。以下是一些值得探索的途径:
-
如果你不需要 UIKit 控件,你可能可以在 SpriteKit 中完成这一切,使用约束来匹配“广告牌”节点的位置ARSKView
提供但不提供其规模。可能看起来像这样(未经测试,买者自负):
func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
let label = MyLabelNode(text: labelText) // or however you make your label
view.scene.addChild(label)
// constrain label to zero distance from ARSKView-provided, anchor-following node
let zeroDistanceToAnchor = SKConstraint.distance(SKRange(constantValue: 0), to: node)
label.constraints = [ zeroDistanceToAnchor ]
}
如果您想要 UIKit 元素,请制作ARSKView
视图控制器的子视图(不是根视图),并将这些 UIKit 元素设置为其他子视图。然后,在你的 SpriteKit 场景中update
方法,通过你的ARAnchor
- 以下节点,将它们的位置从 SpriteKit 场景坐标转换为 UIKit 视图坐标,并相应地设置 UIKit 元素的位置。 (该演示似乎正在使用弹出窗口,因此您不会将其作为子视图进行管理......您可能会更新sourceRect https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/1622324-sourcerect对于每个弹出窗口。)这涉及更多内容,因此详细信息超出了这个已经很长的答案的范围。
最后一点……希望这个冗长的答案对关键有所帮助issues您的问题(了解锚点位置并在相机移动时放置跟随它们的 3D 或 2D 内容)。
但要澄清一些关键问题并发出警告words在你的问题的早期:
当 ARKit 说它在检测后不跟踪图像时,这意味着它不知道图像何时/是否移动(相对于周围的世界)。 ARKit 仅报告一次图像的位置,因此该位置甚至无法从 ARKit 不断改进其对您周围世界以及您在其中的位置的估计中受益。例如,如果图像在墙上,则报告的图像位置/方向可能与墙上的垂直平面检测结果不一致(尤其是随着时间的推移,随着平面估计的改进)。
Update:在 iOS 12 中,您can启用对检测到的图像的“实时”跟踪。但一次可以追踪的数量是有限制的,因此本建议的其余部分可能仍然适用。
这并不意味着您不能放置看起来“跟踪”世界空间中的静态位置的内容,即随着相机的移动而在屏幕上移动以跟随它。
但这确实意味着,如果您尝试执行依赖于图像位置高精度实时估计的操作,您的用户体验可能会受到影响。因此,不要尝试在你的画作周围放置虚拟框架,或者用动画版本替换画作。但有一个带有箭头的文本标签roughly图像在太空中的位置非常棒。