1. 向量
1.1 向量相關概念
- 向量:在 3D 笛卡爾坐標系中,一個由 x、y、z組成的頂點就是一個向量
- 單位向量:長度為 1 的向量
-
標準化向量:將長度不為 1 的向量縮放為 1 的過程。
(x/|xyz|, y/|xyz|, z/|xyz|)
使?一個非零向量除以它的模(向量的?度), 就可以得到?向相同的單位向量;
1.2 OpenGL 如何定義向量
- math3d庫,有2個數據類型,能夠表示一個三維或者四維向量。
typedef float M3DVector3f[3]; // 表示一個三維向量(x,y,z)
typedef float M3DVector4f[4]; // 表示一個四維向量(x,y,z,w). - 在典型情況下,w為縮放因子,坐標設為1.0。x,y,z值通過除以w,來進?行縮放。而除以1.0則本質上不改變x,y,z值。
1.3 向量運算
-
點乘
兩個單位向量點乘會得到一個標量,是一個[-1,1]范圍的值,這個標量表示的是兩個向量的夾角(也就是cos余弦值)。
// 獲取兩個向量的夾角
float m3dDotProduct3(const M3DVector3f u,const M3DVector3f v);
// 獲取兩個向量的夾角的弧度
float m3dGetAngleBetweenVector3(const M3DVector3f u,const
M3DVector3f v);
-
叉乘
2個向量(不必為單位向量)之間叉乘就可以得到另外?個新的向量,它會與原來2個向量定義的平面垂直(也叫法線)。向量叉乘不滿足交換律。
// 獲取兩個向量的叉乘結果(法線)
void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u ,const
M3DVector3f v);
2. 矩陣(Matrix)
2.1 矩陣的定義方式
typedef float M3DMatrix33f[9]; // 三維矩陣的聲明
typedef float M3DMatrix44f[16]; // 四維矩陣的聲明
- 單元矩陣:一個矩陣叉乘單元矩陣后,結果還是原來的矩陣,不會發(fā)生改變。
// 初始化一個單元矩陣
void m3dLoadIdentity44f(M3DMatrix44f m);
矩陣可以只有一行或者一列。這種矩陣也可以叫做向量。
OpenGL的約定?,更多傾向使?一維數組; 這樣做的原因是: OpenGL 使用的是 Column-Major(以列為主)矩陣排序的約定(在數學中叫做轉置矩陣)。
A0 A1 A2 A3
A4 A5 A6 A7
A8 A9 A10 A11
A12 A13 A14 A15
??從左至右、從上到下的讀取方式為行優(yōu)先矩陣排序
A0 A4 A8 A12
A1 A5 A9 A13
A2 A6 A10 A14
A3 A7 A11 A15
??從上到下、從左至右的讀取方式為列優(yōu)先矩陣排序列矩陣的最后?行都為0,只有最后?個元素為1。
xx xx xx xx
xx xx xx xx
xx xx xx xx
0 0 0 1
2.2 為什么使用矩陣?
一個點(x,y,z)在坐標系中旋轉后,我們想得到新的坐標,就需要使用到矩陣。因為新坐標的 x 值,不僅與舊坐標的x值及旋轉參數有關系,甚至還和 y 值及 z 值有關系。一個物體的所有頂點都乘以矩陣,就能讓整個物體變換到空間中給定的位置和方向。
2.3 使用矩陣的步驟
在坐標系文章中,曾介紹過坐標系變換的步驟如下圖所示:

