淺談Ogre的四元數與旋轉-1

yangyue811發表於2007-04-18
轉自:  Ogre Wiki
作者:  Cory Petkovsek
翻譯肥羊
 
目錄
·1簡介
·2必備知識
       ·2.1向量(Vectors)
·3尤拉(Euler)旋轉
·4矩陣(Matrix)旋轉
·5四元數(Quaternion)旋轉
·6問題
       ·6.1四元數
       ·6.2其它
·7程式碼片斷
       ·7.1物體在XZ平面上的旋轉
       ·7.2平滑旋轉
       ·7.3重設方向
 
簡介:
如果你在Ogre中處理完物件,發現它們不像你想象的那樣旋轉。那麼,此時閱讀這篇文章,大概會有些勉強。當我剛接觸Ogre時,我用了很長的時間才理解了向量(vector)。在學習矩陣(matrix)時,那時我還不懂四元數,矩陣的某些知識點搞得我非常迷茫。這篇文章是針對Ogre中四元數旋轉的實際運用,其中沒有介紹相關的數學知識。
 
必備知識:
在學習四元數之前,讓我們回顧一下尤拉角、矩陣和向量數學這些基礎知識。在這篇文章和Ogre中,我們使用的都是右手座標系。這意味著兩件事:其一,我們認為+X軸指向螢幕的正右方,+Y軸指向螢幕的正上方,+Z軸透過螢幕垂直指向你。其二,要查+45度角是如何旋轉的,將你的右手拇指沿旋轉軸方向伸出,其餘手指的自然彎曲方向就是實際的角度旋轉方向。
向量:
在數學裡,一個向量描述了在3D空間中從一點指向另一點的一條有方向的線段。它具有方向和距離。這說明它並不是一般意義上的沒有方向的線段(譯者注:線段沒有方向)也不是一條無限的射線(譯者注:射線沒有大小)。向量具有射線與線段的共同特徵。(譯者注:向量是一個有大小由方向的量。)
在3D空間中描述兩個點需要六個引數。在Ogre中,向量的儲存是假設第一個點為(0,0,0)第二個點為一個相關偏移量。這是向量的一個通常用法。可以用(0,0,0)作為世界的根,或者將根設定於一個物件的物體座標系中。當進行變換(transformations)時只要知道你工作在那個座標體系下就可以了。由於向量只有三個引數,所以經常被用來定義三位點。
長度為1的向量稱為規格化向量。當我們僅僅要描述一個方向時可以使用單位向量或規格化向量。如果你期望的結果是一個需要通過許多精確的運算時,使用規格化限量是必須的。不要忘記規格化你的向量!
在Ogre中,向量的普遍用法是描述一個單一的點、使用單位向量定義一個方向或者計算兩點之間的距離。
下面是一些有用的向量操作
·     向量B減去向量A
得到一個包含從A到B的距離和方向的向量(在這個例子中,A可以是(0,0,0),表示B的向量可以按A的自身的物體座標系定義,而不是世界座標系)。
即:Bv – Av = Av ->Bv
·     向量A加上向量B
向量A加上向量B得到向量C。如果向量A與向量B首尾相接,就好像兩個箭頭依次指出了一個路徑,向量C由向量A的尾部指向B的首部,構成一個三角形。
       ·乘上或除以一個標量
乘上或除以一個標量並不改變向量的方向,只是改變此向量的大小。例如,Vector(1,1,1)描述了一條(或者一個預設原點的3D點)與XZ軸平面正向夾角45度的線(向上,位與+X,+Y,+Z三個軸所圍空間中),與XY平面45度(面向你),與YZ平面45度(向右)。其長度為sqrt(X^2+Y^2+Z^2)或者sqrt(3)。如果我們將向量乘以2,得到(2,2,2),構成了一個相同方向的向量。詳情參閱Ogre API的Vector3::length()。
       ·向量的叉乘
兩個向量叉乘得到一個與兩個向量都垂直的向量。這是一個非常有用的操作。如果你要指出垂直於一個多邊形的向量,你可以叉乘多邊形的兩個邊,那麼你就能夠得到一個垂直於這個多邊形平面的向量。詳情參閱Ogre API的Vector3 Vector3::crossProduct(Vector3)。
       ·向量的點乘
