向量、矩陣變換
©shuan9999
1. 矩陣
-
OpenGL在渲染的時候是通過模型視圖矩陣和投影矩陣運算得到最終顯示的座標。
模型矩陣(Model): 將頂點從局部座標系轉換到世界座標系中;
視圖矩陣(View): 將頂點從世界座標轉化到視圖座標系下;
投影矩陣(Projection): 將頂點從視圖座標系轉換到規範立方體中(即屏幕中);
模型視圖投影矩陣(MVP) = 投影矩陣 * 模型視圖矩陣 (不能寫成 模型視圖矩陣 * 投影矩陣,矩陣乘法不滿足交換律) -
爲了保證每一次渲染的獨立性,需要在每一次渲染前保存當前狀態(PushMatrix),並在渲染結束後恢復這個狀態(PopMatrix)。
-
單位矩陣
-
將一個向量乘以一個單位矩陣,就相當於用這個向量乘以1,不會發生任何變化。 單位矩陣中除了對角線上的一組元素爲1之外,其他元素均爲0。
-
可以通過如下方式生成一個單位矩陣:
1、
GLFloat m[] = { 1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f.0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f };
2、或者使用math3d
的M3DMatrix44f
類型:
M3DMatrix44f m = { 1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f.0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f };
3、在math3d
庫中,還有一個快捷函數m3dLoadIdentity44
,這個函數初始化一個單位矩陣。
void m3dLoadIdentity44(M3DMatrix44f m); -
平移
-
一個平移矩陣僅僅是將我們的頂點沿着3個座標軸重的一個或多個進行平移。
可以調用math3d庫中的m3dTranslationMatrix44函數來使用變換矩陣void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
-
旋轉
-
將一個對象沿着3個座標軸中的一個或任意向量進行旋轉,需要一個旋轉矩陣。
void m3dRotationMatrix44(M3DMatrix44f m, float x, float y, float z);
-
縮放
-
縮放矩陣可以沿着3個座標軸方向按照指定因子放大或縮小所有頂點,以改變對象的大小。縮放不一定是一致的,我們可以在不同的方向同時使用它來進行伸展和壓縮。
M3DMatrix44f m;
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale); -
綜合變換
-
爲了將對象移動道想要的位置,我們可能需要先將它平移到指定位置,然後在旋轉得到想要的結果。又因爲4 x 4變換矩陣包含一個位置和一個方向,那麼一個矩陣就可以完成這兩種轉換。只需將兩個矩陣相乘,結果得到的矩陣包含結合道一起的轉換,都在一個矩陣中。
在math3d
庫中,m3dMatrixMultiply44
用來將兩個矩陣相乘並返回運算結果。void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
-
在代碼中應用矩陣:
//初始化
GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
//在堆棧頂部載入一個單元矩陣
void GLMatrixStack::LoadIdentity(void);
//在堆棧頂部載入任何矩陣
//參數:4*4矩陣
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
//矩陣乘以矩陣堆棧頂部矩陣,相乘結果會自動存儲到棧頂
void GLMatrixStack::MultMatrix(const M3DMatrix44f);
//獲取矩陣堆棧頂部的值 GetMatrix 函數
//爲了適應GLShaderMananger的使用,或者獲取頂部矩陣的副本
const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix); -
壓棧出棧
//將當前矩陣壓⼊堆棧(棧頂矩陣copy 一份到棧頂)
void GLMatrixStack::PushMatrix(void);
//將M3DMatrix44f 矩陣對象壓入當前矩陣堆棧
void PushMatrix(const M3DMatrix44f mMatrix);
//將GLFame 對象壓入矩陣對象
void PushMatrix(GLFame &frame);
//出棧(出棧指的是移除頂部的矩陣對象)
void GLMatrixStack::PopMatrix(void); -
矩陣變換
//Rotate 函數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); -
GLMatrixStack::LoadIdentity(void)
、void GLMatrixStack::PushMatrix(void);
二者很像,都是往棧頂append一個節點,之所以要這樣去做就是爲了不破壞棧中的原始數據,當你操作完成後將push進來的直接pop掉就能恢復到初始狀態,也就是說push之後要記得pop,否則就是埋坑。 -
矩陣變換在3D環境中的典型渲染循環流暢
-
OpenGL 變換術語概況
-
視圖變換
-
視圖變換是應用到場景中的第一種變換。他用來確定場景中的遊離位置。在默認情況下,透視投影中的觀察點位於原點(0,0,0),並沿着z軸的負方向進行觀察(向顯示器內部看)。觀察點相對於視覺座標系進行移動,來提供特定的有利位置。當觀察點位於原點(0,0,0)時,就像在透視投影中一樣,繪製在z座標爲正的位置的對象則位於觀察者背後。
-
在正投影中,觀察者被認爲是在z軸正方向無窮遠的位置,能夠看到視景體中的任何東西。
視圖變換允許我們把觀察點放在所希望的任何位置,並允許在任何方向上觀察場景,確定視圖變換就想在場景中放置照相機並讓它指向某個方向 -
總之就是變換相機的位置
-
模型變換
-
下圖展示了3種最普遍的模型變換:
平移: 對象沿着給定的軸進行移動
旋轉: 對象圍繞着一條座標軸進行旋轉
縮放: 對象的大小進行了指定數量的放大或縮小。縮放可以是不均勻的,即不同維度可以進行不同程度的縮放。
-
物體最終外觀在很大程度上取決於應用的模型變換順序。 如下圖,模型變換先旋轉後平移與先平移後旋轉,結果是不同的。
-
投影變換
-
投影變換將在模型視圖變換之後應用到頂點上。這種投影實際上定義了視景體並創建了裁剪平面。 更具體的說,投影變換指定一個完成的場景(所有模型變換都已完成)是如何投影到屏幕上的最終圖像。
- 在正投影中,所有多邊形都是精確的按照指定的相對大小來在屏幕上繪製的
- 透視投影所顯示的場景與現實生活更加接近,透視投影的特點就是透視縮短,這種特性似的遠處的物體看起來比近處同樣大小的物體更小一些。
2. 向量
-
3個值(x、y、z)組合起來表示2個重要的值,方向和數量
-
math3d庫,有2個數據類型,能夠表示一個三維或者四維向量。
M3DVector3f
可以表示一個三 維向量(x,y,z),而M3DVector4f
則可以表示一個四維向量(x,y,z,w)。在典型情況下,w 座標設爲1.0。x,y,z值通過除以w,來進行縮放。而除以1.0則本質上不改變x,y,z值。typedef float M3DVector3f[3];
typedef float M3DVector4f[4];
//聲明一個三分量向量操作:
M3DVector3f vVector;
//類似,聲明一個四分量的操作:
M3DVector4f vVectro= {0.0f,0.0f,1.0f,1.0f};
//聲明一個三分量頂點數組,例如生成一個三⻆形
M3DVector3f vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f
}; -
點乘
-
兩個單位向量之間的點乘運算將得到一個標量(只有一個值),它表示兩個向量之間的夾角。要進行這種運算,這兩個向量必須爲單位向量,返回的結果將在-1~1之間,實際上就是這兩個向量之間夾角的餘弦值
//實現點乘方法:
//方法1:返回的是-1,1之間的值。它代表這個2個向量的餘弦值。
float m3dDotProduct3(const M3DVector3f u,const M3DVector3f v);
//方法2:返回2個向量之間的弧度值。
float m3dGetAngleBetweenVector3(const M3DVector3f u,const M3DVector3f v); -
叉乘
-
兩個向量之間叉乘所得的結果是另外一個向量,這個新向量與原來兩個向量定義的平面垂直。要進行叉乘,這兩個向量都不必爲單位向量。 與點乘還有一個不同之處是叉乘不符合交換定律即 V1 x V2 != V2 x V1。
void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v);