2016-07-12 100 views
3

我正在使用iOS SceneKit框架构建360视频查看器。如何使用平移手势在SceneKit中使用四元数旋转摄像机

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

SCNNode■找几个属性,我们可以使用,以指定它们的旋转: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

:(我的移动手势处理程序是在这里我没有做过多年为四元数相关的任何事情

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

我这里张贴的视频演示错误(请EX请使用低分辨率的视频文件): https://vimeo.com/174346191

在此先感谢您的帮助!

回答

4

我能用四元数得到这个工作。完整的代码在这里: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) 
1

对不起,这使用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 
    } 
} 
0

bbedit的解决方案很好地结合了Rotating a camera on an orbit。按链接答案中的建议设置相机,然后使用bbedit的想法旋转“轨道”节点。我已经修改了代码,雨燕4.0版本的他的代码,做了工作:

@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 
    } 
} 
相关问题