2016-07-12 24 views
3

Ich baue ein 360-Video-Viewer mit dem iOS SceneKit Framework.So verwenden Sie eine Schwenkgeste zum Drehen einer Kamera in SceneKit mit quaternions

Ich möchte eine UIPanGestureRecognizer verwenden, um die Ausrichtung der Kamera zu steuern.

SCNNode s haben mehrere Eigenschaften, die wir ihre Rotation angeben können: rotation (a Rotationsmatrix), orientation (a Quaternion), eulerAngles (pro Achswinkel).

Alles, was ich gelesen habe sagt, um die Verwendung von Euler Winkel zu vermeiden, um zu vermeiden, gimbal lock.

Ich möchte Quaternionen aus ein paar Gründen verwenden, auf die ich hier nicht eingehen werde.

Ich habe Probleme, dies richtig zu funktionieren. Kamerasteuerung ist fast wo ich es gerne hätte, aber da stimmt was nicht. Es sieht so aus, als ob die Kamera um die Z-Achse gedreht wird, obwohl ich versucht habe, nur die X- und Y-Achse zu beeinflussen.

Ich glaube, das Problem hat etwas mit meiner Quaternion-Multiplikationslogik zu tun. Ich habe nichts in den nächsten Jahren quaternion Zusammenhang getan :(Mein pan Geste Handler hier:

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

Mein Code Open Source ist hier: https://github.com/alfiehanssen/360Player/

Schauen Sie sich die pan-gesture Zweig insbesondere: https://github.com/alfiehanssen/360Player/tree/pan-gesture

Wenn Sie den Code nach unten ziehen glaube ich, werden Sie es nicht auf einem Gerät laufen müssen als der Simulator.

ich ein Video hier gepostet, der den Bug demonstriert (bitte ex cuse die geringe resness der video datei): https://vimeo.com/174346191

Vielen Dank im Voraus für jede Hilfe!

Antwort

4

konnte ich diese Arbeit mit Quaternionen bekommen. Der vollständige Code ist hier: ThreeSixtyPlayer. Eine Probe ist hier:

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

Sorry, dies verwendet SCNVector4 anstelle von Quaternionen, aber funktioniert gut für meine Verwendung. Ich verwende es auf meinen root-most Geometrie-Knoten-Container ("rotContainer") anstelle der Kamera, aber ein kurzer Test scheint darauf hinzudeuten, dass es auch für die Kamera funktioniert.

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

BBEdits Lösung lässt sich gut mit Rotating a camera on an orbit. Richten Sie die Kamera wie in der verknüpften Antwort vorgeschlagen ein und drehen Sie den Knoten "Orbit" mit den bbedit-Ideen. Ich habe den Code für Swift-4-Version seines Code geändert, dass die Arbeit getan:

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