ARKIT中获取当前聚焦摄像头可见ARAnchors的技术问题咨询
我明白你的问题了——当摄像头转向新的墙面后,你想拿到当前视野内的平面锚点,而不是最早检测到的那个。咱们来一步步解决这个问题:
问题根源
你当前的代码sceneView.session.currentFrame?.anchors.first会直接取锚点数组里的第一个元素,也就是最早被检测到的锚点(第一面墙),完全不管它现在是否还在摄像头视野内,这就是问题所在。
解决方案:筛选当前可见/被追踪的平面锚点
我们需要从锚点列表中过滤出符合「当前在视野内/ARKit正在追踪」条件的平面锚点,再选择对应的目标锚点。这里提供几种实用的实现方式:
1. 基于ARKit追踪状态筛选(最简单可靠)
ARKit的ARPlaneAnchor自带isTracked属性,当该平面处于摄像头视野内且ARKit仍在主动追踪它时,这个属性会返回true。我们可以利用它来筛选:
// 筛选出当前正在被追踪的平面锚点 if let trackedPlaneAnchors = sceneView.session.currentFrame?.anchors .compactMap { $0 as? ARPlaneAnchor } // 只保留平面锚点 .filter { $0.isTracked } { // 只保留正在被追踪的平面 // 取最新检测到的追踪平面(新锚点会被追加到数组末尾) if let currentVisibleAnchor = trackedPlaneAnchors.last { if let node = sceneView.node(for: currentVisibleAnchor) { addNode(position: SCNVector3Zero, anchor: currentVisibleAnchor) } } }
2. 基于摄像头视野精准判断(更严格)
如果isTracked的判断不够精准,我们可以把平面的中心坐标投影到屏幕上,直接判断它是否在当前摄像头的可视范围内:
// 自定义方法:获取当前视野内的平面锚点 func getVisiblePlaneAnchor() -> ARPlaneAnchor? { guard let frame = sceneView.session.currentFrame else { return nil } return frame.anchors .compactMap { $0 as? ARPlaneAnchor } .filter { anchor in // 将平面中心的3D坐标转换为屏幕坐标 let planeCenter = SCNVector3(anchor.center.x, anchor.center.y, anchor.center.z) let screenPosition = sceneView.projectPoint(planeCenter) // 判断坐标是否在屏幕可视范围内(x/y在0-1之间,z为正表示在摄像头前方) return screenPosition.x > 0 && screenPosition.x < 1 && screenPosition.y > 0 && screenPosition.y < 1 && screenPosition.z > 0 } .last // 取最新检测到的可见平面 } // 使用示例 if let visibleAnchor = getVisiblePlaneAnchor(), let node = sceneView.node(for: visibleAnchor) { addNode(position: SCNVector3Zero, anchor: visibleAnchor) }
3. 主动维护追踪锚点列表(更灵活)
你也可以通过ARSessionDelegate的回调,自己维护一个仅包含有效追踪平面的列表,这样能更主动地管理锚点状态:
// 全局变量:维护当前被追踪的平面锚点列表 var activePlaneAnchors = [ARPlaneAnchor]() // 实现ARSessionDelegate方法 func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { for anchor in anchors { if let planeAnchor = anchor as? ARPlaneAnchor { activePlaneAnchors.append(planeAnchor) // 这里可以添加平面节点的初始化逻辑 } } } func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) { for anchor in anchors { if let planeAnchor = anchor as? ARPlaneAnchor { // 更新列表中的锚点数据 if let index = activePlaneAnchors.firstIndex(of: planeAnchor) { activePlaneAnchors[index] = planeAnchor } } } // 移除不再被追踪的锚点 activePlaneAnchors = activePlaneAnchors.filter { $0.isTracked } } // 获取当前有效锚点 if let currentAnchor = activePlaneAnchors.last { if let node = sceneView.node(for: currentAnchor) { addNode(position: SCNVector3Zero, anchor: currentAnchor) } }
核心思路总结
不要直接取锚点数组的第一个元素,而是通过筛选条件(追踪状态/视野范围)缩小范围,再选择最新检测到的锚点(数组末尾元素),这样就能精准拿到当前摄像头聚焦的第二面墙的锚点了。
内容的提问来源于stack exchange,提问作者Aasna Islam
相关产品推荐
相关产品推荐

