
學(xué)習(xí)書(shū)籍: OpenGL 超級(jí)寶典(中文第五版) 密碼:fu4w
書(shū)籍源碼:OpenGL 超級(jí)寶典第五版源代碼 密碼:oyb4
環(huán)境搭建:OpenGL 學(xué)習(xí) 01 - Mac 搭建 OpenGL 環(huán)境
PS:因?yàn)橐院?Demo 源碼代碼量會(huì)越來(lái)越多,不太可能全部復(fù)制在這里了,我就解釋下 Demo 的核心源碼,全部源碼請(qǐng)前往我的 github/OpenGLDemo 下載,源碼里會(huì)有詳細(xì)注釋。
基本概念
一、向量
一個(gè)頂點(diǎn)是 XYZ 空間坐標(biāo)系的一個(gè)位置(x,y,z),這一個(gè)坐標(biāo)也能表示一個(gè)向量,是從坐標(biāo)原點(diǎn)指向這個(gè)位置點(diǎn)的一個(gè)向量(帶箭頭的線段)。
在 OpenGL 里,對(duì)應(yīng)數(shù)據(jù)類(lèi)型為 M3DVector3f(3維浮點(diǎn)向量)、M3DVector4f(4維浮點(diǎn)向量)


向量之間計(jì)算的幾何意義看下圖:

二、矩陣
矩陣是一種功能非常強(qiáng)大的數(shù)學(xué)工具,大大簡(jiǎn)化了求解變量之間有復(fù)雜關(guān)系的方程或方程組的過(guò)程。
在 OpenGL 里,主要運(yùn)用于坐標(biāo)變換,對(duì)應(yīng)數(shù)據(jù)類(lèi)型有M3DMatrix33f(3x3浮點(diǎn)矩陣)、M3DMatrix44f(4x4浮點(diǎn)矩陣)等。



注意:矩陣的乘法是不滿(mǎn)足交換律的,即 AB != BA

三、變換

1. 視圖變換
視圖變換是應(yīng)用到場(chǎng)景的第一種變換,用于確定場(chǎng)景中的有利位置,即觀察者的位置,就像在場(chǎng)景中放置照相機(jī)并讓它指向某個(gè)方向。

2. 模型變換
物體本身的坐標(biāo)變換,有旋轉(zhuǎn)、平移、縮放3種基礎(chǔ)模型變換。

3. 模型視圖變換
實(shí)際上就是視圖變換和模型變換的結(jié)合,因?yàn)橐晥D變換和模型變換實(shí)質(zhì)上是一樣的,只是為了程序員方便而區(qū)分出來(lái),因?yàn)槟阋苿?dòng)物體向前10m,和觀察者向后移動(dòng)10m,最終效果是一樣的。
4. 投影變換
投影變換有正投影和透視投影:
- 正投影(平行投影):無(wú)論物體有多遠(yuǎn),都會(huì)按照同樣大小進(jìn)行繪制。
- 透視投影:遠(yuǎn)處的物體看起來(lái)會(huì)比近處同樣大小的物體更小一些。

5. 視口變換
將邏輯窗口坐標(biāo)映射到物理窗口坐標(biāo)的變換,稱(chēng)為視口變換,通常我們不需要為這事操心,圖形硬件已經(jīng)為我們做好了這些。


源碼解析
一、矩陣變換
// 加載單位矩陣
void m3dLoadIdentity44(M3DMatrix44f m);
// 沿著 x/y/z 軸平移
void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
// 沿著 x/y/z 軸旋轉(zhuǎn) angle(弧度)
void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
// 在 x/y/z 軸上進(jìn)行縮放
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
// 矩陣 a 和矩陣 b 相乘得到矩陣 product
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
08-MoveByMatrix 核心源碼如下,全部源碼下載:08-MoveByMatrix
// 創(chuàng)建3個(gè)4x4矩陣,分別是合成矩陣、平移矩陣、旋轉(zhuǎn)矩陣
M3DMatrix44f mFinalTransform, mTranslationMatrix, mRotationMatrix;
// 平移(xPos, yPos, 0)的矩陣表示
m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f);
// 繞z軸旋轉(zhuǎn)的矩陣,每次旋轉(zhuǎn)角度加 5 度,m3dDegToRad = 角度 -> 弧度
static float zRot = 0.0f;
zRot += 5.0f;
m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(zRot), 0.0f, 0.0f, 1.0f);
// 矩陣相乘,參數(shù)順序很重要,先平移,后旋轉(zhuǎn)
m3dMatrixMultiply44(mFinalTransform, mTranslationMatrix, mRotationMatrix);

