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视图卡顿?
麻烦各位大佬帮忙看看,给点思路或者修正建议,谢谢啦!




