模板测试在深度测试之前。当片段着色器处理完一个片段后,模板测试会开始执行,被保留下来的才会进入深度测试。模板测试对应模板缓冲。
模板缓冲:每个模板值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);