OpenGL-Stenciltest
1. 模板测试
模板缓冲在片段着色器处理完之后开始执行,被保留的片段则会继续进入深度测试。GLFW会自动帮助我们创建模板缓冲,但是我们应当关注窗口库的文档,检查它是否会帮助我们创建模板缓冲。
模板缓冲允许我们将模板缓冲设定成为一个特定的值,在渲染的时候修改模板缓冲的内容。在每一个渲染迭代中,读取这些值并决定丢弃还是保留某个片段。
形象点来讲的话,模板缓冲就像是印刷时的制版,每次制作好特定的模板,然后套用模板在屏幕上进行绘制。我们可以操作的就是什么时候采用模板与采用什么样子的模板,已经绘制的时候应当怎样取舍。
我们通过GL_STENCIL_TEST
来开启模板测试
glEnable(GL_STENCIL_TEST);
并且我们也需要在每次迭代前清除模板缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
我们还可以设置位掩码,它会与将要写入缓冲的模板值进行与(AND)运算。默认位掩码所有位都是1。
glStencilMask(0xFF); // 每一位写入模板缓冲时保持原样
glStencilMask(0x00); // 每一位写入模板缓冲时变成0(禁用写入)
2.模板函数
我们可以通过glStencilFunc
和glStencilOp
来配置模板测试
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。
举个栗子:
glStencilFunc(GL_EQUAL, 1, 0xFF);
这会告诉OpenGL,当片段的模板值等于参考值1的时候,片段会通过测试从而进入下一步深度测试中,否则就会被丢弃。
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
其中也是有三个参数
sfail
: 模板测试失败时采取的动作。dpfail
: 模板测试通过,但深度测试失败时采取的动作。dppass
: 模板测试和深度测试都通过时采取的动作。
动作 | 内容 |
---|---|
GL_KEEP | 保持当前存储的模板值 |
GL_ZERO | 将模板值设置为0 |
GL_REPLACE | 将模板值设置为ref 值 |
GL_INCR | 如果模板值小于最大值则将模板值加1 |
GL_INCR_WRAP | GL_INCR 且模板值超过最大值则归零 |
GL_DECR | 如果模板值大于最小值则将模板值减1 |
GL_DECR_WRAP | GL_DECR 且模板值小于0则设置为最大值 |
GL_INVERT | 按位翻转当前的模板缓冲值 |
默认的动作都是GL_KEEP
,也就是不论测试结果如何模板缓冲都会被保留。因此通过使用glStencilFunc
和glStencilOp
我们就可以精确地指定更新模板缓冲的时机与行为了。
3.物体轮廓
下面我们通过实践来更加深刻地体会模板缓冲,也就是为一个物体描出它的轮廓。
首先我用文章中的方法为之前案例中Assimp导入的模型进行了描边,从效果可以看出,这种描边方式对于不规则边框的物体来说就有些差强人意了。我们可能需要进一步寻找更加合适的描边实现方式。
通过这个例子的实践,我个人对于模板测试的理解,就好像套版印刷一样,而模板缓冲就是我们的“版”,我们可以设置并开启模板测试,从而制出我们需要的“版”。然后设置模板缓冲为只读,借助“版”来实现一些特殊的视觉效果。