這裏不介紹四元數的歷史了,我只說明下他是一個超複數(請不要被這個名字嚇倒了)這兒你不須要了解啥是超複數,只要你瞭解啥是向量,那麼你就能讀懂這篇文章了。
四元數的一種表示方式是: Q = xi + yj + zk + w
這兒i, j, k就能夠當作3D 空間的3個座標柱向量。 基於四元數的這種表示方式因此很直接他就能夠被表示成一個標量w
再加上一個3維向量了。
Q = [w, v]
這裏 v = xi + yj + zk
天然有必定編程經驗的人會當即把這個小傢伙用結構體來表示了以下:
struct quaternion
{
double x, y, z, w;
}
這裏咱們不必知道四元數的加減運算法則了,如今爲了解決咱們的目標(建立基於四元數的攝像機)咱們第一步須要知道怎樣把他標準化
。四元數的標準化和向量的標準化是同樣的。目的都是將模變成1. |Q| = sqrt(w^2 + x^2 +y^2 + z^2)
下面是代碼
double getLength(quaternion quat)
{
return sqrt(quat.x * quat.x + quat.y * quat.y + quat.z * quat.z + quat.w * quat.w);
}
(呵呵,長度計算這樣給你們寫出來請你們不要告我小瞧你們的智商)
那麼獲得標準化得四元數天然是水到渠成的事了 Q* = Q/|Q| = [w/|Q|, v/|Q|];
這兒是代碼:
quaternion normalize(quaternion quat)
{
double Length = length(quat);
quat.x /= Length;
quat.y /= Length;
quat.z /= Lenght;
quat.w /= Length;
return quat;
}
下來咱們須要知道怎樣計算一個四元數的共軛四元數, 共軛四元數暫且用Q' 來代替吧。
Q' = [w, -v]
這兒是計算共軛四元數的代碼:
quaternion conjugate(quaternion quat)
{
quat.x = -quat.x;
quat.y = -quat.y;
quat.z = -quat.z;
quat.w = -quat.w;
return quat;
}
下面的事情就是看看怎樣計算四元數的成績了,這個貌似有點小小的複雜。
假設有四元數C , A , B 如今咱們要計算 C= A* B;
具體的運算規則以下:
C.x = |A.w * B.x + A.x * B.w + A.y * B.z - A.z * B.y|
C.y = |A.w * B.y - A.x * B.z + A.y * B.w + A.z * B.x|
C.z = |A.w * B.z + A.x * B.y - A.y * B.x + A.z * B.w|
C.w = |A.w * B.w - A.x * B.x - A.y * B.y - A.z * B.z|
這兒是代碼:
quaternion mult(quaternion quat)
{
quaternion C;
C.x = A.w*B.x + A.x*B.w + A.y*B.z - A.z*B.y;
C.y = A.w*B.y - A.x*B.z + A.y*B.w + A.z*B.x;
C.z = A.w*B.z + A.x*B.y - A.y*B.x + A.z*B.w;
C.w = A.w*B.w - A.x*B.x - A.y*B.y - A.z*B.z;
return C;
}
主要的運算講完了,下面就要進入正題了。 如今咱們是要建立攝像機了請注意。很明顯在3D空間裏面表明一個攝像機
就須要對這個攝像機進行定位,定向了。因此爲了精確的建立攝像機,咱們使用3個3維向量來表明攝像機的位置Position,觀察方向View,
和攝像機的UP(這個不必介紹吧)。對於一個第一人稱的攝像機來講,咱們如今就只須要考慮攝像機的旋轉問題了,
使用四元數咱們就能夠把一個向量繞任意的柱旋轉, 爲了達到這個目的咱們就須要首先將View向量轉化成四元數,而後定義一個旋轉四元數,最後應用這個旋轉四元數到View四元數上就好了。下面請看具體的步驟。
爲了得到View四元數,咱們就要使用[w, v]來表明了。固然標量w 就設成0了。v 很顯然就是View向量了。
所以四元數V(咱們轉化的View四元數) V = [0, View]
而後正如上面所說咱們下面就要建立那個旋轉四元數了,爲了建立這個四元數,你就得明確你要繞哪一個向量旋轉了,還有旋轉的角度。咱們把這個柱向量(就是繞哪一個向量旋轉)叫作A,把旋轉的角度用theta表示,那麼任務即將完成了,咱們建立的旋轉四元數R就能夠表示成:
3DVector A = [x, y , z];
R.x = A.x * sin(theta/2);
R.y = A.y * sin(theta/2);
R.z = A.z * sin(theta/2);
R.w = cos(theta/2);
下來就要進行旋轉計算了:
先看看咱們如今知道的量:
1. 咱們有3維的View向量, 咱們有View四元數 V = [0, View]
2. 假設咱們要繞A向量來旋轉theta度,咱們有旋轉四元數R來定義此次旋轉。
3. 記住旋轉完後咱們獲得的東西仍然是一個View四元數,這個天然是個新的View四元數了。咱們假設他是W
因此旋轉操做就是:
W = R * V * R'
這裏R是旋轉四元數, V是View四元數, R' 是旋轉四元數R的共軛四元數(其運算前面以講了)
如今咱們僅僅提取新的View四元數W的向量部分。 NewView = [W.x, W.y, W.z]
因此綜上所述咱們就能夠建立函數來進行旋轉變換:
void RotateCamera(float angle, float x, float y, float z)
{
quaternion temp, quat_view, result;// quat_view是View四元數, temp是旋轉四元數
// 下面的三行使用旋轉柱和角度計算旋轉四元數
temp.x = x * sin(angle/2);
temp.y = y * sin(angle/2);
temp.z = z * sin(angle/2);
temp.w = cos(angle/2);
// 計算View四元數
quat_view.x = View.x; // View 是View向量
quat_view.y = View.y;
quat_view.z = View.z;
quat_view.w = 0;
// 進行旋轉變換
result = mult(mult(temp, qaut_view), conjugate(temp));
// 獲得新的View向量
View.x = result.x;
View.y = result.y;
View.z = result.z;
}
至此完成。相信有必定OpenGL或D3D基礎的人,很容易就把這個翻譯成代碼了。算法