案例
- 使用GLTool下的glMakeTorus() + 固定光源著色器,創(chuàng)建一個(gè)3D圓環(huán)
/// 創(chuàng)建一個(gè)3D圓環(huán)
/// @param torusBatch 批次類(容器類)
/// @param majorRadius 主(外)半徑
/// @param minorRadius 次(內(nèi))半徑
/// @param numMajor 主單元細(xì)分?jǐn)?shù)量
/// @param numMinor 次單元細(xì)分?jǐn)?shù)量
void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)
//默認(rèn)光源著色器
GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
void SetupRC()
{
//1.設(shè)置背景顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
//2.初始化著色器管理器
shaderManager.InitializeStockShaders();
//3.將相機(jī)向后移動(dòng)7個(gè)單元:肉眼到物體之間的距離
viewFrame.MoveForward(7.0);
//4.創(chuàng)建一個(gè)3D圓環(huán)
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//5.點(diǎn)的大小(方便點(diǎn)填充時(shí),肉眼觀察)
glPointSize(4.0f);
}
//渲染場(chǎng)景
void RenderScene()
{
//1.清除窗口和深度緩沖區(qū)
//可以給學(xué)員演示一下不清空顏色/深度緩沖區(qū)時(shí).渲染會(huì)造成什么問(wèn)題. 殘留數(shù)據(jù)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2.把攝像機(jī)矩陣壓入模型矩陣中
modelViewMatix.PushMatrix(viewFrame);
//3.設(shè)置繪圖顏色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//4.使用默認(rèn)光源著色器
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.繪制
torusBatch.Draw();
//6.出棧 繪制完成恢復(fù)
modelViewMatix.PopMatrix();
//7.交換緩存區(qū)
glutSwapBuffers();
}
-
效果圖
00001.gif
問(wèn)題拋出
- 在使用默認(rèn)光源著色器后,渲染出來(lái)的畫(huà)面會(huì)有一個(gè)明暗度的效果,當(dāng)時(shí)當(dāng)我們旋轉(zhuǎn)視圖時(shí),明暗處理變的混亂,出現(xiàn)了大量的黑紅交錯(cuò)顯示;
- 視圖被穿透了,即當(dāng)物體旋轉(zhuǎn)到正面時(shí),可以透過(guò)正面看到后面;
解決方案
油畫(huà)算法
定義:先繪制場(chǎng)景中離觀察者較元的物體,再繪制較近的物體,比如:

弊端:使用油畫(huà)算法,只能按照物理距離觀察者的距離遠(yuǎn)近排序,或者只能是獨(dú)立的壓棧式繪制,當(dāng)多個(gè)圖層環(huán)環(huán)相扣時(shí),該算法就無(wú)法完成繪制了,比如:

不是單純的層級(jí)關(guān)系,就無(wú)法使用油畫(huà)算法來(lái)完成繪制了,那怎么辦吶?我們接著往下看。
正背面剔除
- 在現(xiàn)實(shí)生活中,我們?cè)谟^察一個(gè)物體時(shí),無(wú)論在任何角度都無(wú)法完全看到一個(gè)物體所有的面(或者全貌)。在計(jì)算機(jī)中的3D圖形中也是這樣的,既然看不到,我們就沒(méi)必要計(jì)算并渲染看不到的部分,而且如果通過(guò)某種方式去丟棄這部分渲染,OpenGL的渲染效率會(huì)大大提高。
-
OpenGL中的正面與背面
正面與背面
正面:按照逆時(shí)針頂點(diǎn)連接順序的三角形面
背面:按照順時(shí)針頂點(diǎn)連接順序的三角形面
在OpenGL中,可以通過(guò)檢測(cè)所有朝向觀察者的面,即正面,并渲染他們??床坏降拿妫幢趁?,丟棄且不進(jìn)行渲染。 - 通過(guò)分析頂點(diǎn)數(shù)據(jù)的順序,來(lái)告訴OpenGL哪個(gè)是正面,哪個(gè)是背面
在OpenGL中,提供了非常簡(jiǎn)單的API來(lái)開(kāi)啟頂點(diǎn)數(shù)據(jù)分析功能(表面剔除)
//開(kāi)啟表面剔除(默認(rèn)是剔除背面)
void glEnable(GL_CULL_FACE);
//關(guān)閉表面剔除
void glDisable(GL_CULL_FACE);
//指定要剔除的面
//mode參數(shù)為: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默認(rèn)GL_BACK
void glCullFace(GLenum mode);
//用戶自定義哪個(gè)面為正面
//mode參數(shù)為: GL_CW,GL_CCW,默認(rèn)值:GL_CCW
void glFrontFace(GLenum mode);
//例如,剔除正面實(shí)現(xiàn)(1)
glCullFace(GL_BACK);
glFrontFace(GL_CW);
//例如,剔除正?實(shí)現(xiàn)(2)
glCullFace(GL_FRONT);
開(kāi)啟正背面剔除后的效果:

