尝试使用 CoreMotion 正确旋转 SceneKit 相机。我构建的场景非常简单......我所做的只是创建一堆盒子,分布在一个区域中,相机正好指向 Z 轴。
不幸的是,从设备运动返回的数据似乎与设备的物理位置和方向没有任何关系。它似乎只是随机地蜿蜒。
正如这篇 SO post中所建议的,我将姿态的四元数直接传递给相机节点的方向属性。
我是否误解了这里的数据核心运动给了我什么?姿态不应该反映设备的物理方向吗?还是增量运动,我应该建立在先前的方向上?
尝试使用 CoreMotion 正确旋转 SceneKit 相机。我构建的场景非常简单......我所做的只是创建一堆盒子,分布在一个区域中,相机正好指向 Z 轴。
不幸的是,从设备运动返回的数据似乎与设备的物理位置和方向没有任何关系。它似乎只是随机地蜿蜒。
正如这篇 SO post中所建议的,我将姿态的四元数直接传递给相机节点的方向属性。
我是否误解了这里的数据核心运动给了我什么?姿态不应该反映设备的物理方向吗?还是增量运动,我应该建立在先前的方向上?
此处的此代码段可能会对您有所帮助:
var motionManager = CMMotionManager()
motionManager?.deviceMotionUpdateInterval = 1.0 / 60.0
motionManager?.startDeviceMotionUpdatesToQueue(
NSOperationQueue.mainQueue(),
withHandler: { (motion: CMDeviceMotion!, error: NSError!) -> Void in
let currentAttitude = motion.attitude
var roll = Float(currentAttitude.roll) + (0.5*Float(M_PI))
var yaw = Float(currentAttitude.yaw)
var pitch = Float(currentAttitude.pitch)
self.cameraNode.eulerAngles = SCNVector3(
x: -roll,
y: yaw,
z: -pitch)
})
此设置适用于横向右侧的设备。您可以通过更改 + 和 - 来玩不同的方向
导入 CoreMotion。
对于任何偶然发现此问题的人,这里有一个更完整的答案,因此您可以了解否定和 pi/2 转换的必要性。您首先需要知道您的参考框架。球坐标系将点定义为与 z 轴和 x 轴成一定角度的矢量。对于地球,我们将 z 轴定义为从地球中心到北极的线,将 x 轴定义为从中心穿过本初子午线(大西洋中部非洲)的赤道的线。
对于(lat, lon, alt)
,我们可以定义roll
并yaw
围绕 z 轴和 y 轴以弧度表示:
let roll = lon * Float.pi / 180
let yaw = (90 - lat) * Float.pi / 180
我分别将, 和与,和配对roll
,如为 eulerAngles 定义的那样。pitch
yaw
z
x
y
额外的 90 度说明北极位于纬度 90 度而不是零。
为了将我的 SCNCamera 放在地球上,我使用了两个 SCNNode:一个“手臂”节点和一个相机节点:
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 轴旋转一个,即在滚动中添加/减去 180 度……或者,由于它们以弧度表示,因此将它们取反以获得相同的效果。
此外,据我所知,CMAttitude 没有明确记录它的roll
值意味着从手机屏幕出来的围绕 z 轴的旋转,并且从实验来看,它的定义似乎attitude.roll
与attitude.yaw
eulerAngles 中的定义相反,但是也许这是使用 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 位置的场景。