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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:05:20