零基礎學習OpenGL(四)--模板測試
模板測試在深度測試之前。當片段著色器處理完一個片段後,模板測試會開始執行,被保留下來的才會進入深度測試。模板測試對應模板緩衝。
模板緩衝:每個模板值8位表示,這樣每個片段就有2的8次方,256種模板值。因此可以利用這些不同的模板值,保留或丟棄某個片段。模板緩衝操作允許我們在渲染片段時將模板緩衝設定為一個特定的值。通過在渲染時修改模板緩衝的內容,寫入模板緩衝。同一個渲染迭代中,可以讀取這些緩衝值,來決定片段的去留。
步驟:
啟用模板緩衝寫入;
渲染物體,更新模板緩衝;
禁用模板緩衝寫入;
渲染其他物體時,根據模板緩衝的內容丟棄特定的片段。
啟用:
glEnable(GL_STENCIL_TEST);
也需要在每次迭代之前清除模板緩衝:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask允許我們設定一個位掩碼(Bitmask),它會與將要寫入緩衝的模板值進行與(AND)運算。
glStencilMask(0xFF); // 每一位寫入模板緩衝時都保持原樣
一共有兩個函式能夠用來配置模板測試:glStencilFunc和glStencilOp。
1. glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三個引數:
func:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它們的語義和深度緩衝的函式類似。
ref:自定義參考值,緩衝的內容和這個值比較。
mask:設定一個掩碼,它將會與參考值和儲存的模板值在測試比較它們之前進行與(AND)運算。初始情況下所有位都為1。
eg:glStencilFunc(GL_EQUAL, 1, 0xFF) 表示:只要一個片段的模板值等於參考值1,片段將會通過測試並被繪製,否則會被丟棄。
2.glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)能夠設定每個選項應該採取的行為:
sfail:模板測試失敗時採取的行為。
dpfail:模板測試通過,但深度測試失敗時採取的行為。
dppass:模板測試和深度測試都通過時採取的行為。
行為有下面這些:
模板測試有個很實用的例子就是找物體的輪廓。步驟:
1、繪製(需要新增輪廓的)物體之前,將模板函式設定為GL_ALWAYS,每當物體的片段被渲染時,將模板緩衝更新為1。
2、渲染物體。
3、禁用模板寫入以及深度測試。
4、每個物體縮放一點點。
5、用一個不同的片段著色器,輸出一個單獨的(邊框)顏色。
6、再次繪製物體,但只在它們片段的模板值不等於1時才繪製。
7、再次啟用模板寫入和深度測試。
glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
如果其中的一個測試失敗了,我們什麼都不做,我們僅僅保留當前儲存在模板緩衝中的值。如果模板測試和深度測試都通過了,那麼我們希望將儲存的模板值設定為參考值,參考值能夠通過glStencilFunc來設定,我們之後會設定為1。我們將模板緩衝清除為0,對物體所有繪製的片段,將模板值更新為1。
glStencilFunc(GL_ALWAYS, 1, 0xFF); // 所有的片段都應該更新模板緩衝
glStencilMask(0xFF); // 啟用模板緩衝寫入
normalShader.use();
DrawTwoContainers();
將模板函式設定為GL_NOTEQUAL,保證我們只繪製箱子上模板值不為1的部分,即只繪製箱子在之前繪製的箱子之外的部分。注意我們也禁用了深度測試,讓放大的箱子,即邊框,不會被地板所覆蓋。
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);// 禁止模板緩衝的寫入
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use();
DrawTwoScaledUpContainers();
完整的:
glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask(0x00); // 記得保證我們在繪製地板的時候不會更新模板緩衝
normalShader.use();
DrawFloor()
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF); DrawTwoContainers();
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00); glDisable(GL_DEPTH_TEST);
shaderSingleColor.use();
DrawTwoScaledUpContainers();
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);