首先...如果您的应用程序仅限于设备被永久安装并且用户永远无法移动或旋转它的情况,那么使用 ARKit 在相机源上显示覆盖内容有点像“用大炮杀死蚊子”的那种的情况。您也可以在开发时确定您的 3D 引擎需要哪种相机投影,使用“哑”相机馈送并在顶部运行您的 3D 引擎,并且不需要 iOS 11 或支持 ARKit 的设备。
因此,在您承诺特定的解决方案和变通方法之前,您可能需要多考虑一下您的用例或技术堆栈。
至于你更具体的问题......
ARPlaneAnchor
完全是一个只读类,因为它的用例完全是只读的。它存在的唯一目的是为 ARKit 提供一种向您提供有关检测到的平面的信息的方法。然而,一旦你有了这些信息,你就可以随心所欲地使用它。从那里开始,您不再需要保留ARPlaneAnchor
在等式中。
也许您会因为平面检测(和基于 SceneKit 的显示)的典型用例而感到困惑:
- 开启平面检测
- 响应
renderer(_:didAdd:for:)
接收ARPlaneAnchor
对象
- 在该方法中,返回虚拟内容以与平面锚点关联
- 让
ARSCNView
自动为您定位该内容,使其跟随飞机的位置
但是,如果您的飞机相对于相机的位置是静态的,则您不需要所有这些。
如果放置需要持续管理,您只需要 ARKit 来处理场景中内容的放置,例如平面检测是实时的(ARKit 改进其对平面位置和范围的估计并相应地更新锚点)。如果您提前完成了所有的飞机查找,您将不会获得更新,因此您不需要 ARKit 来管理更新。
相反,您的步骤可能看起来更像这样:
- 知道飞机在哪里(在世界空间中的位置)。
- 将您的虚拟内容的位置设置为平面的位置。
- 直接将内容添加到场景中。
换句话说,您的“解决方案 2”是朝着正确方向迈出的一步,但还远远不够。您希望归档的不是ARPlaneAnchor
实例本身,而是它包含的信息——然后在取消归档时,您不需要重新创建ARPlaneAnchor
实例,只需要使用该信息即可。
因此,如果这是您使用“实时”平面检测放置内容的方法:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
let extent = planeAnchor.extent
let center = planeAnchor.center
// planeAnchor.transform not used, because ARSCNView automatically applies it
// to the container node, and we make a child of the container node
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center
node.addChildNode(planeNode)
}
然后你可以为静态内容放置做这样的事情:
struct PlaneInfo { // something to save and restore ARPlaneAnchor data
let transform: float4x4
let center: float3
let extent: float3
}
func makePlane(from planeInfo: PlaneInfo) { // call this when you place content
let extent = planeInfo.extent
let center = float4(planeInfo.center, 1) * planeInfo.transform
// we're positioning content in world space, so center is now
// an offset relative to transform
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center.xyz
view.scene.rootNode.addChildNode(planeNode)
}
// convenience vector-width conversions used above
extension float4 {
init(_ xyz: float3, _ w: Float) {
self.init(xyz.x, xyz.y, xyz.z, 1)
}
var xyz: float3 {
return float3(self.x, self.y, self.z)
}
}