簡體   English   中英

如何使用平移手勢使用四元數在SceneKit中旋轉相機

[英]How to use a pan gesture to rotate a camera in SceneKit using quaternions

我正在使用iOS SceneKit框架構建360視頻查看器。

我想使用UIPanGestureRecognizer來控制相機的方向。

SCNNode有幾個屬性我們可以用來指定它們的旋轉: rotation (旋轉矩陣), orientation (四元數), eulerAngles (每個軸角度)。

我讀過的所有內容都說避免使用歐拉角來避免萬向節鎖定

我想使用四元數有幾個原因,我不會在這里討論。

我無法讓它正常工作。 相機控制幾乎就是我想要的地方,但有些不對勁。 盡管我試圖僅影響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

在此先感謝您的幫助!

我能夠使用四元數來實現這一點。 完整的代碼在這里: 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”)而不是相機,但一個簡短的測試似乎表明它也適用於相機使用。

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的解決方案與旋轉軌道上的攝像機完美結合。 按照鏈接答案中的建議設置攝像機,然后使用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 } } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM