歐拉角


四元數計算公式
四元數的基本數學方程為 : q = cos (a/2) + i(x * sin(a/2)) + j(y * sin(a/2)) + k(z * sin(a/2)) 其中a表示旋轉角度,(x,y,z)表示旋轉軸
下面是如何把具體的四元數與旋轉軸和旋轉角度對應起來。
1.指出旋轉軸和旋轉角度,如何轉化為四元素。
假定旋轉軸是:RAxis = Z軸,換算成三維空間單位向量就是RAxis = [0 0 1],旋轉60度
那么轉化成四元數就是
q.w=cos(60°/2) = 0.866
q.x=RAix.x*sin(60°/2) = 0*0.5=0
q.y=RAix.y*sin(60°/2) = 0*0.5=0
q.z=RAix.z*sin(60°/2) = 1*0.5=0.5
例子驗證:從三維空間中看,假定物體點A=[0 1 0],繞 RAxis = Z軸,旋轉30度(假定順時針為正,因為matlab就是順時針為正,而下面的quat2dcm函數是matlab自帶的)
那么物體點A旋轉后在世界坐標系下的坐標將是B=[0.866 0.5 0],
如何用四元數計算出呢?思路是這樣的:任何一個四元數對應着一個旋轉3*3矩陣。
M=quat2dcm(q)*A'=[0.866;0.5;0],關於quat2dcm在軟件matlab里面有。
四元數編程
四元數的一種表示方式是: 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基礎的人,很容易就把這個翻譯成代碼了。
這兒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基礎的人,很容易就把這個翻譯成代碼了。
