十、OpenGL中的向量、矩陣、矩陣堆棧

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 使用矩陣的步驟

坐標系文章中,曾介紹過坐標系變換的步驟如下圖所示:

坐標系變換的步驟.png

從邏輯上,在坐標系的變換過程中,依次使用了模型矩陣、視圖矩陣、投影矩陣。不過在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);

注:由于矩陣叉乘不符合交換律,矩陣交換后,矩陣相乘的結果不一樣。所以物體有多種變換時,變換順序不可交換。
例如“先平移后旋轉”與“先旋轉后平移”的效果是不一致的。如下圖所示:


“先平移后旋轉”與“先旋轉后平移”的效果不一致.png

先平移再旋轉.gif
先旋轉再平移.gif

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);
矩陣入棧、相乘、出棧.png

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) 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容