模板測試與深度測試類似,但在渲染管線中發生在深度測試之前。模板測試也會丟棄掉一些片段,只是丟棄的片段數量比深度測試少。
同時該測試也是基於另一個緩沖區 --- 模板緩沖區(stencil buffer),同理該緩沖區也是由我們創建窗口庫創建的,我使用的庫是GLFW庫。該模板緩沖區中的模板值大小為8位所以每個像素有256種不同的模板值,就好比RGBA每一個中顏色有256 = 2^8種,所以總的大小為32 位.所以我們就根據模板值來決定是否丟棄或保留。
模板測試例子:

首先要使用模板測試,必須先開啟glEnable(GL_STENCIL_TEST),然后清除上一幀的模板緩沖glClear(GL_STENCIL_BUFFER_BIT)設置所有片段的模板值為0,然后開啟矩形片段用1填充。場景中的模板值為1的那些片段才會被渲染,其他的都被丟棄。
使用模板緩沖的過程:
- 開啟模板緩沖寫入。glEnable(GL_STENCIL_TEST)
- 渲染物體,更新模板緩沖。模板緩沖函數后面介紹
- 關閉模板緩沖寫入。glDisable(GL_STENCIL_TEST)
- 渲染(其他)物體,這次基於模板緩沖內容丟棄特定片段。
模板緩沖的寫入:
與深度測試函數管理glDepthMask()相似,模板緩沖有個函數glStencilMask()來設置是否可以對緩沖區進行寫入,其原理是給模板值設置一個位遮罩(Bitmask),它與模板值進行按位與(and)運算決定緩沖是否可寫。默認參數設置的位遮罩是0xFF(1),這樣就不會影響輸出,但是如果我們設置為0x00,所有寫入深度緩沖最后都是0,說明模板緩沖區不可寫入
配置模板測試函數:
void glStencilFunc(GLenum func, GLint ref, GLuint mask)函數有三個參數:
- func:設置模板測試操作。這個測試操作應用到已經儲存的模板值和
glStencilFunc的ref值上即判斷模板測試通過的條件,例如我func設置為GL_EQUAL,ref為1,那么模板測試通過的條件就是模板值等於1,可用的選項是:GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS,它們的語義和深度緩沖的相似。 - ref:指定模板測試的引用值。模板值會與這個值按func方式進行對比。
- mask:指定一個遮罩,在 模板測試 對比 引用值ref和儲存的模板值 前,對它們進行按位與(and)操作,初始設置為1。
glStencilFunc只描述了OpenGL對模板緩沖做什么,而不是描述我們如何更新緩沖區,此時就出現了glStencilOp()
void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)函數包含三個選項,我們可以指定每個選項的動作:
- sfail: 如果模板測試失敗將采取的動作。
- dpfail: 如果模板測試通過,但是深度測試失敗時采取的動作。
- dppass: 如果深度測試和模板測試都通過,將采取的動作。(此處有點迷惑就是深度測試不是發生在模板測試之后嗎,為什么會出現這個選項)

glStencilOp函數默認設置為 (GL_KEEP, GL_KEEP, GL_KEEP) ,所以任何測試的任何結果,模板緩沖都會保留它的值。總之使用glStencilFunc和glStencilOp,我們就可以指定在什么時候以及我們打算怎么樣去更新模板緩沖了,我們也可以指定何時讓測試通過或不通過。
模板測試的應用:制作物體的輪廓(就好比在策略游戲中鼠標點擊的物體身邊會出現光圈)
具體的步驟如下:
- 在繪制物體前,把模板方程設置為
GL_ALWAYS,用1更新物體將被渲染的片段。//使用glStencilOp選項中的GL_REPLACE將模板值設置成glStencilFunc中的ref值 - 渲染物體,寫入模板緩沖。
- 關閉模板寫入和深度測試。
- 每個物體放大一點點。
- 使用一個不同的片段着色器用來輸出一個純顏色。
- 再次繪制物體,但只是當它們的片段的模板值不為1時才進行。
- 開啟模板寫入和深度測試。
代碼如下:

代碼分析:
1.首先開啟模板測試
2.如果任何測試失敗我們值保持深度緩沖中當前所儲存着的值。如果模板測試和深度測試都成功了,我們就將儲存着的模板值替換為1,我們要用glStencilFunc(等到該函數執行時還功能才實現)來做這件事。
3.清楚上一幀的顏色緩沖,深度緩沖,模板緩沖值
4.繪制地板時禁止對模板緩沖的寫入
5,6.繪制地板
7.設置模板測試都能通過,且ref為1即將模板緩沖區值設置成1
8.設置模板緩沖區是可寫入的
9.繪制兩個容器
10.我們把模板方程設置為GL_NOTEQUAL,它保證我們只箱子上不等於1的部分,這樣只繪制前面繪制的箱子外圍的那部分
11.注意,我們也要關閉深度測試,這樣放大的的箱子也就是邊框才不會被地面覆蓋。
12.再次開啟深度緩沖。