二、投影矩陣
設(shè)置投影矩陣是通過(guò)視景體(GLFrustum)來(lái)設(shè)置的
[GLFrustum] { // 僅僅表示以下方法是 GLFrustum 的方法
// 設(shè)置正投影矩陣參數(shù),(x, y, z)最小和最大值
void SetOrthographic(GLfloat xMin, GLfloat xMax,
GLfloat yMin, GLfloat yMax,
GLfloat zMin, GLfloat zMax);
// 設(shè)置透視投影矩陣參數(shù),分別為:透視角,寬高比,近距,遠(yuǎn)距
void SetPerspective(float fFov, float fAspect, float fNear, float fFar);
// 獲取投影矩陣
const M3DMatrix44f& GetProjectionMatrix(void);
}
為了方便管理各個(gè)矩陣,GLTools 提供了矩陣堆棧 GLMatrixStack,默認(rèn)堆棧最大深度為 64,有壓棧、出棧等操作。
[GLMatrixStack] { // 僅僅表示以下方法是 GLMatrixStack 的方法
// -------- 矩陣加載 -----------
// 在棧頂載入單元矩陣,載入即覆蓋
void LoadIdentity(void);
// 在棧頂載入任何矩陣
void LoadMatrix(const M3DMatrix44f m);
// 用棧頂矩陣乘以某個(gè)矩陣,得到的結(jié)果矩陣覆蓋原來(lái)的棧頂矩陣
void MultMatrix(const M3DMatrix44f mMatrix);
// 獲取棧頂矩陣
const M3DMatrix44f& GetMatrix(void);
// -------- 出棧壓棧 -----------
// 壓棧,在棧頂壓入一個(gè)矩陣
void PushMatrix(const M3DMatrix44f mMatrix);
// 出棧,把棧頂矩陣移除矩陣堆棧
void PopMatrix(void);
// -------- 仿射變換 -----------
// 棧頂矩陣進(jìn)行縮放變換
void Scale(GLfloat x, GLfloat y, GLfloat z);
// 棧頂矩陣進(jìn)行平移變換
void Translate(GLfloat x, GLfloat y, GLfloat z);
// 棧頂矩陣進(jìn)行旋轉(zhuǎn)變換
void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
}
而為了方便管理模型視圖矩陣堆棧和投影矩陣堆棧,GLTools 為我們提供了管理管線 GLGeometryTransform,幫助我們跟蹤記錄這兩種矩陣堆棧,并快速檢索模型視圖矩陣堆棧頂部或投影矩陣堆棧頂部。
[GLGeometryTransform] { // 僅僅表示以下方法是 GLGeometryTransform 的方法
// ----------- 設(shè)置矩陣堆棧 ---------
// 單獨(dú)設(shè)置模型視圖矩陣堆棧
void SetModelViewMatrixStack(GLMatrixStack& mModelView);
// 單獨(dú)設(shè)置投影矩陣堆棧
void SetProjectionMatrixStack(GLMatrixStack& mProjection);
// 同時(shí)設(shè)置模型視圖矩陣堆棧和投影矩陣堆棧
void SetMatrixStacks(GLMatrixStack& mModelView, GLMatrixStack& mProjection);
// ----------- 獲取堆棧頂部 ---------
// 獲取模型視圖矩陣堆棧的棧頂矩陣
const M3DMatrix44f& GetModelViewMatrix(void);
// 獲取投影矩陣堆棧的棧頂矩陣
const M3DMatrix44f& GetProjectionMatrix(void);
// 獲取2個(gè)矩陣堆棧的棧頂矩陣相乘的結(jié)果矩陣
const M3DMatrix44f& GetModelViewProjectionMatrix(void);
}
10-Orthographic 核心源碼如下,全部源碼下載:10-Orthographic
// 設(shè)置正投影參數(shù),(xMin, xMax, yMin, yMax, zMin, zMax)
viewFrustum.SetOrthographic(-130.0f, 130.0f, -130.0f, 130.0f, -130.0f, 130.0f);
// 獲得到的正投影矩陣載入堆棧中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 變換管線,管理2個(gè)堆棧
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);

11-Perspective 核心源碼如下,全部源碼下載:11-Perspective
// 設(shè)置透視投影矩陣參數(shù),分別為:透視角,寬高比,近距,遠(yuǎn)距
viewFrustum.SetPerspective(35.0f, float(width)/float(height), 1.0f, 1000.0f);
// 載入透視投影矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 利用變換管線,管理2個(gè)堆棧
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);

12-ModelViewProjection 核心源碼如下,全部源碼下載:12-ModelViewProjection
// 窗口渲染回調(diào)
void RenderScene(void) {
// 定義一個(gè)測(cè)試運(yùn)行時(shí)間
static CStopWatch rotTimer;
// 獲取到上一個(gè)時(shí)間點(diǎn)到當(dāng)前的時(shí)間間隔(單位是每秒刻度數(shù),即 1/60s)
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
// 清空緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 定義矩陣
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
// 向z軸平移的矩陣變換
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
// 繞y軸旋轉(zhuǎn)的矩陣變換
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
// 矩陣變換的合并矩陣
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 投影矩陣 + 視圖變換矩陣 = 最終物體位置坐標(biāo)
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelview);
// 繪制花托
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
torusBatch.Draw();
// 因?yàn)槭请p緩沖區(qū)模式,后臺(tái)緩沖區(qū)替換到前臺(tái)緩存區(qū)進(jìn)行顯示
glutSwapBuffers();
// 自動(dòng)觸發(fā)下次渲染回調(diào),達(dá)到動(dòng)畫(huà)的效果
glutPostRedisplay();
}

上面的 Demo 源碼全部都放在我的 github/OpenGLDemo 上,大家可以去下載和調(diào)試。
有什么問(wèn)題可以在下方評(píng)論區(qū)提出,寫(xiě)得不好可以提出你的意見(jiàn),我會(huì)合理采納的,O(∩_∩)O哈哈~,求關(guān)注求贊