2013-05-05 338 views
6

我想編碼一個第一人稱相機,它的旋轉存儲在一個四元數中。不幸的是,旋轉有問題。如何正確地沿所有軸旋轉四元數?

以下功能負責旋轉相機。參數MouseSpeed傳遞鼠標移動和旋轉速度。然後函數獲取旋轉四元數,旋轉它並存儲結果。順便說一下,我使用的是Bullet Physics,這是類型和功能的來源。

void Rotate(vec2 Mouse, float Speed) 
{ 
    btTransform transform = camera->getWorldTransform(); 
    btQuaternion rotation = transform.getRotation(); 

    Mouse = Mouse * Speed;     // apply mouse sensitivity 
    btQuaternion change(Mouse.y, Mouse.x, 0); // create quaternion from angles 
    rotation = change * rotation;    // rotate camera by that 

    transform.setRotation(rotation); 
    camera->setWorldTransform(transform); 
} 

爲了說明產生的相機旋轉當鼠標移動時,我告訴你一個手繪圖。在左側顯示相機實際執行的錯誤旋轉。在右側顯示所需的正確情況。箭頭表示在向上(橙色)和向下(藍色)移動鼠標時相機旋轉的方式。

wrong rotation on the left and desired rotation on the right

正如你所看到的,只要偏航爲零,旋轉是正確的。但是它有更多的偏航,相機旋轉的圓圈越小。相反,圓圈應該像經度一樣沿着整個球體運行。

我對四元數不是很熟悉,所以在這裏我問如何正確旋轉它們。

+0

鼠標座標最有可能在屏幕空間,這意味着這樣使用它們幾乎不是一個好主意。相反,使用逆投影矩陣將它們轉換爲世界座標。 – BlackCat 2013-05-05 13:00:10

+0

我的想法是使用鼠標座標'x'和'y'作爲角度來圍繞'up'和'right'向量旋轉相機四元數。我認爲在大多數比賽中都是這樣做的。我不確定您的方法是否有效。你能再解釋一下嗎? – danijar 2013-05-05 13:40:55

回答

9

我發現如何正確地旋轉四元數我自己。關鍵是要找到我想要旋轉的軸的矢量。當角度是圍繞實際軸旋轉的量時,這些用於從軸和角度創建四元數。

下面的代碼顯示了我結束了。它也允許滾動相機,這可能會有用一段時間。

void Rotate(btVector3 Amount, float Sensitivity) 
{ 
    // fetch current rotation 
    btTransform transform = camera->getWorldTransform(); 
    btQuaternion rotation = transform.getRotation(); 

    // apply mouse sensitivity 
    Amount *= Sensitivity; 

    // create orientation vectors 
    btVector3 up(0, 1, 0); 
    btVector3 lookat = quatRotate(rotation, btVector3(0, 0, 1)); 
    btVector3 forward = btVector3(lookat.getX(), 0, lookat.getZ()).normalize(); 
    btVector3 side = btCross(up, forward); 

    // rotate camera with quaternions created from axis and angle 
    rotation = btQuaternion(up,  Amount.getY()) * rotation; 
    rotation = btQuaternion(side, Amount.getX()) * rotation; 
    rotation = btQuaternion(forward, Amount.getZ()) * rotation; 

    // set new rotation 
    transform.setRotation(rotation); 
    camera->setWorldTransform(transform); 
} 

因爲我很少發現有關四元數旋轉的信息,所以我會花一些時間進一步解釋上面的代碼。

獲取和設置旋轉是特定於物理引擎的,並且與此問題無關,所以我不會詳細說明這一點。下一部分,將數量乘以鼠標靈敏度應該非常清楚。讓我們繼續使用方向向量。

  • up矢量取決於您自己的實現。最方便的是,Y軸的正向指向,因此我們以0, 1, 0結束。
  • lookat矢量代表相機看的方向。我們只需旋轉由相機旋轉四元數指向前方的單位矢量。同樣,前向指向矢量取決於你的約定。如果Y軸向上,則正Z軸可能指向前方,即0, 0, 1
  • 不要將它與下一個向量混合。它的名稱爲forward,它引用了相機旋轉。因此,我們只需要將lookat矢量投影到地面。在這種情況下,我們只需採用lookat矢量並忽略向上指向組件。爲了整齊,我們將這個向量歸一化。
  • side向量指向相機方向向左。因此它垂直於upforward矢量,我們可以使用cross product來計算它。

給定這些向量,我們可以正確地旋轉他們周圍的相機四元數。你從哪開始,Z,Y或Z取決於Euler angle sequence,這也是一個從應用程序到應用程序不同的約定。由於我想旋轉以Y X Z順序應用,我做了以下操作。

  • 首先,圍繞up軸將相機旋轉Y軸的旋轉量。這是偏航。
  • 然後圍繞指向左邊的side軸旋轉X量。它是音高。
  • 最後,圍繞forward向量旋轉Z量來應用滾動。

要應用這些旋轉,我們需要乘以由軸和角度創建的四元數與當前相機旋轉。最後,我們將結果四元數應用於物理模擬中的物體。

0

矩陣和俯仰/偏轉/搖擺都有其侷限性,我不再使用它們,而是使用四元數。我旋轉視圖矢量並首先重新計算相機矢量,然後重新計算關於旋轉的視圖矢量的視圖矩陣。

void Camera::rotateViewVector(glm::quat quat) { 

    glm::quat rotatedViewQuat; 

    quat = glm::normalize(quat); 
    m_viewVector = glm::normalize(m_viewVector); 

    glm::quat viewQuat(0.0f, 
     m_viewVector.x, 
     m_viewVector.y, 
     m_viewVector.z); 

    viewQuat = glm::normalize(viewQuat); 

    rotatedViewQuat = (quat * viewQuat) * glm::conjugate(quat); 
    rotatedViewQuat = glm::normalize(rotatedViewQuat); 

    m_viewVector = glm::normalize(glm::vec3(rotatedViewQuat.x, rotatedViewQuat.y, rotatedViewQuat.z)); 
    m_rightVector = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_viewVector)); 
    m_upVector = glm::normalize(glm::cross(m_viewVector, m_rightVector)); 
}