兩個向量點乘能得到它們的夾角。計算如下:DP(V1, V2) = (Cos Th) * Len1 * Len2,Len1和Len2是向量的大小。儘管如此,你也最好將向量都規格化,這可以使它們的大小都為1。規格化後的點乘計算:Th = ArcCos(V1, V2); Len1=Len2=1。詳情參閱Ogre API的Real Vector3::dotProduct(Vector3)。
尤拉旋轉:
我們最常用的旋轉方法應該是使用yaw, roll和pitch。yaw是在XZ軸平面上圍繞Y軸左右旋轉,當開車時使用的是yaw。pitch在YZ軸平面上圍繞X軸上下旋轉,噴氣機飛行或爬坡時用pitch向上或向下。roll是在XY軸平面上繞Z軸傾斜旋轉,從字面意思上說,當你駕駛汽車高速急轉彎時,你的汽車會出現roll運動:P
尤拉旋轉十分的有用,Ogre API也提供了尤拉旋轉。但是,旋轉時也會出問題。一個是操作的順序不能亂。如果你將物件先pitch 90度,然後再yaw 45度得到的結果肯定與先yaw  90度再pitch 45度不同。另外一個叫做萬向節鎖。萬向節是一個旋轉軸。這個問題發生在兩個旋轉軸重合時,此時需要刪掉一個。
(譯者注:尤拉角最著名的問題是這樣的:當你先yaw 45度再pitch 90度,和先pitch90度再roll 90度是等價的。事實上,一旦先擇±90度為pitch角,就被限定在只能繞豎軸旋轉。這種現象,角度為±90度的第二次旋轉使得第一次和第三次旋轉軸相同,成為萬向鎖。為了消除限制尤拉角的這種別名現象,規定萬向鎖情況下只能由yaw完成繞豎軸的旋轉。即若pitch = ±90度,則roll = 0)
總的來說,尤拉角在實際的應用是受限的。這裡我所指的是使用yaw, roll, pitch操作讓物件任意旋轉並不是很實用。如果你要一個可以任意pitch / yaw的相機(就像許多第一人稱視角射擊遊戲)的話,它們是很有用的。使用雙場景節點的技巧,你可以得到一個僅用yaw和pitch操作卻能實現你所需要的全部旋轉功能的相機。儘管如此,當你的一個物件面對一個方向,而且你想讓它面對處於任意位置的物件時,取當前的yaw, roll和pitch值後再計算與新方向之間的角度差並不是十分有效的。同樣的,如何使旋轉更加平滑?進入矩陣的學習。
矩陣旋轉:
由於使用任意三圍尤拉旋轉會產生大量的操作,通常使用矩陣來儲存和轉換它們。
我們使用三個矩陣來分別表示來自X, Y, Z軸的角偏移,即我們上述的pitch (圍繞X軸), yaw(圍繞Y軸)和roll(圍繞Z軸)。我們將其存入獨立的矩陣: matRotationX, matRotationY, matRotationZ。當我們確定我們要旋轉的角度,並且將其儲存到矩陣中。我們可以通過矩陣乘法將大量的旋轉操作合併為一個簡單的矩陣轉換操作:
matTransform = matRotationX * matRotationY * matRotationZ;
幸運的是,Ogre提供Matrix3類來操作3X3的矩陣。這個類中也包含有*操作,因此上面的那個語句將在你的程式碼中良好的工作。不過,在Ogre中要真正旋轉一個物件,你可以將其轉換為軸/角旋轉儲存資料到矩陣自己做變換或者轉換為四元數。
       注意,矩陣的乘法是不可交換的。A*B != B*A。矩陣旋轉的順序都是很重要的,就像尤拉角一樣,不能隨意變化。另外,以矩陣形式表現的尤拉角依然受到萬向鎖的影響。
       矩陣也可以用來操作軸/角旋轉。上述矩陣使用了yaw, roll, pitch的概念。試想一下要是這些換為分別繞X, Y和Z軸的旋轉,這將實現所有的軸/角旋轉。通過這個我們避免了萬向鎖問題(尚未弄明白)。Ogre尚未提供旋轉矩陣與四元數之間的轉換。因此如果你要使用旋轉矩陣,你將需要自己手動將其轉為四元數。
       尤拉角和矩陣以及旋轉矩陣有個共同的問題,叫做矩陣偏移,這將在下一部分敘述。
