如果有人遇到此问题,以下是更完整的答案,以便您理解否定和pi/2偏移的必要性。首先需要了解您的参考系。球面坐标系将点定义为与z轴和x轴成角度的向量。对于地球,让我们将z轴定义为从地心到北极的线,将x轴定义为从中心通过本初子午线(大西洋中部非洲)穿过赤道的线。
对于(纬度、经度、高度)
,我们可以定义绕z轴和y轴的翻滚
和偏航角
(以弧度为单位):
let roll = lon * Float.pi / 180
let yaw = (90 - lat) * Float.pi / 180
我正在将
roll
,
pitch
和
yaw
与
z
,
x
和
y
配对,分别按照eulerAngles的定义。
额外的90度是为了考虑到北极在90度纬度而不是0度。
为了将我的SCNCamera放置在地球仪上,我使用了两个SCNNodes:一个“arm”节点和相机节点:
let scnCamera = SCNNode()
scnCamera.camera = SCNCamera()
scnCamera.position = SCNVector3(x: 0.0, y: 0.0, z: alt + EARTH_RADIUS)
let scnCameraArm = SCNNode()
scnCameraArm?.position = SCNVector3(x: 0, y: 0, z: 0)
scnCameraArm?.addChildNode(scnCamera)
手臂位于地球中心,摄像机距离地球半径
alt + EARTH_RADIUS
的位置,即摄像机现在位于北极。为了在每次位置更新时移动摄像机,我们现在只需旋转手臂节点以获得新的横滚和偏航值:
scnCameraArm.eulerAngles.z = roll
scnCameraArm.eulerAngles.y = yaw
不改变相机的方向,它的虚拟镜头始终面向地面,其虚拟“上”方向指向西方。
要更改虚拟相机的方向,CMMotion回调会返回一个具有滚动、俯仰和偏航值的
CMAttitude,相对于您选择的不同z-和x-轴
参考。基于磁力计的轴使用指向重力的z轴和指向北极的x轴。因此,零俯仰、横滚和偏航的手机屏幕将面向重力,后置摄像头指向地面,纵向模式的右侧面向北方。请注意,这种方向是相对于重力而不是手机的纵向/横向模式(也是相对于重力)。因此,纵向/横向模式无关紧要。
如果您想象一下,当手机的相机在本初子午线附近的北极时,您会注意到CMMotion参考与虚拟相机(SCNCamera)处于不同的方向。两个相机都面向地面,但它们各自的y轴(和x轴)相差180度。为了使它们对齐,我们需要围绕各自的z轴旋转一个相机,即将roll加 / 减180度...或者,由于它们是用弧度表示的,因此可以将其取反以达到相同的效果。
另外,据我所知,CMAttitude没有明确说明其roll值表示从手机屏幕出来的z轴旋转,而且经过实验,似乎attitude.roll和attitude.yaw具有与eulerAngles中定义的相反的定义,但也许这是在虚拟空间中应用旋转变换的顺序的产物(?)。无论如何,回调函数:
motionManager?.startDeviceMotionUpdates(using: .xTrueNorthZVertical, to: OperationQueue.main, withHandler: { (motion: CMDeviceMotion?, err: Error?) in
guard let m = motion else { return }
scnCamera.eulerAngles.z = Float(m.attitude.yaw - Double.pi)
scnCamera.eulerAngles.x = Float(m.attitude.pitch)
scnCamera.eulerAngles.y = Float(m.attitude.roll)
})
你可以从不同的参考框架开始设置虚拟相机,例如z轴指向赤道经线,x轴指向北极(即CMMotion参考),但你仍需要在某个地方反转经度。有了这个设置,你可以很容易地构建一个严重依赖GPS位置的场景。