OpenGL學習(四)-- 正面&背面剔除和深度測試
我的
OpenGL
專題學習目錄,希望和大家一起學習交流進步!
- OpenGL學習(一)-- 術語瞭解
- OpenGL學習(二)-- Xcode 搭建 OpenGL 環境
- OpenGL學習(三)-- OpenGL 基礎渲染
- OpenGL學習(四)-- 正面&背面剔除和深度測試
- OpenGL學習(五)-- 裁剪與混合
- OpenGL學習(六)-- 基礎紋理
- OpenGL學習(七)-- 基礎變化綜合練習實踐總結
- OpenGL學習(八)-- OpenGL ES 初探(上)
- OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
- OpenGL學習(十)-- 著色語言 GLSL 語法介紹
- OpenGL學習(十一)-- 用 GLSL 實現載入圖片
- OpenGL學習(十二)-- OpenGL ES 紋理翻轉的策略對比
一、渲染中可能會出現的問題(不希望出現的幾何圖形)
預設情況下,我們所渲染的每個點、線或三角形都會再螢幕上進行光柵化,並按照在組合圖元批次時指定的順序排列,這在某些情況下會產生問題。
如果我們繪製一個由很多個三角形組成的實體物件,那麼第一個繪製的三角形可能會被後面繪製的三角形覆蓋。如下圖這個像游泳圈似的模型,其中一些三角形在游泳圈的背面,另一些在正面,正常我們應該是看不到背面的(不考慮透明幾何體的特殊情況)。這樣的話,三角形繪製的順序可能會一團糟,就變成了下圖的樣子:
二、解決方法
1. 油畫法(painters algorithm):
對這些三角形排序,先渲染較遠的三角形,再在它們上方渲染較近的三角形。但這種方法在圖形處理中效率很低,必須在任何發生重疊的地方對每個畫素進行兩次寫操作,速度會變慢。並且對獨立的三角形排序的開銷會過高。所以一般不推薦使用。
2. 正面&背面剔除:
對正面和背面三角形進行區分的原因之一就是為了進行剔除。背面剔除能極大提高效能,避免上圖出現的問題。它很高效,在渲染的圖元裝配階段就整體拋棄了一些三角形。
開啟背面剔除:
glEnable(GL_CULL_FACE);
關閉背面剔除:
glDisable(GL_CULL_FACE);
請注意,我們並沒有知名剔除的是正面還是背面。這是由另外一個函式 glCullFace
控制的。
void glCullFace(GLenum mode);
複製程式碼
mode 引數的可用值為 GL_FRONT
、GL_BACK
或 GL_FRONT_AND_BACK
。這樣要消除不透明物體的內部幾何圖形就需要兩行程式碼:
void glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
複製程式碼
在某些情況下,剔除實體幾何體的正面也很有必要,比如要顯示圖形內部渲染的時候。在渲染透明物件時(下面馬上就會講到混合),我們經常會對一個物件進行兩次渲染,第一次會開啟透明並剔除正面,第二次則消除背面。這樣就在渲染正面之前渲染了背面,這也是渲染透明物體的需要。
但是在開啟背面剔除後,會發現上面的游泳圈模型還是顯示的有問題,如圖:
這是因為沒有開啟深度測試。3. 深度測試:
- 深度:
深度
就是在 openGL 座標系中,畫素點的 Z 座標距離觀察者的距離。觀察者可能放在座標系的任何位置,那麼,就不能簡單的說 Z 數值越大或越小,就是越靠近觀察者。 如果觀察者在Z軸的正方向,Z 值大的靠近觀察者,如果是在Z軸的反方向,則 Z 值小的更靠近觀察者。
- 深度緩衝區(DepthBuffer):
深度緩衝區
原理就是把一個距離觀察平面(近裁剪面)的深度值(或距離)與視窗中的每個畫素相關聯。 首先,使用glClear(GL_DEPTH_BUFFER_BIT)
,把所有畫素的深度值設定為最大值。 如果啟用了深度緩衝區,在繪製每個畫素之前,OpenGL 會把它的深度值和已經儲存在這個畫素的深度值進行比較。如果,新畫素深度值 < 原先畫素深度值,則新畫素值會取代原先的;反之,新畫素值被遮擋,它的顏色值和深度將被丟棄。 這個比較、丟棄的過程就叫做深度測試
,深度測試是另一種高效消除隱藏面的技術。
申請一個顏色緩衝區和一個深度緩衝區:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
複製程式碼
要 啟用
深度測試,只需呼叫
glEnable(GL_DEPTH_TEST);
關閉
深度測試:
glDisable(GL_DEPTH_TEST);
如果沒有深度緩衝區,那麼啟動深度測試的命令將被忽略。 在繪製場景前,清除顏色緩衝區和深度緩衝區:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
複製程式碼
開啟了深度測試後,我們終於得到了一個我們想要的游泳圈模型:
清除深度緩衝區的預設值是1.0,表示最大的深度值,深度值的範圍在[0,1]之間。
使用者通過 glDepthFunc(GLenum func)
函式指定深度測試的規則,這個函式包括一個引數,如下表:
引數 | 說明 |
---|---|
GL_ALWAYS |
總是通過測試 |
GL_NEVER |
總是不通過測試 |
GL_LESS |
當前深度值 < 儲存的深度值時通過 |
GL_EQUAL |
當前深度值 = 儲存的深度值時通過 |
GL_LEQUAL |
當前深度值 <= 儲存的深度值時通過 |
GL_GREATER |
當前深度值 > 儲存的深度值時通過 |
GL_NOTEQUAL |
當前深度值 != 儲存的深度值時通過 |
GL_GEQUAL |
當前深度值 >= 儲存的深度值時通過 |
z-fighting(z衝突、閃爍)問題:
當深度值精確度很低時,容易引起ZFighting現象,表現為兩個物體靠的很近時確定誰在前,誰在後時出現了歧義。
- 避免深度值相同造成的z-fighting衝突問題的幾種做法:
- 在第二次繪製時,插入一個少量的偏移。
- 使用
glPolygonOffset
函式調節片段的深度值,使得深度值偏移而不產生重疊。
- 使用更高位數的深度緩衝區,通常使用的深度緩衝區是 24 位的,現在有一些硬體使用使用 32 位的緩衝區,使精確度得到提高。
以上的總結參考了並部分摘抄了以下文章,非常感謝以下作者的分享!:
1、《OpenGL超級寶典 第5版》
2、《OpenGL程式設計指南(第八版)》
3、作者 The fool 的《OpenGL學習腳印:深度測試(depth testing)》
4、作者 lysc_forever的《OpenGL中的深度、深度快取、深度測試》
轉載請備註原文出處,不得用於商業傳播——凡幾多