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

我正在使用iOS SceneKit框架构build360video查看器。

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

SCNNode有几个属性可以用来指定它们的旋转: rotation (旋转matrix), orientation (四元数), eulerAngles (每个轴angular度)。

我读过的一切都是为了避免使用欧拉angular度来避免万向locking 。

我想用四元数的原因,我不会在这里进入。

我无法正常工作。 相机控制几乎是我想要的地方,但有什么问题。 尽pipe我试图只影响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: qx, y: qy, z: qz, w: qw) 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

如果你把代码拉下来,我相信你将不得不在模拟器上运行它。

我在这里发布了一个video,演示了这个bug(请原谅video文件的低分辨率): https ://vimeo.com/174346191

提前感谢任何帮助!

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

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

 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的解决scheme很好地结合了在轨道上旋转相机 。 按链接的答案中的build议设置摄像机,然后使用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 } }