1瞳抓、隱藏面消除
在渲染3D場景過程中可能會產(chǎn)生以下問題
- 我們需要決定哪些部分是對觀察者可?的,或者哪些部分是對觀察者不可見的埃疫?
- 對于不可見的我們應該怎么處理?
解決方案
1. 油畫算法
-
先繪制場景中較遠的物體孩哑,再繪制較近的物體
弊端
- 使?用油畫算法,只要將場景按照物理理距離觀察者的距離遠近排序,由遠及近的繪制即可.那么會出現(xiàn) 什什么問題? 如果三個三?角形是疊加的情況,油畫算法將?無法處理理.
2.正背面剔除(Face Culling)
從任何?個?向去觀察一個立方體胳蛮,最多可以看到3個?抚垄。如果我們能以某種?式去丟棄這部分數(shù)據(jù)智哀。OpenGL在渲染的性能即可提高超過50%。
- 正/背面的區(qū)分:
正?:按照逆時針頂點連接順序的三?形?
背?:按照順時針頂點連接順序的三角形?
任何平?都有2個?:正?和背面,?個時刻我們只能看到一面。通過分析頂點數(shù)據(jù)的順序幸撕,OpenGL可以做到檢查所有正面朝向觀察者的面貌矿,并渲染它們尽楔;從?丟棄背面朝向的面呕寝。
??注意:正?和背?是有三角形的頂點定義順序和觀察者方向共同決定的讶坯。若觀察者的觀察?向發(fā)生改變漱办,正?和背面也會發(fā)生相應的改變。
- 當觀察者在右側(cè)時:右邊的三角形為逆時針方向,則為正面;而左側(cè)的三?形為順時針麦向,則為背?沙郭。
- 當觀察者在左側(cè)時:左邊的三?形為逆時針?向送挑,則為正?;而右側(cè)的三?形為順時針络凿,則為背?虐先。
弊端:如果前后兩個點都是正面或是背面,這時OpenGL無法區(qū)分哪個面在前,哪個面在后貌笨,就可能出現(xiàn)下圖所示的問題锥惋。
1.2.1OpenGL中的剔除
//開啟表面剔除(默認背面剔除)
glEnable(GL_CULL_FACE);
//關(guān)閉表面剔除(默認背面剔除)
glDisable(GL_CULL_FACE);
//選擇剔除那個面(正面/背面)
// mode參數(shù)為: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK丛忆,默認GL_BACK
glCullFace(GLenum mode);
//用戶指定繞序那個為正面
//mode參數(shù)為: GL_CW, GL_CCW,默認值:GL_CCW
glFrontFace(GL enum mode);
//剔除正面實現(xiàn)①
glCullFace(GL_BACK);
glFrontFace(GL_CW);
//剔除正面實現(xiàn)②
glCullFace(GL_FRONT);
glFrontFace(GL_CCW);
2凰浮、深度測試
2.1深度
深度纳鼎,就是像素點在3D世界中距離攝像機的距離,即Z值导帝。
2.2深度緩沖區(qū)
深度緩存區(qū)守谓,就是?塊內(nèi)存區(qū)域,專門存儲每個像素點的深度值您单。深度值(Z值)越?斋荞,則離攝像機就越遠。
為什么需要深度緩沖區(qū)?
在不使?深度測試的時候虐秦,如果先繪制?個?較近的物體平酿,再繪制較遠的物體凤优。較遠的圖像就會像油畫一樣覆蓋掉之前的圖像
有了深度緩沖區(qū)后,繪制物體的順序就不那么重要了蜈彼。
只要通過開啟了深度緩沖區(qū)筑辨,并允許深度值的寫入,OpenGL都會把像素的深度值寫入到緩沖區(qū)中柳刮。
2.3深度測試
在決定是否繪制?個物體表?時挖垛,首先要將表面對應的像素的深度值與當前深度緩沖區(qū)中的值進??較。如果大于深度緩沖區(qū)中的值秉颗,則丟棄這部分痢毒;否則利?這個像素對應的深度值和顏?值,分別更新深度緩沖區(qū)和顏色緩存區(qū)蚕甥。這個過程稱為深度測試哪替。
2.4深度值的計算
深度值,?般由16位菇怀、24位或者32位值表示凭舶,通常是24位。
- 位數(shù)越高爱沟,深度的精確度越好帅霜。
- 深度值的范圍在[0,1]之間,值越?表示越靠近觀察者呼伸,值越大表示遠離觀察者身冀。
2.5 深度值的使用
- 開啟深度測試
glEnable(GL_DEPTH_TEST);
- 在繪制場景前,清除顏?緩存區(qū)括享,深度緩沖搂根。清除深度緩沖區(qū)默認值為1.0
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- 指定深度測試判斷模式
void glDepthFunc(GLEnum mode);
- 深度緩沖區(qū)寫入開關(guān)
//value: GL_TURE,開啟深度緩沖區(qū)寫入; GL_FALSE,關(guān)閉深度緩沖區(qū)寫?
void glDepthMask(GLBool value);
2.6總結(jié)
使?正?/背面剔除法和深度測試法解決了OpenGL的渲染效率問題。
3铃辖、ZFighting閃爍問題
深度緩測試雖然能解決大部分渲染問題剩愧,但是還是存在隱患。
3.1原因
由于精度的限制娇斩,對于相差非常小的深度值(比如在同一個深度進行2次渲染)仁卷,就可能出現(xiàn)不能正確區(qū)分兩個深度值的問題,導致測試的結(jié)果隨機出現(xiàn)犬第。所以五督,顯示時2個畫?交錯出現(xiàn),就會出現(xiàn)閃爍問題瓶殃。
3.2解決方案
- 步驟1:啟用Polygon Offset
增大重疊或深度值接近的2個圖形的深度值差距充包,使得OpenGL可以區(qū)分兩個深度值。
Polygon Offset模式 | 對應的光柵化模式 |
---|---|
GL_POLYGON_OFFSET_FILL | GL_FILL |
GL_POLYGON_OFFSER_LINE | GL_LINE |
GL_POLYGON_OFFSER_POINT | GL_POINT |
glEnable(GL_POLYGON_OFFSET_FILL);
- 步驟二:指定偏移量
- 通過glPolygonOffset來指定2個參數(shù) factor和units
//應?到?段上總偏移計算?程式
//Depth Offset = (DZ * factor) + (r * units);
//DZ:深度值(Z值)
//r:使得深度緩沖區(qū)產(chǎn)?變化的最?值,是由具體OpenGL平臺指定的?個常量
void glPolygonOffset(Glfloat factor, Glfloat units);
- offset為負值基矮,將使得z值距離攝像機更近淆储;?正值,將使得z值距離攝像機更遠家浇。 一般而言本砰,我們設(shè)置factor和units設(shè)置為-1.0和-1.0。
步驟三:關(guān)閉Polygon Offset
// 參數(shù)和開啟的參數(shù)相同
glDisable(GL_POLYGON_OFFSET_FILL);
3.3 問題的預防
- 不要將兩個物體靠的太近钢悲。
避免渲染時三?形疊在一起点额。
- 不要將兩個物體靠的太近钢悲。
- 盡可能將近裁剪面設(shè)置得離觀察者遠一些。
如果觀察者離近裁剪平?很近莺琳,那么深度測試對精確度要求很?还棱。因此,可以適當推遠近裁剪平?的位置來避免這個問題惭等,但是可能導致離觀察者較近的物體被裁減掉珍手,使用時需要小心。
- 盡可能將近裁剪面設(shè)置得離觀察者遠一些。
- 使用更高位數(shù)的深度緩沖區(qū)辞做。
默認的深度緩沖區(qū)精度是24位的琳要,如果硬件支持32位的緩沖區(qū),就可以提供更高的精度秤茅。
- 使用更高位數(shù)的深度緩沖區(qū)辞做。
4稚补、裁剪
在OpenGL中提?渲染效率的?種?式。只刷新屏幕上發(fā)?變化的部分框喳。
- 基本原理
用于渲染時限制繪制區(qū)域课幕,通過此技術(shù)可以在屏幕(幀緩沖)指定一個矩形區(qū)域。啟用剪裁測試之后帖努,不在此矩形區(qū)域內(nèi)的片元被丟棄,只有在此矩形區(qū)域內(nèi)的?元才有可能進入幀緩沖粪般。因此拼余,實際達到的效果就是在屏幕上開辟了一個?窗口,可以在其中進?指定內(nèi)容的繪制亩歹。
//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);
4.1 理解窗口匙监、視口、裁剪區(qū)域
- 窗口
顯示界面小作。就相當于iOS里面的window亭姥。 - 視口
窗口中?來顯示圖形的一塊矩形區(qū)域,它可以和窗?等?顾稀,也可以?窗口?或者小达罗。只有繪制在視口區(qū)域中的圖形才能被顯示,如果圖形有一部分超出了視口區(qū)域,那么那一部分是看不到的粮揉。通過glViewport()函數(shù)設(shè)置巡李。就相當于View。 - 裁剪區(qū)域(平?投影)
視口矩形區(qū)域的最小最大x坐標(left,right)和最小最?y坐標 (bottom,top)扶认,?不是窗口的最小最大x坐標和y坐標侨拦。通過glOrtho()函數(shù)設(shè)置,這個函數(shù)還需指定最近最遠z坐標辐宾,形成一個立體的裁剪區(qū)域狱从。 就相當于設(shè)置一個frame。
5混合
OpenGL渲染時會把顏色值存在顏?緩存區(qū)中叠纹,每個?段的深度值也是放在深度緩沖區(qū)季研。
- 當深度緩沖區(qū)被關(guān)閉時,新的顏色將簡單地覆蓋原來顏色緩存區(qū)存在的顏色值吊洼。
- 當深度緩沖區(qū)再次打開時训貌,新的顏?片段只是當它們比原來的值更接近鄰近的裁剪平?才會替換原來的顏?片段。
//開啟混合
gl_Enable(GL_BIEND);
5.1 顏色混合
- ?標顏色:已經(jīng)存儲在顏色緩存區(qū)的顏色值 (已經(jīng)存在的顏色冒窍,舊顏色)
- 源顏色:作為當前渲染命令結(jié)果進入顏色緩存區(qū)的顏?值 (新進來的顏色 递沪,新顏色)
當混合功能被開啟時,源顏色和?標顏色的組合方式是混合方程式控制的综液。在默認情況下款慨,混合方程式如下所示:
//Cf: 最終計算參數(shù)的顏?
//Cs: 源顏?
//Cd: 目標顏?
//S: 源混合因?,源Alpha混合因子
//D: ?標混合因?,?標Alpha混合因子
Cf = (Cs * S) + (Cd * D);
5.2 混合方程式
實際上不止一種顏色混合方程式,OpenGL有5個不同的方程式進行選擇谬莹。
glbBlendEquation(GLenum mode);
模式 | 函數(shù) |
---|---|
GL_FUNC_ADD | Cf = (Cs * S) + (Cd * D) |
GL_FUNC_SUBTRACT | Cf = (Cs * S) - (Cd * D) |
GL_FUNC_REVERSE_SUBTRACT | Cf = (Cd * D) - (Cs * S) |
GL_MIN | Cf = min(Cs, Cd) |
GL_MAX | Cf = max(Cs, Cd) |
5.3 設(shè)置混合因?
//S:源混合因?
//D:?標混合因子
glBlendFunc(GLenum S, GLenum D);
函數(shù) | RGB混合因子 Alpha混合因子 |
---|---|
GL_ZERO | (0, 0, 0) 0 |
GL_ONE | (1, 1, 1) 1 |
GL_SRC_COLOR | (Rs, Gs, Bs) As |
GL_ONE_MINUS_SCR_COLOR | (1, 1, 1) - (Rs, Gs, Bs) 1 - As |
GL_DST_COLOR | (Rd, Gd, Bd) Ad |
GL_ONE_MINUS_DST_COLOR | (1, 1, 1) - (Rd, Gd, Bd) 1 - Ad |
GL_SRC_ALPHA | (As, As, As) As |
GL_ONE_MINUS_SCR_ALPHA | (1, 1, 1) - (As, As, As) 1- As |
GL_DST_ALPHA | (Ad, Ad, Ad) Ad |
GL_ONE_MINUS_DST_ALPHA | (1, 1, 1) - (Ad, Ad, Ad) 1- Ad |
GL_CONSTANT_COLOR | (Rc, Gc, Bc) Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (1, 1, 1) - (Ac, Ac, Ac) 1- Ac |
GL_CONSTANT_ALPHA | (Ac, Ac, Ac) Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (1, 1, 1) - (Ac, Ac, Ac) 1- Ac |
GL_SRC_ALPHA_SATURATE | (f, f, f)* f = min(As, 1 - Ad) 1 |
表中R檩奠、G、B附帽、A 分別代表 紅埠戳、綠、藍蕉扮、Alpha
表中下標S整胃、D,分別代表源喳钟、?標
表中C 代表常量顏?(默認?色)
- 常量混合顏色
默認初始化為?色(0, 0, 0, 0)屁使,通過下面的函數(shù)可以修改這個顏色。
void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );
- glBlendFuncSeparate函數(shù)
除了能使?OpenGL內(nèi)置的混合因?奔则,還可以有更靈活的選擇蛮寂。
//strRGB: 源顏色的混合因?
//dstRGB: 目標顏?的混合因?
//strAlpha: 源顏?的Alpha因?
//dstAlpha: 目標顏?的Alpha因?
void glBlendFuncSeparate(GLenum strRGB, GLenum dstRGB , GLenum strAlpha, GLenum dstAlpha);
5.3.1 舉例
下?通過一個常見的混合函數(shù)組合來說明問題:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
如果顏色緩存區(qū)已經(jīng)有一種顏?紅色(1, 0, 0, 0)
在這上面混合一種alpha為0.6的藍色(0, 0, 1, 0.6)
Cd (目標顏色) = (1, 0, 0, 0)
Cs (源顏色) = (0, 0, 1, 0.6)
S = 源alpha值 = 0.6
D = 1 - 源alpha值= 1-0.6 = 0.4
?程式Cf = (Cs * S) + (Cd * D)
等價于 = (Blue * 0.6) + (Red * 0.4)
最終顏色是以原先的紅色(?標顏色)與后來的藍色(源顏色)進?組合。源顏色的alpha值越高易茬,添加的藍色顏色成分越高酬蹋,?標顏?所保留的成分就會越少。