作者:位元組流動
來源:
https://blog.csdn.net/Kennethdroid/article/details/102533260OpenGL ES 模闆測試

模闆測試與深度測試類似,主要作用是利用模闆緩沖區(Stencil Buffer)所儲存的模闆值決定目前片段是否被丢棄,且發生于深度測試之前。
圖檔來源:
https://learnopengl.com/Advanced-OpenGL/Stencil-testing模闆測試一般步驟:
- 啟用模闆測試,開啟模闆緩沖寫入
;glStencilMask(0xFF)
- 執行渲染操作,更新模闆緩沖區;
- 關閉模闆緩沖寫入
glStencilMask(0x00)
- 執行渲染操作,利用模闆緩沖區所儲存的模闆值确定是否丢棄特定片段。
啟用模闆測試
glEnable(GL_STENCIL_TEST);
, 清空模闆緩沖區
glClear( GL_STENCIL_BUFFER_BIT);
。
控制模闆緩沖區是否可以進行寫入:
// 0xFF == 0b11111111
// 模闆值與它進行按位與運算結果是模闆值,模闆緩沖可寫
glStencilMask(0xFF);
// 0x00 == 0b00000000 == 0
// 模闆值與它進行按位與運算結果是0,模闆緩沖不可寫
glStencilMask(0x00);
模闆測試的配置函數
glStencilFunc
和
glStencilOp
void glStencilFunc(GLenum func, GLint ref, GLuint mask);
- func:設定模闆測試操作。這個測試操作應用到已經儲存的模闆值和
glStencilFunc
的 ref 值上,可用的選項是:
GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS ;
- ref:指定模闆測試的引用值。模闆緩沖區中的模闆值會與這個值對比;
- mask:指定一個遮罩,在模闆測試對比引用值和儲存的模闆值前,對它們進行按位與(and)操作,初始設定為 1 。
glStencilFunc(GL_EQUAL, 1, 0xFF);
// 表示當一個片段模闆值等于(GL_EQUAL)引用值1,片段就能通過測試被繪制了,否則就會被丢棄。
glStencilFunc(GL_ALWAYS, 1, 0xFF);
// 表示所有片段模闆測試總是通過。
glStencilOp
主要用于控制更新模闆緩沖區的方式。
void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
- sfail: 如果模闆測試失敗将如何更新模闆值;
- dpfail: 如果模闆測試通過,但是深度測試失敗時将如何更新模闆值;
- dppass: 如果深度測試和模闆測試都通過,将如何更新模闆值。
參數可選操作:
操作 | 描述 |
GL_KEEP | 保持現有的模闆值 |
GL_ZERO | 将模闆值置為 0 |
GL_REPLACE | 将模闆值設定為用 glStencilFunc 函數設定的ref值 |
GL_INCR | 如果模闆值不是最大值就将模闆值 +1 |
GL_INCR_WRAP | 與 GL_INCR 一樣将模闆值 +1 ,如果模闆值已經是最大值則設為 0 |
GL_DECR | 如果模闆值不是最小值就将模闆值 -1 |
GL_DECR_WRAP | 與 GL_DECR 一樣将模闆值 -1 ,如果模闆值已經是最小值則設為最大值 |
GL_INVERT | 按位反轉目前模闆緩沖區的值 |
繪制物體輪廓是模闆測試的常見應用,其步驟一般如下:
- 啟動深度測試和模闆測試,清空模闆緩沖和深度緩沖;
- 在繪制物體前,設定
,用 1 更新物體将被渲染的片段對應的模闆值;glStencilFunc(GL_ALWAYS, 1, 0xFF);
- 渲染物體,寫入模闆緩沖區;
- 關閉模闆寫入和深度測試;
- 将物體放大一定比例;
- 使用一個不同的片段着色器用來輸出一個純顔色(物體輪廓顔色);
- 再次繪制物體,設定
當片段的模闆值不為 1 時,片段通過測試進行渲染;glStencilFunc(GL_NOTEQUAL, 1, 0xFF)
- 開啟模闆寫入和深度測試。
關鍵實作的代碼片段:
//啟動深度測試和模闆測試,清空模闆和深度緩沖
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF); //所有片段都要寫入模闆緩沖
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);//若模闆測試和深度測試都通過了,将片段對應的模闆值替換為1
glStencilMask(0xFF);
//繪制物體
glBindVertexArray(m_VaoId);
glUseProgram(m_ProgramObj);
glUniform3f(m_ViewPosLoc, 0.0f, 0.0f, 3.0f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);
UpdateMatrix(m_MVPMatrix, m_ModelMatrix, m_AngleX, m_AngleY , 1.0, glm::vec3(0.0f, 0.0f, 0.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glUniformMatrix4fv(m_ModelMatrixLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//當片段的模闆值不為 1 時,片段通過測試進行渲染
//禁用模闆寫入和深度測試
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
//繪制物體輪廓
glBindVertexArray(m_VaoId);
glUseProgram(m_OutlineProgramObj);
//放大 1.05 倍
UpdateMatrix(m_MVPMatrix, m_ModelMatrix, m_AngleX, m_AngleY, 1.05, glm::vec3(0.0f, 0.0f, 0.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glUniformMatrix4fv(m_ModelMatrixLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
//開啟模闆寫入和深度測試
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
另外需要注意,在使用 GLSurfaceView 時,新的 API 預設沒有配置模闆緩沖區,需要使用
setEGLConfigChooser
配置模闆緩沖區。
public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setEGLContextClientVersion(2);
mGLRender = new MyGLRender();
/*If no setEGLConfigChooser method is called,
then by default the view will choose an RGB_888 surface with a depth buffer depth of at least 16 bits.*/
setEGLConfigChooser(8, 8, 8, 8, 16, 16);//最後 2 個參數表示分别配置 16 位的深度緩沖區和模闆緩沖區
setRenderer(mGLRender);
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
利用模闆測試繪制物體輪廓效果圖 聯系與交流
技術交流/擷取源碼可以添加我的微信:Byte-Flow
視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。