這里我們通過(guò)開(kāi)啟正背面剔除,解決了物體在旋轉(zhuǎn)時(shí)的明暗交替問(wèn)題,但是在旋轉(zhuǎn)過(guò)程中發(fā)現(xiàn),在旋轉(zhuǎn)到重疊部分時(shí),發(fā)生了透視,這里引入下一個(gè)概念:深度。
深度
概念
深度:像素點(diǎn)在3D世界中距離觀察者(Camra視角)的距離,z值。
深度緩存區(qū):一塊內(nèi)存區(qū)域,專門(mén)存儲(chǔ)每個(gè)像素點(diǎn)的深度值,z值越大,距離觀察者越遠(yuǎn)(這里要考慮z軸的正/負(fù)方向,這里是負(fù)方向)。
意義:在不使用深度測(cè)試的時(shí)候,如果我們先繪制一個(gè)距離比較近的物體,再繪制距離較遠(yuǎn)的物體,此時(shí)距離遠(yuǎn)的位圖因?yàn)楹罄L制,會(huì)把距離近的物體覆蓋掉,有了深度緩沖區(qū)后,繪制物體的順序就不不那么重要了(此時(shí)由z值來(lái)控制先后), 實(shí)際上,只要存在深度緩沖區(qū),OpenGL 都會(huì)把像素的深度值寫(xiě)入到緩沖區(qū)中. 除非調(diào)?用 glDepthMask(GL_FALSE).來(lái)禁?寫(xiě)?。深度測(cè)試
深度緩沖區(qū)(DepthBuffer)和顏色緩存區(qū)(ColorBuffer)是一一對(duì)應(yīng)的。顏色緩存區(qū)存儲(chǔ)像素的顏色信息,而深度緩沖區(qū)存儲(chǔ)像素的深度信息。 在決定是否繪制一個(gè)物體表面時(shí),首先要將表面對(duì)應(yīng)的像素的深度值與當(dāng)前深度緩沖區(qū)中的值進(jìn)行比較, 如果大于深度緩沖區(qū)中的值,則丟棄這部分,否則利用這個(gè)像素對(duì)應(yīng)的深度值和顏色值,分別更新深度緩沖區(qū)和顏色緩存區(qū),這個(gè)過(guò)程稱為”深度測(cè)試”。使用深度測(cè)試
在OpenGL中同樣給提供了簡(jiǎn)單的API來(lái)開(kāi)啟和關(guān)閉深度測(cè)試,以及一些相關(guān)的自定義API(至于內(nèi)部如何實(shí)現(xiàn)我們并不需要關(guān)注)。
深度緩沖區(qū),一般由窗口管理系統(tǒng),GLFW創(chuàng)建.深度值一般由16位,24位,32位值表示. 通常是24位。數(shù)越高,深度精確度更好。
//開(kāi)啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
//在繪制場(chǎng)景下,清除顏色緩存區(qū)和深度緩存區(qū)
glClear(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度緩沖區(qū)默認(rèn)值為1.0,表示最大的深度值,深度值的范圍為(0,1)之間. 值越小表示越靠近觀察者,值越大表示越遠(yuǎn)離觀察者。
看下開(kāi)啟深度測(cè)試后的效果:

總結(jié)
針對(duì)上面的兩個(gè)問(wèn)題,我們最終其實(shí)就是用了2行代碼:
//開(kāi)始深度測(cè)試
glEnable(GL_DEPTH_TEST);
//開(kāi)啟背面剔除
glEnable(GL_CULL_FACE);
3D效果的呈現(xiàn),其實(shí)內(nèi)部也是通過(guò)復(fù)雜的計(jì)算轉(zhuǎn)換為了2D數(shù)據(jù),并渲染出一個(gè)讓人看起來(lái)像3D的效果;(比如透視投影的內(nèi)部處理)。
在OpenGL中我們并不需要寫(xiě)復(fù)雜的代碼來(lái)寫(xiě)對(duì)應(yīng)的邏輯,只需要通過(guò)OpenGL開(kāi)啟相關(guān)的繪制能力即可。

