現在我們用 OpenGL 繪製瞭如下的立方體:
不管我們怎麼旋轉立方體,從任何一個方向去看它,最多都只能看到三個面。
那麼對於 OpenGL 來說,那看不到的另外三個面完全可以不用繪製它,從而提高繪製的效能。
面剔除
既然現在要把看不到的面丟棄,那麼問題就來了:
如何去確定哪個面看得到,哪個面看不到呢?
在 OpenGL 中允許檢查所有正面朝向觀察者的面,並渲染它們,而丟棄所有背向觀察者的面,這就可以節省片段著色器的執行。
所以,我們要做的就是告訴 OpenGL 哪個面是正面,哪個面是背面。
通過頂點連線順序確定正反面
當我們通過三角形來繪製形狀時,會定義頂點連線的順序,它可能是順時針或逆時針。
上圖中,左側三角形就是順時針方向,右側就是逆時針方向。
而 OpenGL 就是利用這個三角形的順時針或逆時針方向來決定三角形是正面還是反面。
預設情況下,逆時針的頂點連線順序被定義為三角形的正面
逆時針或順時針都是相對於觀察者方向的
當定義頂點順序時,應該想象對應的三角形是面向你的,所以定義的三角形頂點方向應該逆時針的。
這樣定義的好處在於三角形頂點的實際連線順序是在光柵化階段進行的,也就是頂點著色器執行之後,這些頂點就是以觀察者視角所見的了。
對於上圖,左側三角形 1 -> 2 ->3 的連線順序是順時針的,這是在觀察者位於螢幕前看到的,如果觀察者位於螢幕後,連線順序依舊是 1 -> 2 -> 3 ,那麼就是逆時針了。
這也是為什麼說,定義三角形頂點順序時要假設三角形是面向你的,保證逆時針定義,並且可以根據觀察者方向的改變,順時針和逆時針方向會發生改變。
如下圖:
三角形的頂點順序都 1 -> 2 -> 3,當我們定義這個順序時,都是假設觀察者正面向這個三角形呢,所以都是逆時針定義的。
但是從右側眼鏡處來觀察,右側三角形方向是逆時針的,左側三角形方向是順時針的,這就是因為對於右側三角形來說,觀察者方向和當初定義順序時的假設方向一致,而對於左側三角形,觀察者方向就和定義順序時的假設方向相反了,所以從反方向來看就成了順時針了。
這樣一來,在面剔除的優化下,右側面可見,左側面不可見了,也就是面向觀察者的正面可見,反面不可見了。
看了好多文章,都沒有講:為什麼要逆時針定義三角形方向,但是觀察時卻成了順時針了,就是因為當初定義的逆時針方向其實是和觀察者方向掛鉤的。
當上圖的觀察者方向變成了左側,那麼頂點連線順序都還是 1 -> 2 -> 3 的情況下,左側三角形的順序就和當初定義頂點順序一樣成了逆時針可見,而右側的三角形頂點順序就成了順時針不可見。
明白了這一點,就對於面剔除更加清晰了。
具體使用
在 OpenGL 中可以通過如下方法開啟面剔除:
glEnable(GLES20.GL_CULL_FACE)
複製程式碼
預設情況下,面剔除是關閉的。
開啟面剔除後,所有的背向觀察者的面都會被丟棄,節省渲染效能。
另外,OpenGL 還提供了其他功能來選擇要剔除的面。
public static native void glCullFace(
int mode
);
複製程式碼
有三個模式可選:
- GL_BACK:只剔除背向面
- GL_FRONT:只剔除正向面
- GL_FRONT_AND_BACK:剔除正向面和背向面
glCullFace 的初始值是 GL_BACK,只剔除背向面。
除了需要剔除的面之外,還可以通過呼叫 glFrontFace 方法告訴 OpenGL 將順時針的面(而不是逆時針的面)定義為正向面。
public static native void glFrontFace(
int mode
);
複製程式碼
它有兩個選項:
- GL_CCW:代表逆時針方向為正向面
- GL_CW:代表順時針方向為正向面
glFrontFace 模式值是 GL_CCW,逆時針方向為正向面。
現假設開啟面剔除,並且剔除正向,順時針為正向:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glFrontFace(GL_CW);
複製程式碼
關於具體的程式碼實現,可以參考我的 Github 專案:
小結
使用面剔除可以優化渲染過程,省下超過 50 % 的片段著色器執行數。
使用面剔除時定義頂點要以逆時針方向定義。
逆時針或順時針都是相對於觀察者方向的。
一起討論
歡迎一起討論 OpenGL 相關知識:
要是二維碼過期了,加微信 ezglumes 好友,備註 OpenGL ,拉你入群~
參考
- https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/04%20Face%20culling/
歡迎關注微信公眾號:【紙上淺談】,獲得最新文章推送~~~