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

ARKit中如何为AR对象仅创建一次阴影平面而非逐帧生成

解决方案

你遇到的阴影截断问题,本质是初始创建阴影平面时用了平面检测初始阶段的小范围尺寸,后续平面范围扩展时没有同步更新阴影平面参数,完全不需要逐帧销毁重建节点,只需要创建一次阴影平面,后续在锚点更新时同步修改参数即可,性能开销可以忽略。

核心实现逻辑

  • 阴影平面仅在renderer(_:didAdd:for:)回调中创建一次,通过节点name做标记,不需要额外维护hash数组遍历子节点。
  • 在renderer(_:didUpdate:for:)回调中,直接找到已创建的阴影平面节点,更新它的位置和几何尺寸即可,不需要销毁、重建节点或材质。
  • 如果你需要把阴影绑定到自己放置的可移动AR对象上,直接利用SCNFloor默认无限延展的特性,甚至不需要更新尺寸,只要对齐平面高度就能全位置承接阴影。

修正后的代码

原有的阴影材质方法不需要修改,可直接保留:

func shadowMaterialStandard() -> SCNMaterial {
    let material = SCNMaterial()
    material.lightingModel = .physicallyBased
    material.writesToDepthBuffer = true
    material.readsFromDepthBuffer = true
    material.colorBufferWriteMask = []
    return material
}

在didAdd回调中仅执行一次阴影平面创建:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
    
    // 初始化阴影平面,全局仅执行一次
    let floor = SCNFloor()
    floor.reflectivity = 0
    floor.materials = [shadowMaterialStandard()]
    
    let floorNode = SCNNode(geometry: floor)
    floorNode.name = "ARShadowFloor" // 用name标记阴影节点,替代原有hash数组逻辑
    node.addChildNode(floorNode)
    
    // 同步初始位置和尺寸
    syncShadowPlaneConfig(floorNode: floorNode, anchor: planeAnchor)
}

在didUpdate回调中仅更新参数,不重建节点:

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor,
          let floorNode = node.childNodes.first(where: { $0.name == "ARShadowFloor" }) else {
        return
    }
    // 仅更新位置、尺寸属性,无资源创建销毁开销
    syncShadowPlaneConfig(floorNode: floorNode, anchor: planeAnchor)
}

抽离的参数同步方法:

private func syncShadowPlaneConfig(floorNode: SCNNode, anchor: ARPlaneAnchor) {
    // 对齐平面中心位置
    floorNode.position = SCNVector3(anchor.center.x, 0, anchor.center.z)
    // 同步平面检测到的最新范围
    if let floor = floorNode.geometry as? SCNFloor {
        floor.length = CGFloat(anchor.extent.z)
        floor.width = CGFloat(anchor.extent.x)
    }
}

额外优化点

如果你需要阴影跟随自己放置的可移动AR对象,不需要受平面检测范围限制,可以直接删掉设置floor.length、floor.width的代码:SCNFloor本身默认是无限延展的平面,你只要把阴影平面作为自定义AR对象的子节点,把y轴坐标对齐到物体放置的平面高度,不管物体移动到什么位置,阴影都能正常投射,连didUpdate中的尺寸同步逻辑都可以省略,CPU/GPU零额外开销,完全满足每秒25次快照的性能要求。

和逐帧销毁重建的方案比,这个方案不会频繁触发SceneKit的几何资源创建、释放和渲染树重建,哪怕在高频快照的场景下也不会出现掉帧问题。


内容的提问来源于stack exchange,提问作者martin wotterman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:57:17