從邏輯上,在坐標系的變換過程中,依次使用了模型矩陣、視圖矩陣、投影矩陣。不過在OpenGL計算變換后的頂點向量時,其計算順序是相反的。 計算公式如下所示:
變換頂點向量 = M_pro * M_view * M_model * V_local
變換頂點向量 = 投影矩陣 ? 視圖變換矩陣 ? 模型矩陣 ? 頂點
2.3.1. 視圖變換:設置觀察者的位置,以確定觀察者坐標系。任何模型變換之前,都要先進行視圖變換。因為一旦觀察者坐標系改變后,所以的物體變換都要基于新的坐標系進行。
默認情況下,透視投影中,觀察者處于原點(0,0,0),并沿著Z軸負方向看過去(看向顯示器內部)。
2.3.2. 模型變換:物體進行平移、旋轉、縮放
// 平移
void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
// 旋轉
m3dRotationMatrix44(m3dDegToRad(45.0), float x, float y, float z);
// 縮放 (x/y/z參數傳值-1時,可以實現物體圍繞某一個軸的翻轉)
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
// 綜合變換, a 為先變換的矩陣、b 為后變換的矩陣
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
注:由于矩陣叉乘不符合交換律,矩陣交換后,矩陣相乘的結果不一樣。所以物體有多種變換時,變換順序不可交換。
例如“先平移后旋轉”與“先旋轉后平移”的效果是不一致的。如下圖所示:



2.3.3. 投影變換:設置投影方式
// 平截頭體設置透視投影
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
// 平截頭體設置正投影
void SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax)
3. 矩陣堆棧(GLMatrixStack)
- 使用矩陣堆棧,在一個視圖變換前,將其壓棧,變換后出棧。以達到不影響其他視圖的目的。
- 矩陣堆棧的深度為64,默認棧頂有一個單元矩陣。
- API 如下:
///---- 矩陣加載、相乘、獲取
//在堆棧頂部載?一個單元矩陣
void GLMatrixStack::LoadIdentity(void);
//在堆棧頂部載?任何矩陣 //參數:4*4矩陣
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
//矩陣乘以矩陣堆棧頂部矩陣,相乘結果存儲到堆棧的頂部
void GLMatrixStack::MultMatrix(const M3DMatrix44f);
//獲取矩陣堆棧頂部的值
void GLMatrixStack::GetMatrix(void);
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);
///---- 矩陣壓棧、出棧
//將當前矩陣壓?入堆棧(棧頂矩陣copy ?份到棧頂)
void GLMatrixStack::PushMatrix(void);
//將 M3DMatrix44f 矩陣對象壓入當前矩陣堆棧
void GLMatrixStack::PushMatrix(const M3DMatrix44f mMatrix);
//將GLFame 對象壓?入矩陣對象
void GLMatrixStack::PushMatrix(GLFame &frame);
//出棧(出棧指的是移除頂部的矩陣對象)
void GLMatrixStack::PopMatrix(void);
//將堆棧的頂部壓?GLFrame角色幀
void GLMatrixStack::LoadMatrix(GLFrame &frame);
//GLFrame乘以矩陣堆棧頂部的矩陣。相乘結果存儲在堆棧的頂部
void GLMatrixStack::MultMatrix(GLFrame &frame);
//將當前的GLFrame壓棧
void GLMatrixStack::PushMatrix(GLFrame &frame);
///---- 矩陣仿射變換(一般使用模型變換,很少直接通過堆棧進行放射變換)
// 旋轉 angle是度數,?不是弧度
void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
// 平移
void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z);
// 縮放
void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);

3.1 GLFrame 角色幀類
-
可以表示物體或觀察者所處的位置,主要有三個參數
vOrigin:當前所處的位置,默認是(0,0,0),處于原點
vForward:即將要去的位置,默認是(0,0,-1),朝向-z軸方向
vUp:朝向哪,默認是(0,1,0),朝向+y軸方向 - 通過 "旋轉坐標系/平移" 來改變觀察者/物體的位置
///---- 移動位置
// Move Forward (along Z axis)
inline void MoveForward(float fDelta);
// Move along Y axis
inline void MoveUp(float fDelta)
// Move along X axis
inline void MoveRight(float fDelta)
///---- 旋轉
// Rotate in world coordinates...
void RotateWorld(float fAngle, float x, float y, float z)
// Rotate around a local axis
void RotateLocal(float fAngle, float x, float y, float z)