VisionOS ARKit:追踪图像上放置网格球体失败,求简便实现方法
问题分析与解决方案
你的代码追踪功能正常但球体不显示,核心问题有两个:
- ViewModel中的
rootEntity没有关联到ARView的渲染场景,导致所有子实体都不在可视层级内 updateImage方法每次收到锚点更新都会重复创建新实体,既浪费资源也可能引发显示异常
修复步骤
1. 关联rootEntity到ARView场景
把ViewModel里的rootEntity改为@Published属性,让SwiftUI的ARView能绑定它作为场景根实体:
@MainActor class ImageTrackingViewModel: ObservableObject { private let session = ARKitSession() @Published var rootEntity = Entity() // 改为@Published对外暴露 private let imageInfo = ImageTrackingProvider( referenceImages: ReferenceImage.loadReferenceImages(inGroupNamed: "albumCovers") ) // ... 其他代码不变 }
然后在SwiftUI视图中,将ARView的场景根锚点设置为ViewModel的rootEntity:
struct ARViewContainer: UIViewRepresentable { @ObservedObject var vm: ImageTrackingViewModel func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) // 将ViewModel的rootEntity加入AR场景 arView.scene.addAnchor(vm.rootEntity) return arView } func updateUIView(_ uiView: ARView, context: Context) {} }
2. 避免重复创建实体
修改updateImage方法,先检查entityMap中是否已有对应锚点的实体,仅在不存在时创建添加:
func updateImage(_ anchor: ImageAnchor, rootEntity: Entity) { // 仅当实体不存在时才创建 if entityMap[anchor.id] == nil { let entity = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial(color: .systemBlue, isMetallic: false)]) entityMap[anchor.id] = entity rootEntity.addChild(entity) } if anchor.isTracked { // 使用anchorFromOriginTransform作为实体相对父节点的变换 entityMap[anchor.id]?.transform = Transform(matrix: anchor.anchorFromOriginTransform) } }
3. 更简便的锚点绑定方式
可以直接将实体挂载到AnchorEntity上,无需手动维护transform,ARKit会自动同步锚点位置:
func updateImage(_ anchor: ImageAnchor, rootEntity: Entity) { if entityMap[anchor.id] == nil { let entity = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial(color: .systemBlue, isMetallic: false)]) entityMap[anchor.id] = entity // 用AnchorEntity绑定ImageAnchor,自动同步位置 let anchorEntity = AnchorEntity(anchor: anchor) anchorEntity.addChild(entity) rootEntity.addChild(anchorEntity) } }
这种方式无需手动更新变换,代码更简洁可靠。
额外注意事项
- 确保
albumCovers资源组已勾选"Target Membership",且参考图片的物理尺寸设置准确 - 补充相机权限请求逻辑,否则会话可能无法正常启动:
func start() async { do { // 请求相机权限 let status = await AVCaptureDevice.requestAccess(for: .video) guard status else { print("相机权限被拒绝") return } if ImageTrackingProvider.isSupported { print("ARKitSession starting.") try await session.run([imageInfo]) for await update in imageInfo.anchorUpdates { updateImage(update.anchor, rootEntity: rootEntity) } } } catch { print("会话启动失败:\(error)") } }
内容的提问来源于stack exchange,提问作者azozea
相关产品推荐
相关产品推荐

