渲染中可能會(huì)出現(xiàn)的問(wèn)題(不希望出現(xiàn)的幾何圖形)
默認(rèn)情況下,我們所渲染的每個(gè)點(diǎn)、線或三角形都會(huì)在[圖片上傳中...(opengl1.png-b3c9f6-1625020212941-0)]
屏幕上進(jìn)行光柵化,并按照在組合圖元批次時(shí)指定的順序排列,這在某些情況下會(huì)產(chǎn)生問(wèn)題。
如果我們繪制一個(gè)由很多個(gè)三角形組成的實(shí)體對(duì)象,那么第一個(gè)繪制的三角形可能會(huì)被后面繪制的三角形覆蓋。如下圖這個(gè)像游泳圈似的模型,其中一些三角形在游泳圈的背面,另一些在正面,正常我們應(yīng)該是看不到背面的(不考慮透明幾何體的特殊情況)。這樣的話,三角形繪制的順序可能會(huì)一團(tuán)糟,就變成了下圖的樣子:

解決方法
1.油畫(huà)法(painters algorithm):
對(duì)這些三角形排序,先渲染較遠(yuǎn)的三角形,再在它們上方渲染較近的三角形。但這種方法在圖形處理中效率很低,必須在任何發(fā)生重疊的地方對(duì)每個(gè)像素進(jìn)行兩次寫(xiě)操作,速度會(huì)變慢。并且對(duì)獨(dú)立的三角形排序的開(kāi)銷會(huì)過(guò)高。所以一般不推薦使用。
2. 正面&背面剔除:
對(duì)正面和背面三角形進(jìn)行區(qū)分的原因之一就是為了進(jìn)行剔除。背面剔除能極大提高性能,避免上圖出現(xiàn)的問(wèn)題。它很高效,在渲染的圖元裝配階段就整體拋棄了一些三角形。
開(kāi)啟背面剔除:
glEnable(GL_CULL_FACE);
關(guān)閉背面剔除:
glDisable(GL_CULL_FACE);
請(qǐng)注意,我們并沒(méi)有知名剔除的是正面還是背面。這是由另外一個(gè)函數(shù) glCullFace 控制的。
void glCullFace(GLenum mode);
mode 參數(shù)的可用值為 GL_FRONT、GL_BACK 或 GL_FRONT_AND_BACK。這樣要消除不透明物體的內(nèi)部幾何圖形就需要兩行代碼:
void glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
在某些情況下,剔除實(shí)體幾何體的正面也很有必要,比如要顯示圖形內(nèi)部渲染的時(shí)候。在渲染透明對(duì)象時(shí)(下面馬上就會(huì)講到混合),我們經(jīng)常會(huì)對(duì)一個(gè)對(duì)象進(jìn)行兩次渲染,第一次會(huì)開(kāi)啟透明并剔除正面,第二次則消除背面。這樣就在渲染正面之前渲染了背面,這也是渲染透明物體的需要。
但是在開(kāi)啟背面剔除后,會(huì)發(fā)現(xiàn)上面的游泳圈模型還是顯示的有問(wèn)題,原因沒(méi)有開(kāi)啟深度測(cè)試。如圖:

3. 深度測(cè)試:
- 深度: 深度就是在openGL坐標(biāo)系中,像素點(diǎn)的 Z 坐標(biāo)距離觀察者的距離。觀察者可能放在坐標(biāo)系的任何位置,那么,就不能簡(jiǎn)單的說(shuō) Z 數(shù)值越大或越小,就是越靠近觀察者。
如果觀察者在Z軸的正方向,Z 值大的靠近觀察者,如果是在Z軸的反方向,則 Z 值小的更靠近觀察者。- 深度緩沖區(qū)(DepthBuffer): 深度緩沖區(qū)原理就是把一個(gè)距離觀察平面(近裁剪面)的深度值(或距離)與窗口中的每個(gè)像素相關(guān)聯(lián)。
首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值設(shè)置為最大值。
如果啟用了深度緩沖區(qū),在繪制每個(gè)像素之前,OpenGL會(huì)把它的深度值和已經(jīng)存儲(chǔ)在這個(gè)像素的深度值進(jìn)行比較。如果,新像素深度值 < 原先像素深度值,則新像素值會(huì)取代原先的;反之,新像素值被遮擋,它的顏色值和深度將被丟棄。
這個(gè)比較、丟棄的過(guò)程就叫做 深度測(cè)試,深度測(cè)試是另一種高效消除隱藏面的技術(shù)。
申請(qǐng)一個(gè)顏色緩沖區(qū)和一個(gè)深度緩沖區(qū):
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
要啟用深度測(cè)試,只需調(diào)用
glEnable(GL_DEPTH_TEST);
關(guān)閉深度測(cè)試:
glDisable(GL_DEPTH_TEST);
如果沒(méi)有深度緩沖區(qū),那么啟動(dòng)深度測(cè)試的命令將被忽略。
在繪制場(chǎng)景前,清除顏色緩沖區(qū)和深度緩沖區(qū):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
開(kāi)啟了深度測(cè)試后,我們終于得到了一個(gè)我們想要的游泳圈模型:

清除深度緩沖區(qū)的默認(rèn)值是1.0,表示最大的深度值,深度值的范圍在[0,1]之間。
用戶通過(guò)glDepthFunc(GLenum func)函數(shù)指定深度測(cè)試的規(guī)則,這個(gè)函數(shù)包括一個(gè)參數(shù),如下表:
| 參數(shù) | 說(shuō)明 |
|---|---|
| GL_ALWAYS | 總是通過(guò)測(cè)試 |
| GL_NEVER | 總是不通過(guò)測(cè)試 |
| GL_LESS | 當(dāng)前深度值 < 存儲(chǔ)的深度值時(shí)通過(guò) |
| GL_EQUAL | 當(dāng)前深度值 = 存儲(chǔ)的深度值時(shí)通過(guò) |
| GL_LEQUAL | 當(dāng)前深度值 <= 存儲(chǔ)的深度值時(shí)通過(guò) |
| GL_GREATER | 當(dāng)前深度值 > 存儲(chǔ)的深度值時(shí)通過(guò) |
| GL_NOTEQUAL | 當(dāng)前深度值 != 存儲(chǔ)的深度值時(shí)通過(guò) |
| GL_GEQUAL | 當(dāng)前深度值 >= 存儲(chǔ)的深度值時(shí)通過(guò) |
z-fighting(z沖突、閃爍)問(wèn)題:
當(dāng)深度值精確度很低時(shí),容易引起ZFighting現(xiàn)象,表現(xiàn)為兩個(gè)物體靠的很近時(shí)確定誰(shuí)在前,誰(shuí)在后時(shí)出現(xiàn)了歧義。問(wèn)題表現(xiàn)如圖:

- 在第二次繪制時(shí),插入一個(gè)少量的偏移。
- 使用 glPolygonOffset 函數(shù)調(diào)節(jié)片段的深度值,使得深度值偏移而不產(chǎn)生重疊。
- 使用更高位數(shù)的深度緩沖區(qū),通常使用的深度緩沖區(qū)是24位的,現(xiàn)在有一些硬件使用使用32位的緩沖區(qū),使精確度得到提高。