瞭解了OpenGL的渲染流程和經常使用API後,就能夠簡單的繪製出圖形了。可是在繪製中可能會碰到些意想不到的問題。bash
在默認狀況下,咱們所渲染的每一個點、線或三角形都會在屏幕上進行光柵化,而且會按照在組合圖元批次時指定的順序進行排列,這在某些狀況下會產生問題。其中一個可能出現的問題是,若是咱們繪製一個由不少個三角形組成的實體對象,那麼第一個繪製的三角形可能會被後面繪製的三角形覆蓋。函數
圖中,圓環上有些三角形在圓環的背面,而另外一些則在圓環的正面。咱們應該是看不到背面的。性能
OpenGL中最簡單的實體多邊形就是三角形,它只有3個邊。光柵化硬件最歡迎三角形,而如今三角形已是OpenGL中支持的惟一一種多邊形了。每3個頂點定義一個新的三角形。下圖是兩個三角形,它們是用6個頂點進行繪製的,這6個頂點編號依次爲V0到V5。測試
請注意鏈接頂點的線段上所標示的箭頭。在繪製第一個三角形時,線條將按照從V0到V1,再到V2,最後回到V0的順序來繪製一個閉合的三角形。這個路徑是按照頂點被指定的順序沿着順時針方向的,這種方向特性也體如今了第二個三角形中。這種順序與方向結合來指定頂點的方式稱爲環繞。圖中右邊的三角形就被稱做是順時針環繞的。若是咱們將左邊三角形的V4和V5的位置進行交換,咱們就獲得了逆時針環繞。下圖是兩個三角形,它們的纏繞方向相反。spa
在默認狀況下,OpenGL認爲具備逆時針方向環繞的多邊形是正面的。這意味着圖3的左側是三角形的正面,而右側是三角形的背面。3d
咱們經常但願爲一個多邊形的正面和背面分別設置不一樣的物理特徵。咱們能夠徹底隱藏一個多邊形的背面,或者給它設置-種不一樣的顏色和反射屬性。紋理圖像在背面三角形中也是相反的。在一個場景中,使全部的多邊形保持環繞方向的一致,並使用正面多邊 形來繪製全部實心物體的外表面是很是重要的。code
若是想改變OpenGL的這個默認行爲,能夠調用下面這個函數。cdn
/*
GL_CW:順時針環繞的多邊形將爲正面
GL_CCW:逆時針環繞的多邊形將爲正面
*/
g1FrontFace(GL_CW) ;
複製代碼
對於這個問題,一個可能的解決辦法是,對這些三角形進行排序,而且首先渲染那些較遠的三角形,再在它們上方渲染那些較近的三角形。這種方式稱爲「油畫法" ( painters algorithm )。對象
這種方法在計算機圖形處理中是很是低效的,主要緣由有兩個。blog
油畫法弊端:若是三個三角形是疊加的狀況,油畫法將沒法處理!
在任何狀況下,咱們都應該只能看到正面,看不到背面,那爲什麼還要浪費資源繪製背面呢?對三角形的區分正面和背面的緣由之一就是爲了剔除。背面剔除可以極大的提升性能,並修正圖1出現的問題。這種方式是很是高效的,在渲染的圖元裝配階段就總體拋棄了一些三角形,並無執行任何不恰當的光柵化操做。
使用glEnable
和glDisable
函數便可設置表面剔除功能。
glEnable(GL_CULL_FACE); //開啓
glDisable(GL_CULL_FACE) ; //關閉
複製代碼
指明剔除的是正面仍是背面調用函數glCullFace
。
/*
mode的可選值:
GL_FRONT
GL_BACK
GL_FRONT_AND_BACK
*/
void glCullFace(GLenum mode);
複製代碼
表面剔除後的效果
仔細觀察不難發現咱們在進行表面剔除後仍然會有一些不現實的畫面。
就算背面剔除可以消除位於對象背面的三角形,那麼若是是重疊的獨立對象又該怎麼辦呢?咱們以前提到過油畫法,這種方法是根據一種油畫使用的技術而得名的。咱們只要先簡單地繪製背景,再在上面繪製較近的對象。這樣作可能只要在畫布上進行次數很少的繪製( 在手工繪製時更加有用),但對於圖形硬件來講,這樣作會致使在同一個片斷區域重複進行繪製,而每一次繪製都會產生性能開銷。若是開銷過大則致使光柵化過程變慢,咱們將這種方式稱爲「填充受限」。可是將油畫法顛倒過來使用,實際上將會加速填充性能。首先繪製那些離觀察者較近的對象,而後再繪製那些較遠的對象。
深度測試將消除那些應該被已存在像素覆蓋的像素,這將節省可觀的存儲器帶寬。
深度測試是另一種高效消除隱藏表面的技術。它的概念很簡單:在繪製一 個像素時,將一個值(稱爲z值)分配給它,這個值表示它到觀察者的距離。而後,當另一個像素須要在屏幕上的一樣位置進行繪製時,新像素的z值將與已經存儲的像素的z值進行比較。若是新像素的z值比較大,那麼它距離觀察者就比較近,這樣就在原來的像素上面,因此原來的像素就會被新的像素覆蓋。若是新像素的z值更低,那麼它 就必須位於原來像素的後面,不能遮住原來的像素。在內部,這個任務是經過深度緩衝區實現的,它存儲了屏幕上每一個像素的深度值。
咱們在使用GLUT設置OpenGL窗口的時候,應該請求一個深度緩衝區並啓用深度測試。
//申請一個顏色緩衝區和一個深度緩衝區。
glutInitDisplayMode(GLUT_DOUBLEI_GLUT_RGBA | GLUT_DEPTH);
//啓用深度測試
glEnable(GL_DEPTH_TEST);
複製代碼
若是沒有深度緩衝區,那麼啓用深度測試的命令將被忽略。