You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:15:28