如何将ARKit中的lookAtPoint转换为屏幕坐标点?
ARKit视线点转屏幕坐标实现方案
核心思路:正确转换世界空间点到屏幕像素坐标
lookAtPoint是ARFaceAnchor提供的世界空间坐标点,直接用projectPoint时需要注意完整转换流程——该方法返回的是标准化设备坐标(NDC),需进一步映射为屏幕像素坐标。
完整代码实现(UIKit红点示例)
在ARSCNViewDelegate的代理方法中处理人脸锚点更新:
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } // 1. 获取世界空间的视线点 let lookAtWorldPoint = faceAnchor.lookAtPoint // 2. 转换为标准化设备坐标(NDC) let ndcPoint = sceneView.projectPoint(lookAtWorldPoint) // 3. 将NDC坐标转为屏幕像素坐标 let bounds = sceneView.bounds let screenX = (ndcPoint.x + 1) * bounds.width / 2 let screenY = (1 - ndcPoint.y) * bounds.height / 2 // 4. 更新红点UI位置 redDotView.center = CGPoint(x: screenX, y: screenY) }
常见失败原因排查
- 无效的人脸锚点:确保已启动
ARFaceTrackingConfiguration,且设备成功捕捉到人脸(可通过session(_:didAdd:)确认锚点添加) - 未转换NDC坐标:直接使用
ndcPoint.x/y会得到[-1,1]范围的值,必须映射到屏幕宽高区间 - 视线点在相机后方:若
lookAtPoint的z值为负(相对于相机),projectPoint会返回无效坐标,可先判断ndcPoint.z是否在[0,1]范围内
用双眼变换实现更精准的视线追踪
如果lookAtPoint精度不足,可通过左右眼的变换矩阵计算视线射线,再与屏幕平面求交点:
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } guard let cameraNode = sceneView.pointOfView else { return } // 获取双眼的世界空间变换 let leftEyeWorldTransform = node.simdTransform * faceAnchor.leftEyeTransform let rightEyeWorldTransform = node.simdTransform * faceAnchor.rightEyeTransform // 计算双眼平均位置与视线方向 let leftEyePos = leftEyeWorldTransform.columns.3 let rightEyePos = rightEyeWorldTransform.columns.3 let avgEyePos = (leftEyePos + rightEyePos) / 2 let leftEyeDir = simd_normalize(-leftEyeWorldTransform.columns.2) let rightEyeDir = simd_normalize(-rightEyeWorldTransform.columns.2) let avgEyeDir = simd_normalize(leftEyeDir + rightEyeDir) // 计算视线与1米远平面的交点(可自定义距离) let targetZ = 1.0 let cameraZ = cameraNode.simdTransform.columns.3.z let t = (targetZ - cameraZ) / avgEyeDir.z let hitWorldPoint = avgEyePos + avgEyeDir * t // 转换为屏幕坐标(同之前的流程) let ndcPoint = sceneView.projectPoint(SCNVector3(hitWorldPoint)) let bounds = sceneView.bounds let screenX = (ndcPoint.x + 1) * bounds.width / 2 let screenY = (1 - ndcPoint.y) * bounds.height / 2 redDotView.center = CGPoint(x: screenX, y: screenY) }
注意事项
- 仅支持iPhone X及以上设备(需前置深度摄像头)
- 若红点是SceneKit节点,无需转屏幕坐标,直接设置
node.position = lookAtWorldPoint即可
内容的提问来源于stack exchange,提问作者Moses
相关产品推荐
相关产品推荐