四元數旋轉:
對於初學者來說,四元數是一個相當有趣的概念。正如上面描述的那樣,當使用四元數時,我發現停止用yaw, pitch和roll思考而改為用繞軸的旋轉來思考變得很容易。例如,我們要模仿一架噴氣機上的推進器,噴氣機面向Z軸。在尤拉角中,這個旋轉應該被稱作roll。在四元數中,其為繞一指向Z軸的向量旋轉或繞Vector3::UNIT_Z旋轉。
四元數由四部分組成: 一個具有x,y,z座標和w旋轉分量的向量。這是我在矩陣那一節提到的軸/角的表示方法。即使使用假設的引數,四元數數學也是相當棘手的。幸運的是,我們不需要從數學的角度使用它,也不需要了解它為什麼要解釋成旋轉。來做個試驗,首先豎起你左手的食指,然後用你的右手握住筆的一頭。現在用你手腕和手做圓周運動,儘可能水平的旋轉筆,始終保持其垂直於你左手的手指、平行於地面,就好像你把筆放在桌子上旋轉一樣。將這個想象成為繞Vector3::UNIT_Y旋轉的四元數。
這時我們有一個從(0,0,0)到(1,1,-1)的向量。用剛才的手指向右指、保持與地面45度夾角。現在,圍繞你的手指旋轉筆,始終保持筆與手指垂直。注意我們如何通過角與軸的選擇建立了一個不同的旋轉。
像矩陣一樣,我們也可以使用四元數相乘來計算之間的角度。不過四元數相乘仍然是不可交換的。Q1*Q2 != Q2*Q1。適用的順序依然很重要。四元數也避免了萬向鎖問題。
 
四元數的優點
四元數有許多矩陣沒有的優點。不過它依然遵循像數一樣的正、負、乘法或加法等操作。按照實踐得到的結論,優點如下:
       ·在軸/角表示中避免了萬向鎖。
       ·修改旋轉更加容易。
假設我們描述一個-45度軸向左yaw旋轉,建立一個新的四元數描述10度軸yaw向右旋轉。將兩個四元數相乘,我們擁有了一個-35度軸的旋轉。這可以使用在其它物件或多個相同的物件需要實現相同旋轉的情況。旋轉因子的變化取決於環境。
       ·避免了耗費操作的矩陣偏移
當在矩陣上使用計算機中有限的點精確度執行大量的操作時,會發生矩陣偏移。四捨五入掉的實數逐漸增大,將會不可避免的弄壞矩陣。矩陣偏移將會導致異常的旋轉的出現。必須把矩陣規格化才能重置旋轉,這將非常耗費操作。從另一方面來說,四元數也有類似的痛苦的偏移,不過,由9個或更多的引數操作降至4個,這比規格化省力多了。
       ·增值旋轉
當旋轉物件時,我們希望物件旋轉進行的平緩。我們常說“從你當前位置轉到這個點。但是要使用300幀來實現它,每次移動整個角度的1/300。”矩陣的增值是可能的,但是別的方式可以麼?四元數提供了兩種增值方法: 線的(linear)和三次的(cubic)。Ogre提供了三個執行方法: slerp, nlerp和squad。一次增值允許兩個四元數間增加的百分比是指定的。線性指的是運動的速率是固定的,如果使用相機或節點會感覺到是急動的。slerp和nlerp都是線性的,slerp更精確但更慢些。squad或cubic增值允許一次性指定四個四元數a, p, q, b。p, q用來定義點a,b間的增值曲線。它允許我們在增值時緩慢的增大速度,停止,減少速度。
       ·Ogre使用四元數
              這是應該你願讀這篇文章的原因吧:)
 
       一些有用的四元數
             
W
X
Y
Z
描述
1
0
0
0
恆定四元數,不旋轉
0
1
0
0
繞X軸旋轉180度
0
0
1
0
繞Y軸旋轉180度
0
0
0
1
繞Z軸旋轉180度
sqrt(0.5)
sqrt(0.5)
0
0
繞X軸旋轉90度
sqrt(0.5)
0
sqrt(0.5)
0
繞Y軸旋轉90度
sqrt(0.5)
0
0
sqrt(0.5)
繞Z軸旋轉90度
sqrt(0.5)
-sqrt(0.5)
0
0
繞X軸旋轉-90度
sqrt(0.5)
0
-sqrt(0.5)
0
繞Y軸旋轉-90度
sqrt(0.5)
0
0
-sqrt(0.5)
繞Z軸旋轉-90度
 
 
 

相關文章