模板测试(Stencil Test)出现原因
深度测试更多是为了解决如何区分物体前后遮挡关系,从而选择丢弃片段的测试。而模板测试,是通过设置片元模板缓冲区模板值和引用模板值,再按照我们设定的运算函数来丢弃某些片段,从而来达到我们想要的一些效果。是一种在深度测试之前丢弃片元的一个辅助方法,是为了达到我们预定的一些效果而添加的一个测试手段。
模板测试(Stencil Test)的位置
模板测试是在透明度测试之后,深度测试之前进行的,模板测试也有一个自己的缓冲区,叫做模板缓冲区。它和颜色缓冲区,深度缓冲区类似,模板缓冲区的模板值通常是8bit(一个字节),因此每个片段的模板值的范围是0-255。模板测试根据模板缓冲区中片段的模板值与设置的引用值,进行我们设置的运算式进行比较,如果没有通过测试,则丢弃该片段。
模板测试(Stencil Test)
openGL中开启模板测试
glEnable(GL_STENCIL_TEST);
清除缓冲区
一旦启用模板测试,在每一次渲染以前,我们需要像清空颜色缓冲区和深度缓冲区一样,清空模板缓冲区。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
通过模板函数,设置运算符和模板引用值
void glStencilFunc(GLenum func, GLint ref, GLuint mask)
//简单例子:它会告诉OpenGL,无论何时,一个片段模板值等于引用值1,片段就能通过测试被绘制了,否则就会被丢弃
glStencilFunc(GL_EQUAL, 1, 0xFF)
func:设置模板测试运算符,可用的选项是:GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、
GL_NOTEQUAL、GL_ALWAYS。它们的语义和深度缓冲的相似。
操作 | 描述 |
---|---|
GL_NEVER | 永远不通过测试 |
GL_ALWAYS | 永远通过测试 |
GL_LEQUAL | 模板值小于等于引用值时通过测试 |
GL_GEQUAL | 模板值大于等于引用值时用过测试 |
GL_EQUAL | 模板值等于引用值的时通过测试 |
GL_GREATER | 模板值大于引用值时通过测试 |
GL_NOTEQUAL | 模板值不等于引用值时通过测试 |
ref:引用值,用于和模板缓冲的模板值做运算比较的
mask:模板值在比较以前位遮罩,即模板值在和引用值ref做运算比较以前,需要先与mask遮罩值进行按位与,然后与后的结果再进行和引用值比较,一般设置0xFF
设置模板缓冲是否可写
OpenGL是通过设置位遮罩来控制写操作的,当我们在准备写入模板值之前,我们会将模板值与这个位遮罩进行与运算,一般我们使用0xFF和0x00就行,如果你有特殊的需求需要控制某一位的写入和其他不一样,你也可以自己设置。
//此时,模板值与它进行按位与运算结果是模板值,模板缓冲可写
glStencilMask(0xFF);
//此时,模板值与它进行按位与运算结果是0,模板缓冲不可写
glStencilMask(0x00);
如何更新缓冲区
与更新深度缓冲区不同,当通过了测试,并且缓冲区设置为可写模式,我们仍然可以设置如何更新模板缓冲区,以及什么时候更新缓冲区,具有更大的自由度。通过glStencilOp函数。
void 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一样将模板值+1,如果模板值已经是最大值则设为0 |
GL_DECR | 如果模板值不是最小值就将模板值-1 |
GL_DECR_WRAP | 与GL_DECR一样将模板值-1,如果模板值已经是最小值则设为最大值 |
GL_INVERT | 按位反转当前模板缓冲区值 |
示列代码:代表任何测试的任何结果,模板缓冲都会保留它的值。默认行为不会更新模板缓冲。
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
模板测试应用1–物体轮廓
大概步骤是
第一步
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF); //所有片段都要写入模板缓冲
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
开启深度测试 ,开启深度写入,设置深度运算为GL_LESS,即片元深度值<深度缓冲值通过测试
开启模板测试,设置模板运算函数为,所有片段都通过
写入模板缓冲为当通过深度测试且通过模板测试,用ref引用值1替换模板缓冲区
第二步
普通绘制正方形1
普通绘制正方形2
绘制完以后
此时能通过深度测试,绘制出来的片元,模板值都为1。
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
深度测试依旧开启,但关闭深度写入
模板测试依旧开启,但关闭模板写入
设置模板值为不等于1则通过
绘制放大一点的正方形1
绘制放大一点的正方形2
由于原本绘制的正方形区域片元模板值都为1,而我们设置的模板值不等于1通过,因此放大的正方形绘制时,只有大于原本大小的正方形的片元片段才能通过模板测试,也就是我们拿来作为描边的部分。
第三步
绘制完成以后,重新开启深度写入和模板写入
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);