RealityKit中USDZ模型选点连线及纯白视图加载问题
RealityKit USDZ模型预览与点选连线实现方案
针对你提出的三个问题,结合现有代码给出修正和优化方案:
1. 在纯白虚拟视图加载模型(无需AR相机)
默认ARView会启动真实相机会话,要实现纯虚拟场景预览,需初始化ARView时指定非AR模式,同时调整模型初始位置确保在视野内,避免默认原点不在相机可视范围的问题。
修改点:
- 初始化ARView时启用
cameraMode: .nonAR,关闭自动AR会话配置 - 将模型锚点设置在相机前方(如z轴-1米处),保证模型默认可见
2. 手势识别选择模型表面点
现有点击检测逻辑基本可行,但需过滤hitTest结果,只响应目标模型的点击(避免误点到辅助的球体或线条),确保点击检测精准性。
优化点:
- 在hitTest结果中筛选出目标模型的碰撞检测结果
- 直接使用
hitResult.position获取点击点的世界坐标,代码更简洁
3. 连接选中点绘制线条
RealityKit中用三角面生成的"线"显示效果异常,正确做法是使用MeshResource.generateLineSegments生成真正的线段网格,同时维护一个统一的线条实体,每次添加点时更新网格,提升性能。
优化点:
- 使用线段网格生成函数替代三角面生成
- 维护全局线条实体,避免重复创建锚点和实体
- 使用无光照材质,避免线条受环境光影响
修正后的完整代码
import SwiftUI import RealityKit struct ContentView: View { var body: some View { ARViewContainer() .edgesIgnoringSafeArea(.all) } } struct ARViewContainer: UIViewRepresentable { private var targetModel: ModelEntity! func makeUIView(context: Context) -> ARView { // 初始化非AR模式的ARView,纯虚拟场景 let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: false) context.coordinator.arView = arView // 加载USDZ模型 do { targetModel = try ModelEntity.loadModel(named: "yourModel.usdz") // 根据模型实际大小调整缩放比例 targetModel.scale = [0.5, 0.5, 0.5] // 将模型放在相机前方1米处,确保默认可见 let anchorEntity = AnchorEntity(world: [0, 0, -1]) anchorEntity.addChild(targetModel) arView.scene.addAnchor(anchorEntity) } catch { print("模型加载失败:\(error.localizedDescription)") } // 设置纯白背景 arView.environment.background = .color(.white) // 传递目标模型给Coordinator用于点击过滤 context.coordinator.targetModel = targetModel context.coordinator.setupGestureRecognition() return arView } func updateUIView(_ uiView: ARView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } } class Coordinator: NSObject { var arView: ARView? var targetModel: ModelEntity? var selectedPoints: [SIMD3<Float>] = [] // 维护全局线条实体,避免重复创建 private var lineEntity: ModelEntity? func setupGestureRecognition() { guard let arView = arView else { return } let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) arView.addGestureRecognizer(tapGesture) } @objc private func handleTap(_ gesture: UITapGestureRecognizer) { guard let arView = arView, let targetModel = targetModel else { return } let location = gesture.location(in: arView) // 只响应目标模型的点击 let hitResults = arView.hitTest(location) guard let hitResult = hitResults.first(where: { $0.entity == targetModel }) else { return } // 获取点击点的世界坐标 let point = hitResult.position selectedPoints.append(point) // 添加标记点 addMarker(at: point) // 更新连接线条 updateConnectedLine() } // 添加红色标记球 private func addMarker(at position: SIMD3<Float>) { guard let arView = arView else { return } let sphereMesh = MeshResource.generateSphere(radius: 0.01) let material = SimpleMaterial(color: .red, isMetallic: false) let marker = ModelEntity(mesh: sphereMesh, materials: [material]) marker.position = position let anchor = AnchorEntity(world: position) anchor.addChild(marker) arView.scene.addAnchor(anchor) } // 更新连接所有选中点的线条 private func updateConnectedLine() { guard let arView = arView, selectedPoints.count >= 2 else { return } // 生成线段网格 let lineMesh = MeshResource.generateLineSegments(vertices: selectedPoints) let lineMaterial = UnlitMaterial(color: .blue) if let existingLine = lineEntity { // 更新已有线条的网格和材质 existingLine.model?.mesh = lineMesh existingLine.model?.materials = [lineMaterial] } else { // 首次创建线条实体 lineEntity = ModelEntity(mesh: lineMesh, materials: [lineMaterial]) let anchor = AnchorEntity(world: .zero) anchor.addChild(lineEntity!) arView.scene.addAnchor(anchor) } } }
内容的提问来源于stack exchange,提问作者Bruno Eigenmann
相关产品推荐
相关产品推荐

