模版测试是图形渲染管线中位于片元着色器之后深度测试之前的一个阶段,其主要用途为实现一些效果:物体轮廓、在一个后视镜中绘制纹理、使用阴影体积(Shadow Volume)的模版缓冲技术渲染实时阴影。
而许多RTS游戏中常见到的物体被框选的效果,也是通过模版测试来实现的。例如下图中,绿色的线条表现了物体的轮廓。
模版测试的工作机制为将顶点着色器得到的颜色缓冲(Color Buffer)与一个与其尺寸相同的模版缓冲(Stencil Buffer)进行特定的操作,例如下图中,只有当模版值为1的时候,颜色缓冲中的片段才会被渲染。
有了模版测试这样的工具,若我们将物体片段所在位置的模版缓冲值置为1,再使用轮廓颜色绘制一个放大过后的物体并只在模版缓冲值不为1的时候绘制,即可得到轮廓效果。
我们首先启用模板测试,并设置测试通过或失败时的行为:
glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
// 1st. render pass, draw objects as normal, writing to the stencil buffer
// --------------------------------------------------------------------
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
normalShader.use();
DrawTwoContainers();
...
...
使用normalShader绘制一遍物体,其中,glStencilFunc(GLenum func, GLint ref, GLuint mask)函数控制OpenGL应该对模版缓冲内容做什么,或者说颜色缓冲会怎样受到模版缓冲的影响,例如:
glStencilFunc(GL_EQUAL, 1, 0xFF)
表示只要片段的模版值等于(GL_EQUAL)参考值1,片段将会通过测试并被绘制,否则会被丢弃。
而glStencilMask函数是模版值即将写入模版缓冲前的一个操作,其起到的作用大多数为启用和禁用模版缓冲写入,在这里我们传入的参数是0xFF,其代表有物体片段的地方模版值都将设为1。
// 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing.
// -----------------------------------------------------------------------------------------------------------------------------
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use();
...
glStencilMask(0xFF);
使用shaderSingleColor这个 shader再次绘制物体时,模版缓冲中已经有了前一个normalShader绘制时写入的模版值了,故
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
将使得模版值不等于1的片段通过模版测试,随后
glStencilMask(0x00);
LearnOpenGL:https://learnopengl-cn.github.io/04 Advanced OpenGL/02 Stencil testing/