为何导入Reality Composer场景需手动配置ARReferenceObject才能显示?
我希望将从Reality Composer导出的包含物体锚点和模型的.reality文件加载到RealityKit中。根据我的理解,只需从.reality文件加载根级AnchorEntity并调用view.scene.addAnchor()添加到场景即可(见代码中// 1:处),但这样物体检测无法正确配置,场景无法显示。不过,若我将用于在RealityComposer中创建场景的.arobject文件对应的AR参考对象显式添加到ARWorldTrackingConfiguration.detectionObjects(见代码// 2.处),然后在物体锚点添加到场景的回调事件中,手动将场景内容附加到该锚点,场景就能正常显示(见代码// 3.处)。
请问为何需要这些额外步骤?我在导入RealityComposer场景时,遗漏了哪些能为其中的AnchorEntity正确配置物体跟踪的设置?
注:为简洁起见,本示例中所有文件均采用同步加载方式。
struct RealityKitView: UIViewRepresentable { private static let logger = Logger( subsystem: Bundle.main.bundleIdentifier!, category: String(describing: RealityKitView.self) ) func makeUIView(context: Context) -> ARView { let view = ARView() let session = view.session let config = ARWorldTrackingConfiguration() // 1: Try to load the .reality file (nothing appears in the ARView): let realityFileSceneURL = Self.createRealityURL(filename: "ImpactHub_Sign", sceneName: "Scene") if let realityFileSceneURL { do { let anchorEntity = try Entity.loadAnchor(contentsOf: realityFileSceneURL) view.scene.addAnchor(anchorEntity) } catch { debugPrint(error.localizedDescription) } } // 2: Add detection reference objects: config.detectionObjects = ARReferenceObject.referenceObjects(inGroupNamed: "AR", bundle: Bundle.main)! session.run(config, options: [.resetTracking]) context.coordinator.view = view session.delegate = context.coordinator return view } func updateUIView(_ view: ARView, context: Context) { } static func createRealityURL(filename: String, sceneName:String) -> URL? { guard let realityFileURL = Bundle.main.url(forResource: filename, withExtension: "reality") else { print("Error finding Reality file \(filename)") return nil } let realityFileSceneURL = realityFileURL.appendingPathComponent(sceneName, isDirectory: false) return realityFileSceneURL } func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator: NSObject, ARSessionDelegate { private static let logger = Logger( subsystem: Bundle.main.bundleIdentifier!, category: String(describing: Coordinator.self) ) weak var view: ARView? func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { guard let view = self.view else { return } debugPrint("Anchors added to the scene: ", anchors) anchors.compactMap { $0 as? ARObjectAnchor }.forEach { anchor in debugPrint("Object anchor added") // 3: Load .reality file and manually create an AnchorEntity bound to the object // anchor that has just been detected. This way, the scene appears. let realityFileSceneURL = RealityKitView.createRealityURL(filename: "ImpactHub_Sign", sceneName: "Scene") if let realityFileSceneURL { do { let anchorEntity = try Entity.loadAnchor(contentsOf: realityFileSceneURL) anchorEntity.children.forEach { childEntity in let anchorEntity = AnchorEntity(anchor: anchor) anchorEntity.addChild(childEntity) view.scene.addAnchor(anchorEntity) } } catch { print(error) } } } } } }
解答
1. 直接加载.reality文件不生效的核心原因
从RealityComposer导出的.reality文件里的AnchorEntity,只是绑定了物体锚点的逻辑结构,但它不会自动触发ARKit的物体检测功能——ARKit的物体跟踪必须显式在会话配置中启用,RealityComposer导出的资源不会自动修改你的AR会话参数。
另外,直接加载的AnchorEntity并没有和ARKit实时检测到的ARObjectAnchor建立绑定:它只是一个“等待匹配”的锚点模板,但ARKit不知道要去检测对应的物体,自然不会激活这个锚点,模型也就不会显示。
2. 你遗漏的关键设置
- 未显式启用物体检测会话配置:ARKit不会自动扫描
.reality文件里的参考对象信息来开启检测,必须手动将对应的.arobject参考对象添加到config.detectionObjects,这是启动物体跟踪的必要前提。 - 未将场景锚点与实时检测结果绑定:直接加载的
AnchorEntity处于“未激活”状态,只有当ARKit检测到目标物体并生成真实空间中的ARObjectAnchor后,把场景内容绑定到这个实时锚点上,才能让模型和检测到的物体位置对齐。
3. 为何手动步骤能生效?
- 步骤2中添加
config.detectionObjects,相当于告诉ARKit:“我要检测这些物体,找到后生成对应的空间锚点”。 - 步骤3中在
didAdd anchors回调里绑定场景,是因为此时ARKit已经定位到目标物体并生成了真实的空间锚点,把模型挂到这个锚点上,才能让模型随物体位置变化实时更新。
优化建议
可以简化流程,不需要重复加载.reality文件,提前保存场景内容,等到检测到锚点时直接复用:
// 在RealityKitView中添加属性保存场景内容 private var sceneContents: [Entity]? // 在makeUIView的步骤1中修改: if let realityFileSceneURL { do { let anchorEntity = try Entity.loadAnchor(contentsOf: realityFileSceneURL) // 克隆并保存子实体,不直接添加锚点到场景 sceneContents = anchorEntity.children.map { $0.clone(recursive: true) } } catch { debugPrint(error.localizedDescription) } } // 修改Coordinator,让它能访问sceneContents(可通过给Coordinator添加parent属性实现) class Coordinator: NSObject, ARSessionDelegate { weak var view: ARView? weak var parent: RealityKitView? // 添加这个属性 func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { guard let view = view, let sceneContents = parent?.sceneContents else { return } anchors.compactMap { $0 as? ARObjectAnchor }.forEach { anchor in let anchorEntity = AnchorEntity(anchor: anchor) sceneContents.forEach { anchorEntity.addChild($0) } view.scene.addAnchor(anchorEntity) } } } // 在makeCoordinator中传递parent func makeCoordinator() -> Coordinator { let coordinator = Coordinator() coordinator.parent = self return coordinator }
内容的提问来源于stack exchange,提问作者Peter

