簡(jiǎn)介:
如果你在Ogre中處理完對(duì)象,,發(fā)現(xiàn)它們不像你想象的那樣旋轉(zhuǎn),。那么,此時(shí)閱讀這篇文章,,大概會(huì)有些勉強(qiáng),。當(dāng)我剛接觸Ogre時(shí),我用了很長(zhǎng)的時(shí)間才理解了向量(vector),。在學(xué)習(xí)矩陣(matrix)時(shí),,那時(shí)我還不懂四元數(shù),矩陣的某些知識(shí)點(diǎn)搞得我非常迷茫,。這篇文章是針對(duì)Ogre中四元數(shù)旋轉(zhuǎn)的實(shí)際運(yùn)用,,其中沒(méi)有介紹相關(guān)的數(shù)學(xué)知識(shí)。
必備知識(shí):
在學(xué)習(xí)四元數(shù)之前,,讓我們回顧一下歐拉角,、矩陣和向量數(shù)學(xué)這些基礎(chǔ)知識(shí)。在這篇文章和Ogre中,,我們使用的都是右手坐標(biāo)系,。這意味著兩件事:其一,我們認(rèn)為+X軸指向屏幕的正右方,,+Y軸指向屏幕的正上方,,+Z軸透過(guò)屏幕垂直指向你。其二,,要查+45度角是如何旋轉(zhuǎn)的,,將你的右手拇指沿旋轉(zhuǎn)軸方向伸出,其余手指的自然彎曲方向就是實(shí)際的角度旋轉(zhuǎn)方向,。
向量:
在數(shù)學(xué)里,,一個(gè)向量描述了在3D空間中從一點(diǎn)指向另一點(diǎn)的一條有方向的線段。它具有方向和距離,。這說(shuō)明它并不是一般意義上的沒(méi)有方向的線段(譯者注:線段沒(méi)有方向)也不是一條無(wú)限的射線(譯者注:射線沒(méi)有大?。O蛄烤哂猩渚€與線段的共同特征,。(譯者注:向量是一個(gè)有大小由方向的量,。)
在3D空間中描述兩個(gè)點(diǎn)需要六個(gè)參數(shù)。在Ogre中,向量的存儲(chǔ)是假設(shè)第一個(gè)點(diǎn)為(0,0,0)第二個(gè)點(diǎn)為一個(gè)相關(guān)偏移量,。這是向量的一個(gè)通常用法,。可以用(0,0,0)作為世界的根,,或者將根設(shè)定于一個(gè)對(duì)象的物體坐標(biāo)系中,。當(dāng)進(jìn)行變換(transformations)時(shí)只要知道你工作在那個(gè)坐標(biāo)體系下就可以了。由于向量只有三個(gè)參數(shù),,所以經(jīng)常被用來(lái)定義三位點(diǎn),。
長(zhǎng)度為1的向量稱為規(guī)格化向量。當(dāng)我們僅僅要描述一個(gè)方向時(shí)可以使用單位向量或規(guī)格化向量,。如果你期望的結(jié)果是一個(gè)需要通過(guò)許多精確的運(yùn)算時(shí),,使用規(guī)格化限量是必須的。不要忘記規(guī)格化你的向量,!
在Ogre中,,向量的普遍用法是描述一個(gè)單一的點(diǎn)、使用單位向量定義一個(gè)方向或者計(jì)算兩點(diǎn)之間的距離,。
下面是一些有用的向量操作
· 向量B減去向量A
得到一個(gè)包含從A到B的距離和方向的向量(在這個(gè)例子中,,A可以是(0,0,0),表示B的向量可以按A的自身的物體坐標(biāo)系定義,,而不是世界坐標(biāo)系),。
即:Bv – Av = Av ->Bv 。
· 向量A加上向量B
向量A加上向量B得到向量C,。如果向量A與向量B首尾相接,,就好像兩個(gè)箭頭依次指出了一個(gè)路徑,向量C由向量A的尾部指向B的首部,,構(gòu)成一個(gè)三角形,。
·乘上或除以一個(gè)標(biāo)量
乘上或除以一個(gè)標(biāo)量并不改變向量的方向,只是改變此向量的大小,。例如,,Vector(1,1,1)描述了一條(或者一個(gè)默認(rèn)原點(diǎn)的3D點(diǎn))與XZ軸平面正向夾角45度的線(向上,位與+X,,+Y,,+Z三個(gè)軸所圍空間中),與XY平面45度(面向你),,與YZ平面45度(向右),。其長(zhǎng)度為sqrt(X^2+Y^2+Z^2)或者sqrt(3)。如果我們將向量乘以2,,得到(2,2,2),,構(gòu)成了一個(gè)相同方向的向量,。詳情參閱Ogre API的Vector3::length()。
·向量的叉乘
兩個(gè)向量叉乘得到一個(gè)與兩個(gè)向量都垂直的向量,。這是一個(gè)非常有用的操作,。如果你要指出垂直于一個(gè)多邊形的向量,你可以叉乘多邊形的兩個(gè)邊,,那么你就能夠得到一個(gè)垂直于這個(gè)多邊形平面的向量,。詳情參閱Ogre API的Vector3 Vector3::crossProduct(Vector3)。
·向量的點(diǎn)乘
兩個(gè)向量點(diǎn)乘能得到它們的夾角,。計(jì)算如下:DP(V1, V2) = (Cos Th) * Len1 * Len2,,Len1和Len2是向量的大小。盡管如此,,你也最好將向量都規(guī)格化,這可以使它們的大小都為1,。規(guī)格化后的點(diǎn)乘計(jì)算:Th = ArcCos(V1, V2); Len1=Len2=1,。詳情參閱Ogre API的Real Vector3::dotProduct(Vector3)。
歐拉旋轉(zhuǎn):
我們最常用的旋轉(zhuǎn)方法應(yīng)該是使用yaw, roll和pitch,。yaw是在XZ軸平面上圍繞Y軸左右旋轉(zhuǎn),,當(dāng)開(kāi)車時(shí)使用的是yaw。pitch在YZ軸平面上圍繞X軸上下旋轉(zhuǎn),,噴氣機(jī)飛行或爬坡時(shí)用pitch向上或向下,。roll是在XY軸平面上繞Z軸傾斜旋轉(zhuǎn),從字面意思上說(shuō),,當(dāng)你駕駛汽車高速急轉(zhuǎn)彎時(shí),,你的汽車會(huì)出現(xiàn)roll運(yùn)動(dòng):P
歐拉旋轉(zhuǎn)十分的有用,Ogre API也提供了歐拉旋轉(zhuǎn),。但是,,旋轉(zhuǎn)時(shí)也會(huì)出問(wèn)題。一個(gè)是操作的順序不能亂,。如果你將對(duì)象先pitch 90度,,然后再yaw 45度得到的結(jié)果肯定與先yaw 90度再pitch 45度不同。另外一個(gè)叫做萬(wàn)向節(jié)鎖,。萬(wàn)向節(jié)是一個(gè)旋轉(zhuǎn)軸,。這個(gè)問(wèn)題發(fā)生在兩個(gè)旋轉(zhuǎn)軸重合時(shí),此時(shí)需要?jiǎng)h掉一個(gè),。
(譯者注:歐拉角最著名的問(wèn)題是這樣的:當(dāng)你先yaw 45度再pitch 90度,,和先pitch90度再roll 90度是等價(jià)的。事實(shí)上,,一旦先擇±90度為pitch角,,就被限定在只能繞豎軸旋轉(zhuǎn),。這種現(xiàn)象,角度為±90度的第二次旋轉(zhuǎn)使得第一次和第三次旋轉(zhuǎn)軸相同,,成為萬(wàn)向鎖,。為了消除限制歐拉角的這種別名現(xiàn)象,規(guī)定萬(wàn)向鎖情況下只能由yaw完成繞豎軸的旋轉(zhuǎn),。即若pitch = ±90度,,則roll = 0)
總的來(lái)說(shuō),歐拉角在實(shí)際的應(yīng)用是受限的,。這里我所指的是使用yaw, roll, pitch操作讓對(duì)象任意旋轉(zhuǎn)并不是很實(shí)用,。如果你要一個(gè)可以任意pitch / yaw的相機(jī)(就像許多第一人稱視角射擊游戲)的話,它們是很有用的,。使用雙場(chǎng)景節(jié)點(diǎn)的技巧,,你可以得到一個(gè)僅用yaw和pitch操作卻能實(shí)現(xiàn)你所需要的全部旋轉(zhuǎn)功能的相機(jī)。盡管如此,,當(dāng)你的一個(gè)對(duì)象面對(duì)一個(gè)方向,,而且你想讓它面對(duì)處于任意位置的對(duì)象時(shí),取當(dāng)前的yaw, roll和pitch值后再計(jì)算與新方向之間的角度差并不是十分有效的,。同樣的,,如何使旋轉(zhuǎn)更加平滑?進(jìn)入矩陣的學(xué)習(xí),。
矩陣旋轉(zhuǎn):
由于使用任意三圍歐拉旋轉(zhuǎn)會(huì)產(chǎn)生大量的操作,,通常使用矩陣來(lái)存儲(chǔ)和轉(zhuǎn)換它們,。
我們使用三個(gè)矩陣來(lái)分別表示來(lái)自X, Y, Z軸的角偏移,,即我們上述的pitch (圍繞X軸), yaw(圍繞Y軸)和roll(圍繞Z軸)。我們將其存入獨(dú)立的矩陣: matRotationX, matRotationY, matRotationZ,。當(dāng)我們確定我們要旋轉(zhuǎn)的角度,,并且將其存儲(chǔ)到矩陣中。我們可以通過(guò)矩陣乘法將大量的旋轉(zhuǎn)操作合并為一個(gè)簡(jiǎn)單的矩陣轉(zhuǎn)換操作:
matTransform = matRotationX * matRotationY * matRotationZ;
幸運(yùn)的是,,Ogre提供Matrix3類來(lái)操作3X3的矩陣,。這個(gè)類中也包含有*操作,因此上面的那個(gè)語(yǔ)句將在你的代碼中良好的工作,。不過(guò),,在Ogre中要真正旋轉(zhuǎn)一個(gè)對(duì)象,你可以將其轉(zhuǎn)換為軸/角旋轉(zhuǎn)存儲(chǔ)數(shù)據(jù)到矩陣自己做變換或者轉(zhuǎn)換為四元數(shù),。
注意,,矩陣的乘法是不可交換的。A*B != B*A,。矩陣旋轉(zhuǎn)的順序都是很重要的,,就像歐拉角一樣,,不能隨意變化。另外,,以矩陣形式表現(xiàn)的歐拉角依然受到萬(wàn)向鎖的影響,。
矩陣也可以用來(lái)操作軸/角旋轉(zhuǎn)。上述矩陣使用了yaw, roll, pitch的概念,。試想一下要是這些換為分別繞X, Y和Z軸的旋轉(zhuǎn),,這將實(shí)現(xiàn)所有的軸/角旋轉(zhuǎn)。通過(guò)這個(gè)我們避免了萬(wàn)向鎖問(wèn)題(尚未弄明白),。Ogre尚未提供旋轉(zhuǎn)矩陣與四元數(shù)之間的轉(zhuǎn)換,。因此如果你要使用旋轉(zhuǎn)矩陣,你將需要自己手動(dòng)將其轉(zhuǎn)為四元數(shù),。
歐拉角和矩陣以及旋轉(zhuǎn)矩陣有個(gè)共同的問(wèn)題,,叫做矩陣偏移,這將在下一部分?jǐn)⑹觥?br>四元數(shù)旋轉(zhuǎn):
對(duì)于初學(xué)者來(lái)說(shuō),,四元數(shù)是一個(gè)相當(dāng)有趣的概念,。正如上面描述的那樣,當(dāng)使用四元數(shù)時(shí),,我發(fā)現(xiàn)停止用yaw, pitch和roll思考而改為用繞軸的旋轉(zhuǎn)來(lái)思考變得很容易。例如,,我們要模仿一架噴氣機(jī)上的推進(jìn)器,,噴氣機(jī)面向Z軸。在歐拉角中,,這個(gè)旋轉(zhuǎn)應(yīng)該被稱作roll,。在四元數(shù)中,其為繞一指向Z軸的向量旋轉(zhuǎn)或繞Vector3::UNIT_Z旋轉(zhuǎn),。
四元數(shù)由四部分組成: 一個(gè)具有x,y,z坐標(biāo)和w旋轉(zhuǎn)分量的向量,。這是我在矩陣那一節(jié)提到的軸/角的表示方法。即使使用假設(shè)的參數(shù),,四元數(shù)數(shù)學(xué)也是相當(dāng)棘手的,。幸運(yùn)的是,我們不需要從數(shù)學(xué)的角度使用它,,也不需要了解它為什么要解釋成旋轉(zhuǎn),。來(lái)做個(gè)試驗(yàn),首先豎起你左手的食指,,然后用你的右手握住筆的一頭?,F(xiàn)在用你手腕和手做圓周運(yùn)動(dòng),盡可能水平的旋轉(zhuǎn)筆,,始終保持其垂直于你左手的手指,、平行于地面,,就好像你把筆放在桌子上旋轉(zhuǎn)一樣。將這個(gè)想象成為繞Vector3::UNIT_Y旋轉(zhuǎn)的四元數(shù),。
這時(shí)我們有一個(gè)從(0,0,0)到(1,1,-1)的向量,。用剛才的手指向右指、保持與地面45度夾角?,F(xiàn)在,,圍繞你的手指旋轉(zhuǎn)筆,始終保持筆與手指垂直,。注意我們?nèi)绾瓮ㄟ^(guò)角與軸的選擇創(chuàng)建了一個(gè)不同的旋轉(zhuǎn),。
像矩陣一樣,我們也可以使用四元數(shù)相乘來(lái)計(jì)算之間的角度,。不過(guò)四元數(shù)相乘仍然是不可交換的,。Q1*Q2 != Q2*Q1。適用的順序依然很重要,。四元數(shù)也避免了萬(wàn)向鎖問(wèn)題,。
四元數(shù)的優(yōu)點(diǎn)
四元數(shù)有許多矩陣沒(méi)有的優(yōu)點(diǎn)。不過(guò)它依然遵循像數(shù)一樣的正,、負(fù),、乘法或加法等操作。按照實(shí)踐得到的結(jié)論,,優(yōu)點(diǎn)如下:
·在軸/角表示中避免了萬(wàn)向鎖,。
·修改旋轉(zhuǎn)更加容易。
假設(shè)我們描述一個(gè)-45度軸向左yaw旋轉(zhuǎn),,創(chuàng)建一個(gè)新的四元數(shù)描述10度軸yaw向右旋轉(zhuǎn),。將兩個(gè)四元數(shù)相乘,我們擁有了一個(gè)-35度軸的旋轉(zhuǎn),。這可以使用在其它對(duì)象或多個(gè)相同的對(duì)象需要實(shí)現(xiàn)相同旋轉(zhuǎn)的情況,。旋轉(zhuǎn)因子的變化取決于環(huán)境。
·避免了耗費(fèi)操作的矩陣偏移
當(dāng)在矩陣上使用計(jì)算機(jī)中有限的點(diǎn)精確度執(zhí)行大量的操作時(shí),,會(huì)發(fā)生矩陣偏移,。四舍五入掉的實(shí)數(shù)逐漸增大,將會(huì)不可避免的弄壞矩陣,。矩陣偏移將會(huì)導(dǎo)致異常的旋轉(zhuǎn)的出現(xiàn),。必須把矩陣規(guī)格化才能重置旋轉(zhuǎn),這將非常耗費(fèi)操作,。從另一方面來(lái)說(shuō),,四元數(shù)也有類似的痛苦的偏移,不過(guò),,由9個(gè)或更多的參數(shù)操作降至4個(gè),,這比規(guī)格化省力多了,。
·增值旋轉(zhuǎn)
當(dāng)旋轉(zhuǎn)對(duì)象時(shí),我們希望對(duì)象旋轉(zhuǎn)進(jìn)行的平緩,。我們常說(shuō)“從你當(dāng)前位置轉(zhuǎn)到這個(gè)點(diǎn),。但是要使用300幀來(lái)實(shí)現(xiàn)它,每次移動(dòng)整個(gè)角度的1/300,。”矩陣的增值是可能的,,但是別的方式可以么?四元數(shù)提供了兩種增值方法: 線的(linear)和三次的(cubic),。Ogre提供了三個(gè)執(zhí)行方法: slerp, nlerp和squad,。一次增值允許兩個(gè)四元數(shù)間增加的百分比是指定的。線性指的是運(yùn)動(dòng)的速率是固定的,,如果使用相機(jī)或節(jié)點(diǎn)會(huì)感覺(jué)到是急動(dòng)的,。slerp和nlerp都是線性的,slerp更精確但更慢些,。squad或cubic增值允許一次性指定四個(gè)四元數(shù)a, p, q, b,。p, q用來(lái)定義點(diǎn)a,b間的增值曲線。它允許我們?cè)谠鲋禃r(shí)緩慢的增大速度,,停止,,減少速度。
·Ogre使用四元數(shù)
這是應(yīng)該你愿讀這篇文章的原因吧:)
一些有用的四元數(shù)
W X Y Z 描述
1 0 0 0 恒定四元數(shù),,不旋轉(zhuǎn)
0 1 0 0 繞X軸旋轉(zhuǎn)180度
0 0 1 0 繞Y軸旋轉(zhuǎn)180度
0 0 0 1 繞Z軸旋轉(zhuǎn)180度
sqrt(0.5) sqrt(0.5) 0 0 繞X軸旋轉(zhuǎn)90度
sqrt(0.5) 0 sqrt(0.5) 0 繞Y軸旋轉(zhuǎn)90度
sqrt(0.5) 0 0 sqrt(0.5) 繞Z軸旋轉(zhuǎn)90度
sqrt(0.5) -sqrt(0.5) 0 0 繞X軸旋轉(zhuǎn)-90度
sqrt(0.5) 0 -sqrt(0.5) 0 繞Y軸旋轉(zhuǎn)-90度
sqrt(0.5) 0 0 -sqrt(0.5) 繞Z軸旋轉(zhuǎn)-90度
本文來(lái)自CSDN博客,,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/yangyue811/archive/2007/04/18/1569501.aspx