You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ARKit结合SwiftUI实现视锥体(Frustum)计算与可视化的问题求助

ARKit结合SwiftUI实现视锥体(Frustum)计算与可视化的问题求助

大家好,我已经折腾好几天了,想在SwiftUI项目里用ARKit计算相机的视锥体并把它可视化出来,但一直没能完成完整的效果。目前我能成功计算并显示视锥体近平面和远平面的中心(黄色/绿色小球),它们确实在相机视野的正确位置,但视锥体的边框、平面这些还没画出来,而且我也不确定自己的视锥体顶点计算逻辑有没有问题,希望各位能帮我看看!

我的代码如下:

// 处理点击手势触发视锥体计算
@objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
    guard let arView = sender?.view as? ARView, let tapLocation = sender?.location(in: arView) else {
        print("Tap or ARView not found")
        return
    }
    
    // 从ARKit获取当前相机帧
    if let currentFrame = arView.session.currentFrame {
        calculateFOVAndFrustum(frame: currentFrame, transform: currentFrame.camera.transform, arView: arView)
    }
}

func calculateFOVAndFrustum(frame: ARFrame, transform: simd_float4x4, arView: ARView) {
    let intrinsics = frame.camera.intrinsics
    let fx = intrinsics[0, 0]
    let fy = intrinsics[1, 1]
    
    let imageWidth = Float(frame.camera.imageResolution.width)
    let imageHeight = Float(frame.camera.imageResolution.height)
    let aspectRatio = imageWidth / imageHeight
    
    // 计算视场角(FOV)
    let verticalFOV = 2 * atan(imageHeight / (2 * fy))
    let horizontalFOV = 2 * atan(imageWidth / (2 * fx))
    
    let nearDistance: Float = 0.03
    let farDistance: Float = 0.1
    
    let nearHeight = 2 * tan(verticalFOV / 2) * nearDistance
    let farHeight = 2 * tan(verticalFOV / 2) * farDistance
    let nearWidth = nearHeight * aspectRatio
    let farWidth = farHeight * aspectRatio
    
    print("Position: \(transform.getPosition())")
    print("Rotation: \(transform.getRotation())")
    
    // 从变换矩阵获取相机位置和朝向
    let camPos = SIMD3<Float>(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
    // ARKit中相机的前方向是-Z轴
    let camForward = -SIMD3<Float>(transform.columns.2.x, transform.columns.2.y, transform.columns.2.z)
    let camUp = SIMD3<Float>(transform.columns.1.x, transform.columns.1.y, transform.columns.1.z)
    let camRight = SIMD3<Float>(transform.columns.0.x, transform.columns.0.y, transform.columns.0.z)
    
    print("Cam Pos: \(camPos)")
    print("Cam Forward: \(camForward)")
    print("Cam Up: \(camUp)")
    print("Cam Right: \(camRight)")
    
    // 计算近/远平面中心(在相机前方)
    let nearCenter = camPos + camForward * nearDistance
    let farCenter = camPos + camForward * farDistance
    
    // 计算视锥体的八个顶点
    let farTopLeft = farCenter + (camUp * (farHeight * 0.5)) - (camRight * (farWidth * 0.5))
    let farTopRight = farCenter + (camUp * (farHeight * 0.5)) + (camRight * (farWidth * 0.5))
    let farBottomLeft = farCenter - (camUp * (farHeight * 0.5)) - (camRight * (farWidth * 0.5))
    let farBottomRight = farCenter - (camUp * (farHeight * 0.5)) + (camRight * (farWidth * 0.5))
    
    let nearTopLeft = nearCenter + (camUp * (nearHeight * 0.5)) - (camRight * (nearWidth * 0.5))
    let nearTopRight = nearCenter + (camUp * (nearHeight * 0.5)) + (camRight * (nearWidth * 0.5))
    let nearBottomLeft = nearCenter - (camUp * (nearHeight * 0.5)) - (camRight * (nearWidth * 0.5))
    let nearBottomRight = nearCenter - (camUp * (nearHeight * 0.5)) + (camRight * (nearWidth * 0.5))
    
    visualizeFrustumPlanes(
        corners: [nearTopLeft, nearTopRight, nearBottomLeft, nearBottomRight, farTopLeft, farTopRight, farBottomLeft, farBottomRight],
        nearCenter: nearCenter,
        farCenter: farCenter,
        arView: arView
    )
}

// 可视化视锥体平面以及近/远中心的球体
func visualizeFrustumPlanes(corners: [SIMD3<Float>], nearCenter: SIMD3<Float>, farCenter: SIMD3<Float>, arView: ARView) {
    // 定义平面材质
    let nearPlaneMaterial = SimpleMaterial(color: .blue, isMetallic: true) // 近平面(蓝色)
    let farPlaneMaterial = SimpleMaterial(color: .red, isMetallic: true) // 远平面(红色)
    let sidePlaneMaterial = SimpleMaterial(color: .green, isMetallic: true) // 侧面(绿色)
    
    // 中心球体材质
    let centerMaterial = SimpleMaterial(color: .yellow, isMetallic: true) // 近中心(黄色)
    let centerMaterial2 = SimpleMaterial(color: .green, isMetallic: true) // 远中心(绿色)
    
    // 创建用于可视化的小球网格
    let sphereMesh = MeshResource.generateSphere(radius: 0.01)
    
    // 创建近/远中心的实体
    let nearCenterEntity = ModelEntity(mesh: sphereMesh, materials: [centerMaterial])
    nearCenterEntity.position = nearCenter
    
    let farCenterEntity = ModelEntity(mesh: sphereMesh, materials: [centerMaterial2])
    farCenterEntity.position = farCenter
    
    // 创建锚点并添加到场景
    let nearCenterAnchor = AnchorEntity(world: nearCenter)
    nearCenterAnchor.addChild(nearCenterEntity, preservingWorldTransform: true)
    
    let farCenterAnchor = AnchorEntity(world: farCenter)
    farCenterAnchor.addChild(farCenterEntity, preservingWorldTransform: true)
    
    arView.scene.addAnchor(nearCenterAnchor)
    arView.scene.addAnchor(farCenterAnchor)
}

我目前遇到的具体问题:

  • 只实现了近/远中心的可视化,但不知道怎么把视锥体的八个顶点连接成棱线,以及绘制出六个平面(近、远、四个侧面)
  • 不确定自己计算视锥体顶点的逻辑是否正确,有没有ARKit坐标系的细节我忽略了?比如相机坐标系和世界坐标系的转换会不会有问题?
  • 在SwiftUI结合ARKit的场景下,绘制这些线条和平面有没有高效的方式?会不会因为频繁更新导致AR视图卡顿?

麻烦各位大佬帮忙看看,给点思路或者修正建议,谢谢啦!

火山引擎 最新活动