ARKit中基于3D坐标计算2D箭头指向的问题及修正方案
用2D箭头引导ARKit相机朝向3D物体的实现方案
我最近在做ARKit开发时,遇到了这么个需求:已知相机和3D物体的笛卡尔坐标,需要用UIImageView做一个2D箭头,指示用户移动相机的方向,引导他们看到目标3D物体。最开始我尝试用反正切函数计算箭头旋转角度,但发现因为坐标系的问题,结果完全不对。
最初的尝试代码
func redirectArrow(bee: SCNNode, arrow: UIImageView) { let (direction, position) = self.getUserVector() let dx = bee.position.x - position.x let dy = bee.position.y - position.y let arctangent = atan(dy/dx) arrow.transform = CGAffineTransform(rotationAngle: CGFloat(arctangent)) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.redirectArrow(bee: bee, arrow: arrow) } } func getUserVector() -> (SCNVector3, SCNVector3) { // (direction, position) if let frame = self.sceneView.session.currentFrame { let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33) // orientation of camera in world space let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space return (dir, pos) } return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2)) }
问题根源
一开始的思路错在直接用3D世界坐标的差值计算角度——我们要的是屏幕2D坐标系下的方向,不是3D世界空间的方向。用3D坐标算出来的角度根本没法对应到屏幕上的箭头指向。
修正后的实现方案
感谢mnuages的解答,我调整了代码,核心是用ARKit的projectPoint(_:)方法把3D坐标转成屏幕2D坐标,再基于屏幕坐标的差值计算箭头旋转角度:
func redirectArrow(bee: SCNNode, arrow: UIImageView) { let (direction, position) = self.getUserVector() let bee2DPosition = sceneView.projectPoint(bee.position) let world2DPosition = sceneView.projectPoint(direction) let dx = bee2DPosition.x - world2DPosition.x let dy = bee2DPosition.y - world2DPosition.y var arctangent: Float = atan(dy/dx) if dx < 0 { arctangent += Float(Double.pi) } arrow.transform = CGAffineTransform(rotationAngle: CGFloat(arctangent)) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.redirectArrow(bee: bee, arrow: arrow) } }
关键细节说明
projectPoint(_:)会把3D世界空间的点转换成屏幕坐标系的点(原点在屏幕左下角,x轴向右,y轴向上);- 计算屏幕坐标差值后,用
atan(dy/dx)得到基础角度,同时要处理dx<0的情况,加上π修正角度象限,确保箭头指向正确; - 通过定时调用更新函数,实现箭头方向的实时刷新。
内容的提问来源于stack exchange,提问作者Dante
相关产品推荐
相关产品推荐

