如何使用平移手势使用四元数在SceneKit中旋转相机

问题描述 投票:3回答:3

我正在使用iOS SceneKit框架构建360视频查看器。

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

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

我读过的所有内容都是为了避免使用欧拉角来避免使用gimbal lock

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

我无法让它正常工作。相机控制几乎就是我想要的地方,但有些不对劲。尽管我试图仅影响X和Y轴,但看起来好像相机正绕着Z轴旋转。

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

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/

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

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

我在这里发布了一个视频来演示该错误(请原谅视频文件的低红色):qazxsw poi

在此先感谢您的帮助!

ios swift scenekit quaternions
3个回答
5
投票

我能够使用四元数来实现这一点。完整的代码在这里:https://vimeo.com/174346191。样本在这里:

ThreeSixtyPlayer

1
投票

对不起,这使用 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) 而不是四元数,但适合我的使用。我将它应用到我的根最几何的几何节点容器(“rotContainer”)而不是相机,但一个简短的测试似乎表明它也适用于相机使用。

SCNVector4

0
投票

bbedit的解决方案与func panGesture(sender: UIPanGestureRecognizer) { let translation = sender.translationInView(sender.view!) let pan_x = Float(translation.x) let pan_y = Float(-translation.y) let anglePan = sqrt(pow(pan_x,2)+pow(pan_y,2))*(Float)(M_PI)/180.0 var rotationVector = SCNVector4() rotationVector.x = -pan_y rotationVector.y = pan_x rotationVector.z = 0 rotationVector.w = anglePan rotContainer.rotation = rotationVector if(sender.state == UIGestureRecognizerState.Ended) { let currentPivot = rotContainer.pivot let changePivot = SCNMatrix4Invert(rotContainer.transform) rotContainer.pivot = SCNMatrix4Mult(changePivot, currentPivot) rotContainer.transform = SCNMatrix4Identity } } 结合得很好。按照链接答案中的建议设置摄像机,然后使用bbedit的想法旋转“轨道”节点。我修改了他的代码的Swift 4版本的代码,它确实有效:

Rotating a camera on an orbit
© www.soinside.com 2019 - 2024. All rights reserved.