片段着色器处理一个片段后,就该模板测试执行了,跟深度测试类似,可能会丢弃片段。被保留的片段会进入深度测试,可能会丢弃更多的片段。
模板测试时根据模板缓冲进行的。一个模板缓冲中,每个模板值是8位的,所以每个像素、片段一共有256种不同的模板值。可以把模板值设置为我们想要的值,
当某一个片段有某一个模板值的时候,可以选择丢弃或保留这个片段。
补充:每个窗口库都需要为你配置一共模板缓冲,但是GLFW自动做了,不需要告诉GLFW来创建一个,但是其他的窗口库可能不会默认创建一个模板库,需要查看库文档。
举例:模板缓冲都清空为0,之后在模板缓冲中使用1填充一个图形,则场景中的片段将会只在片段的模板值为1的时候被渲染。
步骤:
1.启动模板缓冲的写入
2.渲染物体,更新模板缓冲的内容
3.禁用模板缓冲的写入
4.渲染其他物体,根据模板缓冲的内容丢弃特定片段
使用glEnable来启用模板测试,里面参数不一样,使用:GL_STENCIL_TEST
和颜色,深度缓冲一样,需要在每次迭代之前清除模板缓冲:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
和深度测试的glDepthMask函数类似,模板缓冲的是:glStencilMask允许设置一个位掩码,会将写入缓冲的模板值进行与运算,默认情况下设置的位掩码所有位都是1,
不影响输出,但如果设置为0x00,写入缓冲的所有模板值都变成0,与深度测试中glDepthMask(GL_FALSE)是等价的:
glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)
也可以自定义位掩码,百度一下~
模板函数是用来配置模板测试的:glStencilFunc和glStencilOp
glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三个参数:
func:设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。
可用的选项有:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。
ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。
mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。
举例:glStencilFunc(GL_EQUAL, 1, 0xFF)
告诉OpenGL,只要一个片段的模板值等于(GL_EQUAL)1,则片段将会通过测试并被绘制,否则会被抛弃。
上述函数只是描述了:应该对模板缓冲内容做什么,没有告诉如何更新缓冲。
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一样,但如果模板值超过了最大值则归零
GL_DECR 如果模板值大于最小值则将模板值减1
GL_DECR_WRAP 与GL_DECR一样,但如果模板值小于0则将其设置为最大值
GL_INVERT 按位翻转当前的模板缓冲值
默认情况下glStencilOp是设置为(GL_KEEP, GL_KEEP, GL_KEEP)的,所以不论任何测试的结果是如何,模板缓冲都会保留它的值。
默认的行为不会更新模板缓冲,所以如果你想写入模板缓冲的话,你需要至少对其中一个选项设置不同的值。
模板测试有一个特性是:物体轮廓。就是给每个物体在它周围创建一个很小的有色边框。绘制 放大版本的物体 中模板测试通过的部分,就是物体的边框位置。
主要使用模板缓冲丢弃了 放大版本中 属于元物体片段的部分。步骤如下:
首先创建一个片段着色器,输出一个边框颜色,将这个着色器命名为shaderSingleColor:
void main()
{
FragColor = vec4(0.04, 0.28, 0.26, 1.0);
}
只给特定的物体加边框,首先绘制不需要加边框的物体,再绘制要加边框的物体,写入模板缓冲,之后再绘制放大要加边框的物体,并丢弃覆盖了之前绘制的箱子
片段的那些片段。
其次,启用模板测试,并设置测试通过或失败时的行为:
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
其中一个测试失败,则什么都不做,只保留当前存储在模板缓冲中的值。如果模板测试和深度测试都通过了,那么希望将存储的模板值设为参考值,参考值能够
通过glStencilFunc来设置
下面,将模板缓冲清除为0,对箱子中所有绘制的片段,将模板值更新为1:
glStencilFunc(GL_ALWAYS, 1, 0xFF);————所有片段都更新模板缓冲,保证每个片段都会将模板缓冲的模板值更新为1
glStencilMask(0xFF);————启用模板缓冲写入
normalShader.use();
DrawTwoContainers();
则通过上述代码后,要被加边框的物体的模板值都更新为1,下面需要禁用模板缓冲的写入,绘制放大的物体:
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00); // 禁止模板缓冲的写入
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use();
DrawTwoScaledUpContainers();
设置GL_NOTEQUAL,保证只绘制物体上模板值不为1的部分,当然也禁用了深度测试,让边框不会被其他物体所覆盖。
当然,在完成后需要重新启用深度缓冲
整体步骤代码如下:
glEnable(GL_STENCIL_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);
359

被折叠的 条评论
为什么被折叠?



