九、OpenGL渲染技巧 - 深度測試&多邊形偏移&裁剪&混合

1. 深度測試

深度測試的目的是防止被其他面遮擋的面顯示出來。開啟深度測試后,會對新的片段進行測試,測試通過的片段放進深度緩沖區(qū),不通過的則被丟棄。

1.1 深度

  • 表示的是像素在Z軸上距離觀察者的距離。
  • 取值范圍是0~1,默認值是1,通常精度是24位,也可以設(shè)置為16位或者32位,位數(shù)越大,精度越高,越可以避免 Z-Fighting , 同時消耗資源越多。
  • 一般情況下,我們作為觀察者,看手機屏幕時,是處于Z軸的負半軸。從這個角度上看,深度值(Z值)越小,離觀察者越近。
  • 屏幕深度值具有非線性特性(在投影矩陣應(yīng)用之前是線性的),即深度值 = 0.5 不代表物體 z 值在投影平截頭體的中間。

1.2 深度緩沖區(qū)

是一塊內(nèi)存區(qū)域,存儲每一個像素點的深度值,一個像素只有一個深度。

1.3 深度測試

  • 深度緩沖區(qū)與顏色緩沖區(qū)是對應(yīng)的,一個存儲像素的深度值,一個存儲像素的顏色值。
  • 每當有新的圖形圖像傳入時,先判斷它的深度值,如果大于該像素在深度緩存區(qū)的深度值則丟棄。反之將新的深度值和顏色值,更新到對應(yīng)的深度緩沖區(qū)及顏色緩沖區(qū)。(因為一般情況深度值越小的離觀察者越近,這個規(guī)則也可以更改)。這個過程,稱為深度測試。
// 在繪制場景前,清除顏?緩存區(qū),深度緩沖
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// 開啟深度測試
glEnable(GL_DEPTH_TEST);
// 關(guān)閉深度測試
glDisable(GL_DEPTH_TEST);

// 打開/關(guān)閉深度緩沖區(qū)寫入 ,GL_FALSE: 關(guān)閉寫入 GL_TRUE:打開寫入
glDepthMask(GLBool value);

// 指定深度測試判斷模式,默認GS_LESS,當前深度值 < 存儲深度值時通過
glDepthFunc(GLEnum mode);
深度測試判斷模式.png

1.4 甜甜圈開啟深度測試

上篇博客中的甜甜圈案例

  // 開啟深度測試
    glEnable(GL_DEPTH_TEST);
    
    //5.繪制
    torusBatch.Draw();
    
    // 關(guān)閉深度測試
    glDisable(GL_DEPTH_TEST);
開啟深度測試后的甜甜圈.gif

現(xiàn)在甜甜圈的展示,已經(jīng)非常好了!
其實開啟了深度測試后,我們甚至不需要使用背面剔除功能。因為深度測試的原理是一個像素內(nèi)只繪制離觀察者最近的圖形圖像。甜甜圈的背面離觀察者遠,自然不會繪制。

1.5 深度測試存在 Z-Fighting / Z閃爍 / Z沖突 的問題

  • 如果AB兩個深度值特別相近,已經(jīng)超出了深度值的精度位數(shù),就無法比較兩個值的大小,會出現(xiàn)可能一會兒顯示A的圖像,一會兒顯示B的圖像,造成一種閃爍的現(xiàn)象,這個現(xiàn)象叫做Z-Fighting 或者 Z閃爍。
  • 我們通過一個金字塔的案例,來觀察Z沖突現(xiàn)象。先畫一個金字塔,默認使用多邊形填充模式。然后再為三角形畫上黑色的邊。這是在邊的位置上,由于既有黑邊也有顏色填充,且兩者的深度值一樣。變會產(chǎn)生下圖中的閃爍現(xiàn)象。一會兒可以從底部看到黑色邊線,一會兒看不到。
// 使用多邊形填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// 設(shè)置平面著色器,顏色為綠色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
// 繪制
pBatch->Draw();

// 使用多邊形線框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//設(shè)置線條寬度
glLineWidth(2.5f);
// 設(shè)置平面著色器,顏色為黑色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
// 繪圖
pBatch->Draw();

// 復(fù)原原本的設(shè)置
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
Z沖突效果.gif

1.6 Z-Fighting / Z閃爍 解決方案

  • 多邊形偏移。如果兩個相差的很小,可以人為的設(shè)置一個偏移值,使得兩個值變大,變大后符合精度位數(shù)限制,就可以正確的比較大小了。
