OpenGL 六 - 3D數學基礎 - 向量、矩陣及OpenGL中的變換
關於矩陣和向量的相關知識,大家可能和我一樣畢業後幾乎就慢慢遺忘乾淨了。但是,既然學過,回憶起來其實並不太難。而且,即使沒有學過,也並不影響我們對相關API的使用。當然基礎知識的理解會幫助我們弄明白和更好的進行OpenGL的開發工作。
GLTools庫中的Math3d,其中包含了大量的OpenGL 3D數學的資料型別,矩陣、向量的計算等等 API。
一、向量
1、何為向量
3D笛卡爾座標系中,一個頂點就是 XYZ 座標空間上的一個點的位置。XYZ 就是一個向量,數學思維中一個頂點就是一個向量。
向量長度為1的稱為單位向量。向量長度 = 2√(x2+ y2+ z2) 即:向量的模。
把一個非單位向量縮放到 1 的過程,稱為標準化。也叫做單位化向量。 -- 保留向量的方向
1.1、宣告向量
M3DVector3f() -- 3維向量 (x,y,z)
M3DVector4f() -- 4維向量 (x,y,z,w) -- w: 縮放值,不縮放為:1
// 三維向量/四維向量的宣告
typedef float M3DVector3f[3];
typedef float M3DVector4f[4];
// 宣告一個三維向量
M3DVector3f vVector;
// 宣告一個四維向量並初始化一個四維向量
M3DVector4f vVertex = {0,0,1,1};
// 宣告一個三分量頂點陣列,例如生成一個三角形
M3DVector3f vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f
}
2、向量的點乘(dot product)
兩個向量之間可以進行加、減計算,使用率較高的是點乘,點乘只發生在兩個向量之間。
2個單位向量 點乘 得到 一個標量 -->標量:沒有方向,只有數值 -->一個 [-1,1] 的值,這個值其實就是兩個向量間夾⻆的 cos 值 -- 餘弦值。
如何求2個普通向量的夾角呢?單位化向量 --> x,y,z 分別 ➗ 向量的模(x/|xyz|, y/|xyz|, z/|xyz|)--> 與此普通向量方向相同的單位向量。
2.1、向量的點乘
// math3d 庫中提供了了關於點乘的API
// 1.m3dDotProduct3 函式獲得2個向量之間的點乘結果
float m3dDotProduct3(const M3DVector3f u,const M3DVector3f v);
// 2.m3dGetAngleBetweenVector3 可獲取2個向量之間夾角的弧度值
float m3dGetAngleBetweenVector3(const M3DVector3f u,const M3DVector3f v)
3、向量的叉乘(cross product)
2個向量v1、v2叉乘可得到一個向量v3,向量v3垂直於向量v1、v2 --> 法線 --> 遊戲場景中
叉乘不滿足交換律,因為向量是有方向的,交換叉乘方向會不同。
3.1、向量的叉乘
math3d 庫中提供了關於叉乘的API // 1.m3dCrossProduct3 函式獲得2個向量之間的叉乘結果 --> 一個新的向量 void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u ,const M3DVector3f v);
二、矩陣(Matrix)
為什麼使用矩陣?
我們對一個物體進行移動旋轉等操作時,物體的每個點都需要進行相應的移動旋轉,簡單的圖形的平移可以對每個點進行x,y,z的計算,但是當物體移動很複雜,頂點又很多時,我們對每個點進行計算操作再賦值的工作量很大且易出錯,更何況有旋轉時我們不一定精確知道角度,此時,矩陣就解決了我們的這個問題。
矩陣一行 or 一列都是合理的,其也可以稱為向量。
1、矩陣的宣告
// 三維矩陣/四維矩陣的宣告
typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];
在其他程式設計標準中, 許多矩陣庫定義一個矩陣時使⽤的是二維陣列;
OpenGL 的約定裡,更多傾向使⽤一維陣列,這樣做的原因是: OpenGL 使⽤的是 Column-Major (以列為主)矩陣排序的約定。 --> 列矩陣 == 轉置矩陣(數學中的)
列優先矩陣:
下圖,這 16 個值表示空間中一個特定的位置,這4列中,每⼀列都是有4個元素組成的向量;
如果無們要轉動一個一個物體,那麼將此物體所有的頂點向量 每一個頂點依次乘以一個矩陣,讓此物體所有的頂點都應用 相同的變換,從而得到物體轉動後 所在的空間中的 位置和⽅向。
列向量的特別標註:矩陣的最後⼀行都為0,最後⼀個元素為0。
單元矩陣:
// 單元矩陣初始化 ⽅式1 GLFloat m[] = { 1,0,0,0, //X Column 0,1,0,0, //Y Column 0,0,1,0, //Z Column 0,0,0,1 // Translation } // 單元矩陣初始化 方式 2 M3DMatrix44f m = { 1,0,0,0, //X Column 0,1,0,0, //Y Column 0,0,1,0, //Z Column 0,0,0,1 // Translation }
// 單元矩陣初始化 方式3
void m3dLoadIdentity44f(M3DMatrix44f m);
2、矩陣的乘法
線性代數角度計算:線性代數中,為便於書寫,座標的 計算順序都是從左到右的方式進行的。
變換後頂點向量 = V_local * M_model * M_view * M_pro
頂點 * 模型矩陣 * 檢視矩陣 * 投影矩陣
從OpenGL的角度:因為OpenGL中的約定是列優先矩陣,所以計算如下:
變換後頂點向量 = M_pro* M_view * M_model *V_local
投影矩陣 * 檢視變換矩陣 * 模型矩陣 * 頂點
矩陣的計算規則:左乘:代數中,用到兩個矩陣相乘的時候,矩陣A×矩陣B,那麼就稱為 A左乘以B。
三、OpenGL中的變換
1、檢視變換
2個視角的 視覺座標系
檢視變換是應⽤到場景中的第⼀種變換, 它⽤來確定場景中的有利位置,在預設情況下, 透視投影中 位於原點(0,0,0),並沿著 z 軸負⽅向進⾏觀察 (向顯示器內部”看過去”)。當觀察者點 位於原點(0,0,0) 時,就像在透視投影中⼀樣。
檢視變換將觀察者放在你希望的任何位置,並允許在任何⽅向上觀察場景, 確定檢視變換就像 在場景中放置觀察者並讓它指向某⼀個⽅向;
從⼤局上考慮, 在應⽤任何其他模型變換之前, 必須先應⽤檢視變換。這樣做是因為, 對於視覺座標系⽽言, 檢視變換移動了當前的⼯作的座標系; 後續的變化都會基於新調整的座標系進⾏。 -- 《OpenGL超級寶典》
2、模型變換
⽤於操縱模型與其中某特定變換。這些變換通過 旋轉,縮放,平移 將物件移動到需要的位置。模型變換的平移旋轉縮放的不同順序結果是不同的。
1.1 平移
void m3dTranslationMatrix44(M3DMatrix44f m, floata x, float y, float z);//M3DMatrix44f m 平移的結果
1.2 旋轉
m3dRotationMatrix44(m3dDegToRad(45.0), floata x, float y, float z);// 有返回值
1.3 縮放
void m3dScaleMatrix44(M3DMatrix44f m, floata xScale, float yScale, float zScale);//M3DMatrix44f m 縮放的結果
1.4 綜合變換 -- 既縮放也平移
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b); //M3DMatrix44f product放結果;const M3DMatrix44f a、b ,順序不定,根據需求確定是 先平移還是先縮放。
3、投影變換