OpenGL/OpenGL ES 入門:基礎變換 - 初識向量/矩陣

系列推薦文章:
OpenGL/OpenGL ES入門:圖形API以及專業名詞解析
OpenGL/OpenGL ES入門:渲染流程以及固定存儲着色器
OpenGL/OpenGL ES入門:圖像渲染實現以及渲染問題
OpenGL/OpenGL ES入門:基礎變換 - 初識向量/矩陣
OpenGL/OpenGL ES入門:紋理初探 - 經常使用API解析
OpenGL/OpenGL ES入門: 紋理應用 - 紋理座標及案例解析(金字塔)
OpenGL/OpenGL ES入門: 頂點着色器與片元着色器(OpenGL過渡OpenGL ES)
OpenGL/OpenGL ES入門: GLKit以及API簡介
OpenGL/OpenGL ES入門: GLKit使用以及案例程序員

肯定對象位置和方向的能力對於任何3D圖形編程人員來講都是很是重要的,正如咱們將要看到的,圍繞着原點來描述對象的維度,再將對象變換到須要的位置其實是很是方便的。編程

向量

爲空間中(任意選擇)的一個點,以及空間中從座標系原點到這個點座標的一條帶箭頭的線段,這個帶箭頭的線段能夠視爲一個向量數組

向量

向量可以表明的第一個量就是方向,第二個量就是數量。 方向:好比X軸就是向量(1,0,0)。在X方向爲+1,而在Y方向和Z方向則爲0; 數量:一個向量的數量就是這個向量的長度。對於上面X軸的向量(1,0,0)來講,向量的長度爲1。咱們把長度爲1的向量稱爲單位向量bash

math3d庫有兩個數據類型,可以表示一個三維或四維向量:M3DVertor3f能夠表示一個三維向量(X,Y,Z),而M3DVertor4f則能夠表示一個四維向量(X,Y,Z,W),其中W爲縮放因子,通常狀況下爲1.函數

// 簡單的聲明
M3DVector3f vVector;
M3DVector4f vVertex = { 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之間,實際上就是這兩個向量之間夾角的餘弦值工具

向量的點乘

math3d庫中也包含一些有用的函數使用點乘操做。post

m3dDotProduct3函數來實際得到兩個向量之間點乘結果ui

// 兩個單位向量夾角的餘弦值 float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v);spa

m3dGetAngleBetweenVectors3函數返回夾角的弧度值 // 返回這個夾角的弧度值 float m3dGetAngleBetweenVectors3(const M3DVector3f u, const M3DVector3f v);設計

叉乘

兩個向量之間叉乘所得的結果是另一個向量,這個新向量與原來兩個向量定義的平面垂直。要進行叉乘,這兩個向量都沒必要爲單位向量。 與點乘還有一個不一樣之處是叉乘不符合交換定律即 V1 X V2 != V2 X V1.

向量的叉乘

math3d庫中也有一個函數m3dCrossProduct3對兩個向量進行叉乘並返回運算獲得的結果向量

void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v);

矩陣

它是一種功能很是強大的數學工具,大大簡化了求解變量之間有複雜關係的方程或方程組的過程。其中一個具備廣泛性的例子和圖形程序設計人員密切相關,就是座標變換。 例如,若是在空間中有一個點,由x,y和z座標定義,將它圍繞任意點沿任意方向選擇必定角度後,我門須要知道這個點如今的位置,就要用到矩陣。爲何呢?由於新的x座標不只與原來的x座標和其餘旋轉參數有關,還與原y和z座標值有關。這種變量與解之間的相關性就是矩陣最擅長解決的問題。

在咱們進行3D程序設計工做是,咱們將使用的幾乎所有是兩種維度的矩陣,即3 x 3 和 4 x 4矩陣。在math3d庫中,也有這兩種維度的矩陣數據類型。

typedef float M3DMatrix33f[9]; typedef float M3DMatrix44f[16];

理解變換

在咱們指定頂點和這些頂點出如今屏幕上之間的這段時間裏,可能會發生3中類型的幾何變換: 視圖變換、模型變換和投影變換

變換的術語概念

視覺座標

視覺座標是相對於觀察者的視角而言的,不管可能進行何種變換,咱們均可以將它們視爲「絕對的」屏幕座標。

視覺座標系
在左邊的座標系1中,視覺座標系是以場景的觀察者的角度(也就是垂直於顯示器的方向)。 在右邊的座標系2中,視覺座標系稍稍進行了選擇,這樣就能夠更好的觀察Z軸的位置關係。

從觀察者的角度來看,x軸和y軸的正方形分別指向右方和上方。z軸的正方形從原點指向使用者,而z軸的負方向則從觀察者只想屏幕內部。

視圖變換