// 1. 開啟多邊形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
// 2. 關(guān)閉多邊形偏移
glDisable(GL_POLYGON_OFFSET_FILL)
參數(shù)列列表: 
GL_POLYGON_OFFSET_POINT   對應(yīng)光柵化模式: GL_POINT
GL_POLYGON_OFFSET_LINE    對應(yīng)光柵化模式: GL_LINE
GL_POLYGON_OFFSET_FILL    對應(yīng)光柵化模式: GL_FILL

// 2. 設(shè)定偏移量(一般情況下,設(shè)置-1, -1)
glPolygonOffset(Glfloat factor,Glfloat units)

上面金字塔案例中,增加如下修改:

// 設(shè)置偏移值
glPolygonOffset(-1.0f, -1.0f);
// 開啟多邊形偏移
glEnable(GL_POLYGON_OFFSET_LINE);

...

// 繪制后,關(guān)閉多邊形偏移
glDisable(GL_POLYGON_OFFSET_LINE);

增加偏移值后的效果如下:


開啟多邊形偏移的金字塔.gif

1.7 Z-Fighting / Z閃爍 預(yù)防方案

  • 不要將兩個物體靠的太近,避免渲染時三角形疊在?一起。因為手動去插?這個偏移是要付出代價的。
  • 盡可能將近裁剪?設(shè)置得離觀察者遠一些。上面我們看到,在近裁剪平?附近,深度的精確度是很高的,因此盡可能讓近裁剪面遠?些的話,會使整個裁剪范圍內(nèi)的精確度變?一些。但是這種?式會使離觀察者較近的物體被裁減掉,因此需要調(diào)試好裁剪面參數(shù)。
  • 使?更?位數(shù)的深度緩沖區(qū),通常使用的深度緩沖區(qū)是24位的,現(xiàn)在有?些硬件使?32位的緩沖區(qū),使精確度得到提?。

2. 裁剪測試

  • 裁剪測試是OpenGL提高渲染性能的一種方式。只刷新屏幕上變化的部分。
  • 原理是設(shè)置一個裁剪區(qū)域,只渲染裁剪區(qū)域內(nèi)的圖形圖像。也就是只有在范圍內(nèi)的片元,才會到達幀緩沖區(qū),超出范圍的將被丟棄。
//1 開啟裁剪測試 
glEnable(GL_SCISSOR_TEST);
//2.關(guān)閉裁剪測試 
glDisable(GL_SCISSOR_TEST);
//3.指定裁剪窗?,  x,y:指定裁剪框左下角位置; width , height:指定裁剪尺?
void glScissor(Glint x,Glint y,GLSize width,GLSize height);

3 混合

  • 在像素緩沖區(qū)中,每一個像素有一個對應(yīng)的顏色值。
  • 如果未開啟深度測試,新的顏色值將會改變顏色緩沖區(qū)的顏色值;如果開啟了,新的顏色值離觀察者更近的才會改變。
  • 當新的顏色透明度為 1 時,新的顏色值需要改變顏色緩沖區(qū)的顏色值時,會直接覆蓋。 小于 1 時,兩種顏色需要混合顯示。
// 啟用顏色混合
glEnable(GL_BlEND);
// 關(guān)閉顏色混合
glDisable(GL_BlEND);

3.1 混合方程式

Cf = (Cs * S) + (Cd * D)
Cf :最終計算參數(shù)的顏?
Cs : 源顏?
Cd :?標顏色
S:源混合因?子
D:?標混合因?

// 選擇混合方程式
void glbBlendEquation(GLenum mode);

// 設(shè)置混合因?, S:源混合因子 D:?標混合因?
void glBlendFunc(GLenum S,GLenum D); 
// 常見的混合因子
void glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

// 設(shè)置混合因?及Alpha因?
// strRGB: 源顏?的混合因子 
// dstRGB: ?標顏色的混合因子 
// strAlpha: 源顏?的Alpha因子 
// dstAlpha: ?標顏色的Alpha因?
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);

// 設(shè)置常量混合顏色,默認黑色
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );
可選擇的混合方程式.png
混合因子.png
  • 表中R、G、B、A 分別代表 紅、綠、藍、alpha。
  • 表中下標S、D,分別代表S:源混合因子 D:?標混合因?
  • 表中C 代表常量顏色(默認?色)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容