【3D Math Keynote 2】算法
一、方向(diretion),指的是前方朝向。方位(orientation),指的是head、pitch、roll。ide
二、歐拉角的缺點:spa
1)給定方位的表達式不唯一。code
例如,pitch 135 = heading180 + pitch 45 + bank 180。blog
經過將 heading、bank 限制在 +180~-180度,pitch限制在+90~-90度便可解決不唯一的問題。數學
2)兩個角度間插值很是困難。it
三、複數的共軛io
複數的模。event
四、複數集存在於一個2D平面上,能夠認爲這個平面有2個軸:實軸、虛軸。class
四元數有3個虛部,i、j、k。
繞向量 n 旋轉 0 度的四元數:
q與-q表明的實際角位移是相同的,將0 加上360度,不會改變q的角位移,但q的四個份量都變負了。因此任意角位移有2種四元數的表示法。
四元數也有模。
五、四元數的共軛:
四元數的逆:
當 |q| 爲1時,四元數的共軛,就是四元數的逆。
單位四元數:[1, 0]
四元數逆意味着向相反的方向旋轉相同的角度。
六、四元數乘法。
四元數乘法知足結合律,不知足交換律。
四元數叉乘的模等於模的積:
四元數逆的性質:
七、四元數旋轉公式:
下例,先執行a旋轉,再執行b旋轉:
八、四元數點乘。結果是一個標量。
九、四元數的對數。引入變量 alpha = 0/2
指數公式爲:
9.一、四元數求冪。咱們看看它的數學定義。
結合9中的公式,上式能夠推導爲 exp(t[0 alpha*n]),也就是 q^t次方,實際上是 alpha 乘以了t。因此q^t其實是 [cos(t*alpha) n.sin(t*alpha)]。
下述代碼使用上述原理,計算四元數 q 的 t 次方的值。原理是讓角度 alpha * t。
上面的 if 是用於避免單位四元數[1 0]的狀況,單位四元數放大 t 倍,仍是單位四元數。
十、slerp 避免了歐拉角插值的全部問題。四元數插值的理論:
旋轉插值圖解:
由類似三角形原理,能夠求出 k0、k1。
因此 V(t) 能夠表示爲:
擴展到四元數即爲:
slerp 的完整代碼以下:
上述實現用了一個書上未證實的公式,四元數的點乘等於夾角的 cos。
十一、squard 是四元數的樣條插值。須要引入控制點:
能夠看到,Si的計算須要引用 qi-一、qi、qi+1。因此在計算轉變時,實際須要四個 q點。
樣條插值軌跡爲:
十二、從歐拉角到矩陣。
從慣性座標系到物體座標系很是容易,將3個軸軸的旋轉矩陣相乘便可。
而從物體座標系到慣性座標系,取上面矩陣的轉置矩陣便可。
1三、從矩陣到歐拉角
上面求解出了 pitch,也就推出了 cosp 的值。從而根據 m1三、m33 能夠推出 sinh、cosh 的的值,而後使用 atan2 便可計算出 h。
用一樣的方式,能夠用m2一、m22解得 bank。
若 cosp 爲0,則可推出 p 是+/- 90,b 爲0。從而可使用下面的值化簡公式:
經過 m十一、m31 可計算出h。
1四、實現從矩陣解出歐拉角的算法。
// 設矩陣保存在下面這些變量中 float m11, m12, m13; float m21,m22,m23; float m31,m32,m33; // 以弧度形式計算歐拉角並存在如下變量中 float h,p,b; // 從m23計算pitch, 當心 asin() 的域錯誤,因浮點計算咱們容許必定的偏差 float sp = -m23; if (sp <= -1.0f){ p = -1.570796f; // -pi/2 }else if (sp >= 1.0){ p = 1.570796; // pi/2 } else { p = asign(sp); } // 檢查萬象鎖的狀況,容許一些偏差 if (sp > 0.9999f){ // 向正上或正下看 // 將 bank 置零,賦值給 heading b = 0.0f; h = atan2(-m32, m11); } else { // 經過 m12 和 m33 計算heading h = atan2(m12, m33); b = atan2(m21, m22); }
1五、從四元數轉換到矩陣。
繞任意軸的旋轉矩陣:
繞任意軸旋轉的四元數:
咱們須要把每個m推導成爲 w,x,y,z 的形式,以m11爲例。
使用 cos 倍角公式:
最後展開化簡可得:
其餘m求法的就不舉例了,是相似的方法。下面是最終答案,從四元數構造出的完整旋轉矩陣:
1六、從矩陣轉換到四元數。
從直接利用公式 10.23,首先檢查對角線元素。
能夠用有相似的方法求得其餘三個元素:
由於平方根的結果老是正。另外一個技術是檢查對稱位置上的元素和。
首先用第一種方法計算出 w,x,y,z 其中一個的值,而後再用第二種方法,得出另外三個數值的值,便可避免全部元素均爲正的問題。
下面是算法實現:
// 輸入矩陣 float m11, m12, m13; float m21, m22, m23; float m31, m32, m33; // 輸出四元數 float w, x, y, z; // 探測 w, x, y, z 中的最大絕對值 float fourWSquaredMinus1 = m11 + m22 +m33; float fourXSquaredMinus1 = m11 - m22 - m33; float fourYSquaredMinus1 = m22-m11-m33; float fourZSquaredMinus1 = m33 - m11 -m33; int biggestIndex = 0; float fourBiggestSquaredMinus1 = fourWSquaredMinus1; if (fourXSquaredMinus1 > fourBiggestSquaredMinus1){ fourBiggestSquaredMinus1 = fourXSquaredMinus1; biggestIndex = 1; } if (fourYSquaredMinus1 > fourBiggestSquaredMinus1){ fourBiggestSquaredMinus1 = fourYSquaredMinus1; biggestIndex = 2; } if (fourZSquaredMinus1 > fourBiggestSquaredMinus1){ fourBiggestSquaredMinus1 = fourZSquaredMinus1; biggestIndex = 3; } // 計算平方根和除法 float biggestVal = sqrt(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; float mult = 0.25f / biggestVal; // 計算四元數的值 switch(biggestIndex){ case 0: w = biggestVal; x = (m23-m32)*mult; y = (m31-m13)*mult; z = (m12 - m21) * mult; break; case 1: x = biggestVal; w = (m23-m32)*mult; y = (m12+m21)*mult; z = (m31+m12)*mult; break; case 2: y = biggestVal; w = (m31 - m13) * mult; x = (m12 + m21) * mult; z = (m23 + m32) * mult; break; case 3: z = biggestVal; w = (m12 - m21) * mult; x = (m31 + m13) * mult; y = (m23 + m32) * mult; break; }
1七、從歐拉角轉換到四元數。
先將三個軸的旋轉分別轉換爲四元數,再將這三個四元數鏈接成一個四元數。下面是分別旋轉的四元數:
將其鏈接起來便可獲得結果。
1八、從四元轉換到歐拉角
咱們已經知道從四元數到矩陣,也知道從矩陣到歐拉角。下面是從矩陣求歐拉角:
再下面是四元數求矩陣:
將圖一中的 m 所有替換爲 wxyz,便可得四元數到歐拉角的推導公式。
// 使用全局變量保存輸入輸出 float w,x,y,z; float h,p,b; // 計算 si(pitch) float sp = -2.0f * (y*z + w*x); // 檢查萬向鎖,容許有必定偏差 if (fabs(sp)>0.9999f){ // 向正上或正下看 p = 1.570796f * sp; // 計算 heading, bank 置零 h = atan2(-x*z - w*y, 0.5f - y*y - z*z); b = 0.0f; }else{ // 計算角度 p = asin(sp); h = atan2(x*z - w*y, 0.5f - x*x - y*y); b = atan2(x*y - w*z, 0.5f - x*x, -z*z); }
1九、
20、