【问题标题】:CMMotionData to SceneKit SCNNode orientationCMMotionData 到 SceneKit SCNNode 方向
【发布时间】:2014-12-05 05:41:05
【问题描述】:

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

不幸的是,从设备运动返回的数据似乎与设备的物理位置和方向没有任何关系。它似乎只是随机地蜿蜒。

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

我是否误解了这里的数据核心运动给了我什么?姿态不应该反映设备的物理方向吗?还是增量运动,我应该建立在先前的方向上?

【问题讨论】:

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


    【解决方案1】:

    这里的 sn-p 可能会对您有所帮助:

        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。

    【讨论】:

    • 纵向如何工作?我一直在摆弄他的东西,让它在纵向上工作,只有横向
    【解决方案2】:

    对于任何偶然发现此问题的人,这里有一个更完整的答案,以便您了解否定和 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 位置的场景。

    【讨论】:

      猜你喜欢
      • 2018-03-22
      • 1970-01-01
      • 2018-03-15
      • 2018-03-28
      • 2016-12-01
      • 2016-03-25
      • 2017-01-18
      • 2017-04-15
      • 2014-10-12
      相关资源
      最近更新 更多