經(jīng)過前面幾個頁面的總結(jié),我們知道了OpenGL渲染的基本知識?,F(xiàn)在,就讓我們來學(xué)習(xí)一個完整的OpenGL工程。學(xué)習(xí)其基本架構(gòu)吧
main
main函數(shù)是OpenGL工程的入口,其負(fù)責(zé)OpenGL的初始化,runloop的承接等等功能,這部分在大多數(shù)的OpenGL工程中是一致的??梢院唵蔚膹?fù)制粘貼:
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
//初始化GLUT
glutInit(&argc, argv);
/* glutInitDisplayMode 定義顯示方式
顏色模式:GLUT_RGBA或者GLUT_RGB。指定一個RGBA窗口,這是一個默認(rèn)的顏色模式。
緩沖區(qū): GLUT_SINGLE.單緩沖區(qū)窗口; GLUT_DOUBLE.雙緩沖區(qū)窗口
是否允許深度測試:GLUT_DEPTH
*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
// glutInitWindowSize 定義顯示窗口大小
glutInitWindowSize(800,600);
// glutCreateWindow 創(chuàng)建窗口
glutCreateWindow("OpenGL SphereWorld");
// 注冊函數(shù)指針,這個會在下面細(xì)講
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpeacialKeys);
//glewInit 初始化glew庫,用來抹平不同平臺openGL的差異
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
//用戶自定義的初始化
SetupRC();
//啟動mainloop
glutMainLoop();
return 0;
}
重要函數(shù)


數(shù)據(jù)結(jié)構(gòu)解析
OpenGL提供了一系列數(shù)據(jù)結(jié)構(gòu),下面讓我們依次解釋下面的數(shù)據(jù)類型和他們提供的函數(shù)。
1)管理類
1.1)GLShaderManager 著色器管理器
OpenGL在提交一個幾何圖形進(jìn)行渲染之前必須選定一個著色器。對于初學(xué)者,我們可以使用GLShaderManager來調(diào)用OpenGL提供的一些固定著色器。
GLShaderManager shaderManager; // 著色器管理器
//************************
// In SetupRC
shaderManager.InitializeStockShaders(); // 固定的初始化套路
//************************
//In RenderScene
shaderManager.UseStockShader( // 使用平面著色器,后面一般跟著draw方法
GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor);
shaderManager.UseStockShader( // 使用點光源著色器,注意這里不同的著色器后面參數(shù)不同
GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos,
vTorusColor);
1.2)幾何體容器 GLBatch
GLBatch用來管理要渲染的幾何體, 其有很多子類,最常用的還是三角形管理類GLTriangleBatch
GLTriangleBatch sphereBatch; //小球
GLBatch floorBatch; //地板
//************************
// In SetupRC
// 配置頂點
floorBatch.Begin(GL_LINES, 324); // 初始化管理器,GL_LINES表示線性圖元,324表示有324個頂點
for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {
floorBatch.Vertex3f(x, -0.55f, 20.0f); // 添加頂點數(shù)據(jù)
......
}
floorBatch.End();
gltMakeSphere(sphereBatch, 0.1f, 26, 13); 直接生成一個球形
//************************
// In RenderScene
//啟動繪制
floorBatch.draw();
sphereBatch.draw();
2) 矩陣相關(guān)
首先,我們要知道,在OpenGL的世界里,物體的位置是存儲為矩陣的,物體的移動,平移,旋轉(zhuǎn)等等操作是通過矩陣相乘實現(xiàn)的,此外,為了讓屏幕上的展示產(chǎn)生立體效果,需要通過透視投影進(jìn)行轉(zhuǎn)化,這同樣是通過透視矩陣 x 位置矩陣來實現(xiàn)的。
2.1)GLMatrixStack 矩陣堆棧
由于矩陣需要做多種操作,OpenGL使用GLMatrixStack矩陣堆棧來存儲矩陣。
GLMatrixStack modelViewMatrix; // 模型視圖矩陣棧
GLMatrixStack projectionMatrix; // 投影矩陣棧
//************************
// In RenderScene
//加入objectFrame
modelViewMatrix.PushMatrix(objectFrame);
//使得大球位置平移(3.0)向屏幕里面
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//8.壓棧(復(fù)制棧頂)
modelViewMatrix.PushMatrix();
//自轉(zhuǎn)
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
//配置GLShaderManager著色器
//GLBatch開始繪制
//結(jié)果出棧
GLMatrixStack的方法
作為一個堆棧,GLMatrixStack提供了最基本的 push pop 方法:
void PushMatrix(GLFrame& frame) //GLFrame轉(zhuǎn)成4*4矩陣入棧
void PushMatrix(const M3DMatrix44f mMatrix) //入棧一個4*4矩陣
void PushMatrix(void) //復(fù)制棧頂矩陣并再次入棧
void PopMatrix(void) //出棧
我們還可以看到 load方法,它用于替換棧頂矩陣
void LoadMatrix(GLFrame& frame) //GLFrame轉(zhuǎn)成4*4矩陣替換棧頂矩陣
void LoadMatrix(const M3DMatrix44f mMatrix) //替換棧頂矩陣
void LoadIdentity(void) //一個4*4單位矩陣替換棧頂矩陣
GLMatrixStack的引入就是為了方便計算,它提供了一系列的矩陣處理方法,要注意的是,下面這些操作默認(rèn)將操作結(jié)果替換棧頂矩陣而不是入棧:
第一個就是矩陣相乘MultMatrix
void MultMatrix(const M3DMatrix44f mMatrix) // 棧頂矩陣 x 入?yún)⒕仃噈Matrix,結(jié)果**替換棧頂矩陣**
然后就是依賴矩陣相乘的模型變化:
void Scale(GLfloat x, GLfloat y, GLfloat z) {} // 三向縮放,結(jié)果**替換棧頂矩陣**
void Translate(GLfloat x, GLfloat y, GLfloat z) {}// 三向平移,結(jié)果**替換棧頂矩陣**
void Rotate(GLfloat x, GLfloat y, GLfloat z) {}// 三向旋轉(zhuǎn),結(jié)果**替換棧頂矩陣**
Example:
LoadMatrix(A) stack = [A]
PushMatrix() stack = [A, A]
LoadMatrix(B) stack = [B, A]
PopMatrix() stack = [A]
LoadMatrix(A) stack = [A]
PushMatrix() stack = [A, A]
MultMatrix(B) stack = [AB, A] //
PopMatrix() stack = [A]
2.2)GLFrustum 視景體和投影矩陣
為了在2D屏幕上產(chǎn)生3D視覺效果,投影變換,或者投影矩陣是必不可少的。投影矩陣的生成是由GLFrustum負(fù)責(zé)的, 而結(jié)果會存在在一個GLMatrixStack projectionMatrix中 :
GLMatrixStack projectionMatrix; // 投影矩陣棧
GLFrustum viewFrustum; // 視景體
//************************
// In ChangeSize 視窗size變化是投影矩陣也會變化 (其實ChangeSize函數(shù)除了一句設(shè)置視口外,都是用來生成,更新和存儲投影矩陣)
...
//2.創(chuàng)建投影矩陣
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
//并將其加載到投影矩陣堆棧上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
...
//************************
// In RenderScene
// 投影矩陣應(yīng)用在著色器中
// transformPipeline.GetProjectionMatrix()
shaderManager.UseStockShader(
GLT_SHADER_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
GLFrustum中投影矩陣的生成
投影矩陣的生成方法如下:
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
->
void SetPerspective(float fFov, float fAspect, float fNear, float fFar)

fFov是視角,fAspect是要展示的視窗的寬高比,fNear是展示視窗的視距,fFar是遠(yuǎn)視窗的視距。
2.3)GLGeometryTransform 幾何圖形變換管道
GLGeometryTransform可以類比成一個存儲模型視圖矩陣棧和投影矩陣棧的容器。在GLShaderManager中,使用GLGeometryTransform來方便的獲取這兩個值。
GLMatrixStack modelViewMatrix; // 模型視圖矩陣棧
GLMatrixStack projectionMatrix; // 投影矩陣棧
GLGeometryTransform transformPipeline; // 幾何圖形變換管道
//************************
//In ChangeSize
....矩陣棧的初始化
//將兩個矩陣棧塞入管道
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//************************
// In RenderScene
//在GLShaderManager中使用這兩個矩陣棧
shaderManager.UseStockShader(
GLT_SHADER_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vSphereColor);
2.4)GLFrame 參照幀
一個視圖上可能同時存在很多要渲染的幾何體,如果我們想實現(xiàn)一個屏幕拉遠(yuǎn)的功能,對每一個幾何體進(jìn)行直接矩陣操作無疑是一個很浪費的行為。OpenGL可以為渲染角色提供參照幀,或者成為本地對象坐標(biāo)系。它既可以表示物體,也可以表示觀察者:
//角色幀
GLFrame cameraFrame;
//照相機(jī)角色幀
GLFrame objectFrame;
//**添加附加隨機(jī)球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
//************************
// In SetupRC
// 將物體坐標(biāo)前移5f(在z軸上移動,MoveForward值越大,物體視圖越小,表示距離觀察者越遠(yuǎn))
objectFrame.MoveForward(5.0f);
for (int i = 0; i < NUM_SPHERES; i++) {
//y軸不變,X,Z產(chǎn)生隨機(jī)值
GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
//在y方向,將球體設(shè)置為0.0的位置,這使得它們看起來是飄浮在眼睛的高度
//對spheres數(shù)組中的每一個頂點,設(shè)置frame的頂點數(shù)據(jù)
spheres[i].SetOrigin(x, 2.0f, z);
}
//************************
// In RenderScene
//加入objectFrame
modelViewMatrix.PushMatrix(objectFrame);
.....一系列矩陣變換
void SetupRC()
SetupRC用來初始化渲染配置,在程序運(yùn)行過程中只會執(zhí)行一次。
它主要負(fù)責(zé)GLShaderManager和GLBatch的初始化,頂點數(shù)據(jù)的生成,GLFrame的調(diào)整, 配置處理等操作。
void SetupRC()
{
//清除顏色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// GLShaderManager初始化
shaderManager.InitializeStockShaders();
//移動渲染物體的參考坐標(biāo)
objectFrame.MoveForward(15.0f);
//開啟深度測試
glEnable(GL_DEPTH_TEST);
//設(shè)置GLBatch中的頂點數(shù)據(jù)
floorBatch.Begin(GL_LINES, 320);
for(...) {
floorBatch.Vertex3f(x, -0.55f, 20.0f);
.....
}
floorBatch.End();
//使用固定類型直接配置頂點
gltMakeSphere(torusBatch, 0.4f, 40, 80);
}
void ChangeSize(int nWidth, int nHeight)
當(dāng)?shù)谝淮芜\(yùn)行,以及視窗大小發(fā)生變化時會調(diào)用,主要負(fù)責(zé)視口調(diào)整以及新的投影矩陣的生成
//屏幕更改大小或已初始化
void ChangeSize(int nWidth, int nHeight)
{
//1.設(shè)置視口
glViewport(0, 0, nWidth, nHeight);
//2.創(chuàng)建投影矩陣
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
//viewFrustum.GetProjectionMatrix() 獲取viewFrustum投影矩陣
//并將其加載到投影矩陣堆棧上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.設(shè)置變換管道以使用兩個矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
//初始化GLGeometryTransform 的實例transformPipeline.通過將它的內(nèi)部指針設(shè)置為模型視圖矩陣堆棧 和 投影矩陣堆棧實例,來完成初始化
//當(dāng)然這個操作也可以在SetupRC 函數(shù)中完成,但是在窗口大小改變時或者窗口創(chuàng)建時設(shè)置它們并沒有壞處。而且這樣可以一次性完成矩陣和管線的設(shè)置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void RenderScene(void)
開始繪制場景時調(diào)用,主要負(fù)責(zé)位置數(shù)據(jù)想渲染數(shù)據(jù)的轉(zhuǎn)換,對矩陣地操作主要集中在這個函數(shù)中。
//清除顏色緩存區(qū)和深度緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//配置顏色值
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f};
//配置光源
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
//加入objectFrame
modelViewMatrix.PushMatrix(objectFrame);
//使得大球位置平移(3.0)向屏幕里面
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//壓棧(復(fù)制棧頂)
modelViewMatrix.PushMatrix();
//大球自轉(zhuǎn)
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
//指定合適的著色器(點光源著色器)
shaderManager.UseStockShader(
GLT_SHADER_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vTorusColor);
//繪制大球
torusBatch.Draw();
//繪制完畢則Pop
modelViewMatrix.PopMatrix();
//執(zhí)行緩存區(qū)交換
glutSwapBuffers();
//完成繪制
glutPostRedisplay();