2016-07-12 38 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 
    } 
} 
相關問題