OpenGL渲染技巧

1瞳抓、隱藏面消除

在渲染3D場景過程中可能會產(chǎn)生以下問題

  • 我們需要決定哪些部分是對觀察者可?的,或者哪些部分是對觀察者不可見的埃疫?
  • 對于不可見的我們應該怎么處理?
    解決方案

1. 油畫算法

  • 先繪制場景中較遠的物體孩哑,再繪制較近的物體


    image.png

弊端

  • 使?用油畫算法,只要將場景按照物理理距離觀察者的距離遠近排序,由遠及近的繪制即可.那么會出現(xiàn) 什什么問題? 如果三個三?角形是疊加的情況,油畫算法將?無法處理理.

2.正背面剔除(Face Culling)

從任何?個?向去觀察一個立方體胳蛮,最多可以看到3個?抚垄。如果我們能以某種?式去丟棄這部分數(shù)據(jù)智哀。OpenGL在渲染的性能即可提高超過50%。

  • 正/背面的區(qū)分:
    正?:按照逆時針頂點連接順序的三?形?
    背?:按照順時針頂點連接順序的三角形?
    任何平?都有2個?:正?和背面,?個時刻我們只能看到一面。通過分析頂點數(shù)據(jù)的順序幸撕,OpenGL可以做到檢查所有正面朝向觀察者的面貌矿,并渲染它們尽楔;從?丟棄背面朝向的面呕寝。

??注意:正?和背?是有三角形的頂點定義順序和觀察者方向共同決定的讶坯。若觀察者的觀察?向發(fā)生改變漱办,正?和背面也會發(fā)生相應的改變。

image.png
  • 當觀察者在右側(cè)時:右邊的三角形為逆時針方向,則為正面;而左側(cè)的三?形為順時針麦向,則為背?沙郭。
  • 當觀察者在左側(cè)時:左邊的三?形為逆時針?向送挑,則為正?;而右側(cè)的三?形為順時針络凿,則為背?虐先。

弊端:如果前后兩個點都是正面或是背面,這時OpenGL無法區(qū)分哪個面在前,哪個面在后貌笨,就可能出現(xiàn)下圖所示的問題锥惋。


image.png
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 問題的預防

    1. 不要將兩個物體靠的太近钢悲。
      避免渲染時三?形疊在一起点额。
    1. 盡可能將近裁剪面設(shè)置得離觀察者遠一些。
      如果觀察者離近裁剪平?很近莺琳,那么深度測試對精確度要求很?还棱。因此,可以適當推遠近裁剪平?的位置來避免這個問題惭等,但是可能導致離觀察者較近的物體被裁減掉珍手,使用時需要小心。
    1. 使用更高位數(shù)的深度緩沖區(qū)辞做。
      默認的深度緩沖區(qū)精度是24位的琳要,如果硬件支持32位的緩沖區(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。
image.png

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值越高易茬,添加的藍色顏色成分越高酬蹋,?標顏?所保留的成分就會越少。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市除嘹,隨后出現(xiàn)的幾起案子写半,更是在濱河造成了極大的恐慌,老刑警劉巖尉咕,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠蝇,死亡現(xiàn)場離奇詭異,居然都是意外死亡年缎,警方通過查閱死者的電腦和手機悔捶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來单芜,“玉大人蜕该,你說我怎么就攤上這事≈摒” “怎么了堂淡?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扒腕。 經(jīng)常有香客問我绢淀,道長,這世上最難降的妖魔是什么瘾腰? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任皆的,我火速辦了婚禮,結(jié)果婚禮上蹋盆,老公的妹妹穿的比我還像新娘费薄。我一直安慰自己,他們只是感情好栖雾,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布楞抡。 她就那樣靜靜地躺著,像睡著了一般析藕。 火紅的嫁衣襯著肌膚如雪召廷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天噪径,我揣著相機與錄音柱恤,去河邊找鬼数初。 笑死找爱,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的泡孩。 我是一名探鬼主播车摄,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吮播?” 一聲冷哼從身側(cè)響起变屁,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎意狠,沒想到半個月后粟关,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡环戈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年闷板,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片院塞。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡遮晚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拦止,到底是詐尸還是另有隱情县遣,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布汹族,位于F島的核電站萧求,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鞠抑。R本人自食惡果不足惜饭聚,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搁拙。 院中可真熱鬧秒梳,春花似錦、人聲如沸箕速。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盐茎。三九已至兴垦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間字柠,已是汗流浹背探越。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窑业,地道東北人钦幔。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像常柄,于是被迫代替她去往敵國和親鲤氢。 傳聞我的和親對象是個殘疾皇子搀擂,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349