如何使用平移手势在 SceneKit 中使用四元数旋转相机 [英] How to use a pan gesture to rotate a camera in SceneKit using quaternions

查看:186
本文介绍了如何使用平移手势在 SceneKit 中使用四元数旋转相机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 iOS SceneKit<构建 360 度全景视频查看器 框架.

I'm building a 360 video viewer using the iOS SceneKit framework.

我想使用 UIPanGestureRecognizer 来控制相机的方向.

I'd like to use a UIPanGestureRecognizer to control the camera's orientation.

SCNNodes 有几个我们可以使用的属性指定它们的旋转:rotation(旋转矩阵)、orientation(四元数)、eulerAngles(每个轴的角度).

SCNNodes have several properties we can use to specify their rotation: rotation (a rotation matrix), orientation (a quaternion), eulerAngles (per axis angles).

我读过的所有内容都说要避免使用欧拉角以避免万向节锁定.

Everything I've read says to avoid using euler angles in order to avoid gimbal lock.

我想使用四元数有几个原因,我不会在这里讨论.

I'd like to use quaternions for a few reasons which I won't go into here.

我无法让它正常工作.相机控制几乎是我想要的,但有一些问题.尽管我试图只影响 X 和 Y 轴,但看起来好像相机正在绕 Z 轴旋转.

I'm having trouble getting this to work properly. Camera control is almost where I'd like it to be but there's something wrong. It looks as though the camera is being rotated about the Z axis despite my attempts to only influence the X and Y axes.

我相信这个问题与我的四元数乘法逻辑有关.多年来我没有做过任何与四元数相关的事情:(我的平移手势处理程序在这里:

I believe the issue has something to do with my quaternion multiplication logic. I haven't done anything related to quaternion in years :( My pan gesture handler is here:

func didPan(recognizer: UIPanGestureRecognizer)
{
    switch recognizer.state
    {
    case .Began:
        self.previousPanTranslation = .zero

    case .Changed:
        guard let previous = self.previousPanTranslation else
        {
            assertionFailure("Attempt to unwrap previous pan translation failed.")

            return
        }

        // Calculate how much translation occurred between this step and the previous step
        let translation = recognizer.translationInView(recognizer.view)
        let translationDelta = CGPoint(x: translation.x - previous.x, y: translation.y - previous.y)

        // Use the pan translation along the x axis to adjust the camera's rotation about the y axis.
        let yScalar = Float(translationDelta.x / self.view.bounds.size.width)
        let yRadians = yScalar * self.dynamicType.MaxPanGestureRotation

        // Use the pan translation along the y axis to adjust the camera's rotation about the x axis.
        let xScalar = Float(translationDelta.y / self.view.bounds.size.height)
        let xRadians = xScalar * self.dynamicType.MaxPanGestureRotation

        // Use the radian values to construct quaternions
        let x = GLKQuaternionMakeWithAngleAndAxis(xRadians, 1, 0, 0)
        let y = GLKQuaternionMakeWithAngleAndAxis(yRadians, 0, 1, 0)
        let z = GLKQuaternionMakeWithAngleAndAxis(0, 0, 0, 1)
        let combination = GLKQuaternionMultiply(z, GLKQuaternionMultiply(y, x))

        // Multiply the quaternions to obtain an updated orientation
        let scnOrientation = self.cameraNode.orientation
        let glkOrientation = GLKQuaternionMake(scnOrientation.x, scnOrientation.y, scnOrientation.z, scnOrientation.w)
        let q = GLKQuaternionMultiply(combination, glkOrientation)

        // And finally set the current orientation to the updated orientation
        self.cameraNode.orientation = SCNQuaternion(x: q.x, y: q.y, z: q.z, w: q.w)
        self.previousPanTranslation = translation

    case .Ended, .Cancelled, .Failed:
        self.previousPanTranslation = nil

    case .Possible:
        break
    }
}

我的代码在这里是开源的:https://github.com/alfiehanssen/360Player/

My code is open source here: https://github.com/alfiehanssen/360Player/

特别查看 pan-gesture 分支:https://github.com/alfiehanssen/360Player/tree/pan-gesture

如果你把代码拉下来,我相信你必须在设备上而不是模拟器上运行它.

If you pull the code down I believe you'll have to run it on a device rather than the simulator.

我在这里发布了一个演示错误的视频(请原谅视频文件的低分辨率):https://vimeo.com/174346191

I posted a video here that demonstrates the bug (please excuse the low resness of the video file): https://vimeo.com/174346191

在此先感谢您的帮助!

推荐答案

我能够使用四元数来解决这个问题.完整代码在这里:ThreeSixtyPlayer.示例在这里:

I was able to get this working using quaternions. The full code is here: ThreeSixtyPlayer. A sample is here:

    let orientation = cameraNode.orientation

    // Use the pan translation along the x axis to adjust the camera's rotation about the y axis (side to side navigation).
    let yScalar = Float(translationDelta.x / translationBounds.size.width)
    let yRadians = yScalar * maxRotation

    // Use the pan translation along the y axis to adjust the camera's rotation about the x axis (up and down navigation).
    let xScalar = Float(translationDelta.y / translationBounds.size.height)
    let xRadians = xScalar * maxRotation

    // Represent the orientation as a GLKQuaternion
    var glQuaternion = GLKQuaternionMake(orientation.x, orientation.y, orientation.z, orientation.w)

    // Perform up and down rotations around *CAMERA* X axis (note the order of multiplication)
    let xMultiplier = GLKQuaternionMakeWithAngleAndAxis(xRadians, 1, 0, 0)
    glQuaternion = GLKQuaternionMultiply(glQuaternion, xMultiplier)

    // Perform side to side rotations around *WORLD* Y axis (note the order of multiplication, different from above)
    let yMultiplier = GLKQuaternionMakeWithAngleAndAxis(yRadians, 0, 1, 0)
    glQuaternion = GLKQuaternionMultiply(yMultiplier, glQuaternion)

    cameraNode.orientation = SCNQuaternion(x: glQuaternion.x, y: glQuaternion.y, z: glQuaternion.z, w: glQuaternion.w)

这篇关于如何使用平移手势在 SceneKit 中使用四元数旋转相机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