CMMotionData到SceneKit SCNNode方向

时间:2014-10-10 06:02:52

标签: augmented-reality quaternions core-motion scenekit cmmotionmanager

尝试使用CoreMotion正确旋转SceneKit相机。我构建的场景非常简单......我所做的就是创建一堆盒子,分布在一个区域中,相机只是指向Z轴。

不幸的是,从设备运动中返回的数据似乎与设备的物理位置和方向无关。它似乎随意蜿蜒。

正如this SO post中所建议的那样,我将态度的四元数直接传递给相机节点的方向属性。

我是否误解了核心动议在这里给我的数据?这种态度不应该反映出设备的物理定位吗?还是增量运动,我应该建立在先前的方向?

2 个答案:

答案 0 :(得分:7)

此片段可能会对您有所帮助:

    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。

答案 1 :(得分:3)

对于任何偶然发现此问题的人来说,这里有一个更完整的答案,因此您可以理解是否需要否定和pi / 2班次。您首先需要知道您的参考框架。球面坐标系将点定义为远离z轴和x轴倾斜的矢量。对于地球,让我们将z轴定义为从地球中心到北极的直线,将x轴定义为从中心通过本初子午线的赤道的直线(中间 - 非洲大西洋)。

对于(lat, lon, alt),我们可以用弧度在z轴和y轴周围定义rollyaw

let roll = lon * Float.pi / 180
let yaw = (90 - lat) * Float.pi / 180

我将rollpitchyaw分别与zxy配对为为eulerAngles定义。

额外的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轴reference的滚动,俯仰和偏航值。基于磁力计的磁力计使用指向远离重力的z轴和指向北极的x轴。因此,具有零俯仰,滚动和偏航的手机将使其屏幕面向远离重力,其背部相机指向地面,并且其纵向模式的右侧朝向北方。请注意,此方向与重力有关,而与手机的纵向/横向模式(也与重力相关)无关。所以肖像/风景是无关紧要的。

如果您想象在本初子午线北极附近的手机相机处于此方位,您会注意到CMMotion参考的方向与虚拟相机(SCNCamera)的方向不同。两个相机都面向地面,但它们各自的y轴(和x)相隔180度。为了排列它们,我们需要围绕其各自的z轴旋转一个,即向滚动加/减180度......或者,因为它们以弧度表示,否则它们会产生相同的效果。

另外,据我所知,CMAttitude没有明确记录其roll值表示围绕从手机屏幕出来的z轴的旋转,以及实验,似乎attitude.rollattitude.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位置的场景。