【问题标题】:How to use a pan gesture to rotate a camera in SceneKit using quaternions如何使用平移手势在 SceneKit 中使用四元数旋转相机
【发布时间】:2016-11-14 03:29:49
【问题描述】:

我正在使用 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

如果您将代码拉下来,我相信您将不得不在设备而不是模拟器上运行它。

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

提前感谢您的帮助!

【问题讨论】:

    标签: ios swift scenekit quaternions


    【解决方案1】:

    我能够使用四元数来完成这项工作。完整代码在这里:ThreeSixtyPlayer。示例在这里:

        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)
    

    【讨论】:

    • 多年后,这非常有用。谢谢朋友。
    • 我想知道“减慢”泛响应器的正确方法是什么。它似乎移动得太快了。
    【解决方案2】:

    抱歉,这使用 SCNVector4 而不是四元数,但对我来说效果很好。我将它应用于我的最根几何节点容器(“rotContainer”)而不是相机,但一个简短的测试似乎表明它也适用于相机使用。

    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
        }
    }
    

    【讨论】:

      【解决方案3】:

      bbedit 的解决方案与Rotating a camera on an orbit 结合得很好。按照链接答案中的建议设置相机,然后使用 bbedit 的想法旋转“轨道”节点。我已经修改了他的代码的 Swift 4 版本的代码,它确实有效:

      @IBAction func handlePan(_ sender: UIPanGestureRecognizer) {
          print("Called the handlePan method")
          let scnView = self.view as! SCNView
          let cameraOrbit = scnView.scene?.rootNode.childNode(withName: "cameraOrbit", recursively: true)
          let translation = sender.translation(in: 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)(Double.pi)/180.0
          var rotationVector = SCNVector4()
          rotationVector.x = -pan_y
          rotationVector.y = pan_x
          rotationVector.z = 0
          rotationVector.w = anglePan
          cameraOrbit!.rotation = rotationVector
          if(sender.state == UIGestureRecognizerState.ended) {
              let currentPivot = cameraOrbit!.pivot
              let changePivot = SCNMatrix4Invert(cameraOrbit!.transform)
              cameraOrbit!.pivot = SCNMatrix4Mult(changePivot, currentPivot)
              cameraOrbit!.transform = SCNMatrix4Identity
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-30
        • 2016-05-13
        • 2018-03-15
        • 2016-02-24
        • 2018-07-17
        • 2012-07-24
        • 2018-02-01
        • 2018-07-29
        相关资源
        最近更新 更多