視圖變換是應用到場景中的第一種變換。他用來肯定場景中的遊離位置。在默認狀況下,透視投影中的觀察點位於原點(0,0,0),並沿着z軸的負方向進行觀察(向顯示器內部看)。觀察點相對於視覺座標系進行移動,來提供特定的有利位置。當觀察點位於原點(0,0,0)時,就像在透視投影中同樣,繪製在z座標爲正的位置的對象則位於觀察者背後。 在正投影中,觀察者被認爲是在z軸正方向無窮遠的位置,可以看到視景體中的任何東西。 視圖變換容許咱們把觀察點放在所但願的任何位置,並容許在任何方向上觀察場景,肯定視圖變換就想在場景中放置照相機並讓它指向某個方向。

模型變換

下圖展現了3種最廣泛的模型變換:
平移: 對象沿着給定的軸進行移動
旋轉: 對象圍繞着一條座標軸進行旋轉
縮放: 對象的大小進行了指定數量的放大或縮小。縮放能夠是不均勻的,即不一樣維度能夠進行不一樣程度的縮放。

平移 旋轉 縮放

場景或對象的最終外觀可能在很大程度上取決於應用的模型變換順序。 以下圖,模型變換先旋轉後平移與先平移後旋轉,結果是不一樣的。

模型變換

模型視圖的二元性

實際上,視圖和模型變換按照它們內部效果和對場景的最終外觀來講是同樣的。將這二者分開是爲了咱們(程序員)方便。好比將對象向後移動和將參考座標系向前移動在視覺上沒有區別,以下圖:

模型視圖是指這兩種變換在變換管線中進行組合,成爲一個單獨的矩陣。即模型視圖矩陣。

投影變換

投影變換將在模型視圖變換以後應用到頂點上。這種投影實際上定義了視景體並建立了裁剪平面。 更具體的說,投影變換指定一個完成的場景(全部模型變換都已完成)是如何投影到屏幕上的最終圖像。

  • 在正投影中,全部多邊形都是精確的按照指定的相對大小來在屏幕上繪製的
  • 透視投影所顯示的場景與現實生活更加接近,透視投影的特色就是透視縮短,這種特性似的遠處的物體看起來比近處一樣大小的物體更小一些。

視口變換

當上述都完成後,就獲得一個場景的二維投影,它將被映射到屏幕上某處的窗口上。這種到物理窗口的映射是咱們最後要作的變換,即視口變換

這個過程圖形硬件會爲咱們作好,因此沒必要太操心這個過程。

模型視圖矩陣

單位矩陣

將一個向量乘以一個單位矩陣,就至關於用這個向量乘以1,不會發生任何變化。 單位矩陣中除了對角線上的一組元素爲1以外,其餘元素均爲0.

單元矩陣

能夠在OpenGL中這樣生成一個單位矩陣:

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 };
                
或者使用`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 };
複製代碼

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類 這個類的構造函數容許指定堆棧的最大深度,默認的堆棧深度爲64.
// 同時這個矩陣堆棧在初始化時,已經在堆棧中包含了單位矩陣。

GLMatrixStack::GLMatrixStack(int iStackDepth = 64);

// 在堆棧頂部載入一個單元矩陣
void GLMatrixStack::LoadIdentity(void);

// 在堆棧頂部載入任何矩陣
// 參數:4x4矩陣
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);

// 矩陣乘以矩陣堆棧頂部矩陣,相乘結果存儲到堆棧的頂部
void GLMatrixStack::MultMatrix(const M3DMatrix44f);

// 獲取矩陣堆棧頂部的值 GetMatrix 函數
// 爲了適應GLShaderManager的使用,或者獲取頂部矩陣的副本
const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
void GLMatrixStack::GetMatrix(M3Datrix44f mMatrix);

複製代碼

壓棧、出棧

一個矩陣的真正價值在於經過壓棧操做存儲一個狀態,而後經過出棧操做恢復這個狀態。 經過GLMatrixStack類,咱們可使用PushMatrix函數將矩陣壓入堆棧來存儲當前矩陣的值。而PopMatrix將移除頂部矩陣,並恢復它下面的值。

// 將當前矩陣壓入堆棧(棧頂矩陣copy一份到棧頂)
void GLMatrixStack::PushMatrix(void);

// 將M3DMatrix44f矩陣對象壓入當前矩陣堆棧
void PushMatrix(const M3DMatrix44f mMatrix);

// 將GLFrame對象壓入矩陣對象
void PushMatrix(GLFrame &frame);

// 出棧(出棧指的是移除頂部的矩陣對象)
void GLMatrixStack::PopMatrix(void);
複製代碼

具體過程參照下面流程圖

壓棧 出棧

仿射變換

GLMatrixStack類也內建了對建立旋轉、平移和縮放矩陣的支持。以下函數:

// 平移
void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z);

// 旋轉 參數angle是傳遞的度數, 而不是弧度
void MatrixStack::Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);

// 縮放
void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);
複製代碼
相關文章
相關標籤/搜索