OpenGL渲染技巧
了解了OpenGL的渲染流程和常用API后,就可以簡(jiǎn)單的繪制出圖形了细层。但是在繪制中可能會(huì)碰到些意想不到的問(wèn)題惜辑。
在默認(rèn)情況下,我們所渲染的每個(gè)點(diǎn)疫赎、線或三角形都會(huì)在屏幕上進(jìn)行光柵化盛撑,并且會(huì)按照在組合圖元批次時(shí)指定的順序進(jìn)行排列,這在某些情況下會(huì)產(chǎn)生問(wèn)題捧搞。其中一個(gè)可能出現(xiàn)的問(wèn)題是抵卫,如果我們繪制一個(gè)由很多個(gè)三角形組成的實(shí)體對(duì)象,那么第一個(gè)繪制的三角形可能會(huì)被后面繪制的三角形覆蓋。
圖中胎撇,圓環(huán)上有些三角形在圓環(huán)的背面介粘,而另一些則在圓環(huán)的正面。我們應(yīng)該是看不到背面的晚树。
多邊形的正面和背面
OpenGL中最簡(jiǎn)單的實(shí)體多邊形就是三角形姻采,它只有3個(gè)邊。光柵化硬件最歡迎三角形题涨,而現(xiàn)在三角形已經(jīng)是OpenGL中支持的唯一一種多邊形了偎谁。每3個(gè)頂點(diǎn)定義一個(gè)新的三角形总滩。下圖是兩個(gè)三角形,它們是用6個(gè)頂點(diǎn)進(jìn)行繪制的巡雨,這6個(gè)頂點(diǎn)編號(hào)依次為V0到V5闰渔。
請(qǐng)注意連接頂點(diǎn)的線段上所標(biāo)示的箭頭。在繪制第一個(gè)三角形時(shí)铐望,線條將按照從V0到V1,再到V2冈涧,最后回到V0的順序來(lái)繪制一個(gè)閉合的三角形。這個(gè)路徑是按照頂點(diǎn)被指定的順序沿著順時(shí)針?lè)较虻恼埽@種方向特性也體現(xiàn)在了第二個(gè)三角形中督弓。這種順序與方向結(jié)合來(lái)指定頂點(diǎn)的方式稱(chēng)為環(huán)繞。圖中右邊的三角形就被稱(chēng)作是順時(shí)針環(huán)繞的乒验。如果我們將左邊三角形的V4和V5的位置進(jìn)行交換愚隧,我們就得到了逆時(shí)針環(huán)繞。下圖是兩個(gè)三角形锻全,它們的纏繞方向相反狂塘。
在默認(rèn)情況下,OpenGL認(rèn)為具有逆時(shí)針?lè)较颦h(huán)繞的多邊形是正面的鳄厌。這意味著圖3的左側(cè)是三角形的正面荞胡,而右側(cè)是三角形的背面。
我們常常希望為一個(gè)多邊形的正面和背面分別設(shè)置不同的物理特征了嚎。我們可以完全隱藏一個(gè)多邊形的背面泪漂,或者給它設(shè)置-種不同的顏色和反射屬性。紋理圖像在背面三角形中也是相反的歪泳。在一個(gè)場(chǎng)景中萝勤,使所有的多邊形保持環(huán)繞方向的一致,并使用正面多邊
形來(lái)繪制所有實(shí)心物體的外表面是非常重要的夹囚。
如果想改變OpenGL的這個(gè)默認(rèn)行為纵刘,可以調(diào)用下面這個(gè)函數(shù)邀窃。
/*
GL_CW:順時(shí)針環(huán)繞的多邊形將為正面
GL_CCW:逆時(shí)針環(huán)繞的多邊形將為正面
*/
g1FrontFace(GL_CW) ;
油畫(huà)法
對(duì)于這個(gè)問(wèn)題荸哟,一個(gè)可能的解決辦法是,對(duì)這些三角形進(jìn)行排序,并且首先渲染那些較遠(yuǎn)的三角形瞬捕,再在它們上方渲染那些較近的三角形鞍历。這種方式稱(chēng)為“油畫(huà)法" ( painters algorithm )。
這種方法在計(jì)算機(jī)圖形處理中是非常低效的肪虎,主要原因有兩個(gè)劣砍。
- 必須對(duì)任何發(fā)生幾何圖形重疊地方每個(gè)像素進(jìn)行兩次寫(xiě)操作,而在存儲(chǔ)其中進(jìn)行寫(xiě)操作會(huì)使速度變慢扇救。
- 對(duì)獨(dú)立的三角形進(jìn)行排序的開(kāi)銷(xiāo)會(huì)過(guò)高刑枝。
油畫(huà)法弊端:如果三個(gè)三角形是疊加的情況香嗓,油畫(huà)法將無(wú)法處理!
正面和背面剔除
在任何情況下装畅,我們都應(yīng)該只能看到正面靠娱,看不到背面,那為何還要浪費(fèi)資源繪制背面呢掠兄?對(duì)三角形的區(qū)分正面和背面的原因之一就是為了剔除像云。背面剔除能夠極大的提高性能,并修正圖1出現(xiàn)的問(wèn)題蚂夕。這種方式是非常高效的迅诬,在渲染的圖元裝配階段就整體拋棄了一些三角形,并沒(méi)有執(zhí)行任何不恰當(dāng)?shù)墓鈻呕僮鳌?/p>
使用glEnable
和glDisable
函數(shù)即可設(shè)置表面剔除功能婿牍。
glEnable(GL_CULL_FACE); //開(kāi)啟
glDisable(GL_CULL_FACE) ; //關(guān)閉
指明剔除的是正面還是背面調(diào)用函數(shù)glCullFace
侈贷。
/*
mode的可選值:
GL_FRONT
GL_BACK
GL_FRONT_AND_BACK
*/
void glCullFace(GLenum mode);
表面剔除后的效果
深度測(cè)試
仔細(xì)觀察不難發(fā)現(xiàn)我們?cè)谶M(jìn)行表面剔除后仍然會(huì)有一些不現(xiàn)實(shí)的畫(huà)面。
就算背面剔除能夠消除位于對(duì)象背面的三角形等脂,那么如果是重疊的獨(dú)立對(duì)象又該怎么辦呢铐维?我們之前提到過(guò)油畫(huà)法,這種方法是根據(jù)一種油畫(huà)使用的技術(shù)而得名的慎菲。我們只要先簡(jiǎn)單地繪制背景嫁蛇,再在上面繪制較近的對(duì)象。這樣做可能只要在畫(huà)布上進(jìn)行次數(shù)不多的繪制( 在手工繪制時(shí)更加有用)露该,但對(duì)于圖形硬件來(lái)說(shuō)睬棚,這樣做會(huì)導(dǎo)致在同一個(gè)片段區(qū)域重復(fù)進(jìn)行繪制,而每一次繪制都會(huì)產(chǎn)生性能開(kāi)銷(xiāo)解幼。如果開(kāi)銷(xiāo)過(guò)大則導(dǎo)致光柵化過(guò)程變慢抑党,我們將這種方式稱(chēng)為“填充受限”。但是將油畫(huà)法顛倒過(guò)來(lái)使用撵摆,實(shí)際上將會(huì)加速填充性能底靠。首先繪制那些離觀察者較近的對(duì)象,然后再繪制那些較遠(yuǎn)的對(duì)象。
深度測(cè)試將消除那些應(yīng)該被已存在像素覆蓋的像素,這將節(jié)省可觀的存儲(chǔ)器帶寬特铝。
深度測(cè)試是另外一種高效消除隱藏表面的技術(shù)暑中。它的概念很簡(jiǎn)單:在繪制一 個(gè)像素時(shí),將一個(gè)值(稱(chēng)為z值)分配給它鲫剿,這個(gè)值表示它到觀察者的距離鳄逾。然后,當(dāng)另外一個(gè)像素需要在屏幕上的同樣位置進(jìn)行繪制時(shí)灵莲,新像素的z值將與已經(jīng)存儲(chǔ)的像素的z值進(jìn)行比較雕凹。如果新像素的z值比較大,那么它距離觀察者就比較近,這樣就在原來(lái)的像素上面枚抵,所以原來(lái)的像素就會(huì)被新的像素覆蓋线欲。如果新像素的z值更低,那么它
就必須位于原來(lái)像素的后面汽摹,不能遮住原來(lái)的像素询筏。在內(nèi)部,這個(gè)任務(wù)是通過(guò)深度緩沖區(qū)實(shí)現(xiàn)的,它存儲(chǔ)了屏幕上每個(gè)像素的深度值竖慧。
我們?cè)谑褂肎LUT設(shè)置OpenGL窗口的時(shí)候嫌套,應(yīng)該請(qǐng)求一個(gè)深度緩沖區(qū)并啟用深度測(cè)試。
//申請(qǐng)一個(gè)顏色緩沖區(qū)和一個(gè)深度緩沖區(qū)圾旨。
glutInitDisplayMode(GLUT_DOUBLEI_GLUT_RGBA | GLUT_DEPTH);
//啟用深度測(cè)試
glEnable(GL_DEPTH_TEST);
如果沒(méi)有深度緩沖區(qū)踱讨,那么啟用深度測(cè)試的命令將被忽略。