OpenGL-17-01-模版测试

OpenGL-17-01-模版测试

基本说明

模版测试执行时机:当片段着色器处理完一个一个片段后,模版测试就会进行。

模版测试和深度测试一样,是有可能丢弃片段的。

模版测试是根据模版缓冲进行的。

如何使用模版测试

通过这行代码启动模版测试

glEnable(GL_STENCIL_TEST);

注意,和颜色和深度缓冲一样,你也需要在每次迭代之前清除模板缓冲。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

和深度测试的glDepthMask函数一样,模板缓冲也有一个类似的函数。glStencilMask允许我们设置一个位掩码(Bitmask),它会与将要写入缓冲的模板值进行与(AND)运算。默认情况下设置的位掩码所有位都为1,不影响输出,但如果我们将它设置为0x00,写入缓冲的所有模板值最后都会变成0.这与深度测试中的glDepthMask(GL_FALSE)是等价的。

glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)

大部分情况下你都只会使用0x00或者0xFF作为模板掩码(Stencil Mask),但是知道有选项可以设置自定义的位掩码总是好的。

通过这个函数,我们可以控制是否写入模版缓存

通过函数控制模版测试

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。

这个函数用来控制通过模版测试的条件,也就是把模版缓存值与函数设置的ref进行比较,并判断条件

这里注意和深度测试区别:深度测试是将当前片段的z值与缓存值进行比较,而模版测试中,片段本身没有值

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按位翻转当前的模板缓冲值

通过这个函数,我们可以控制,出现对应的情况时,如何更新模版缓存的值。只用通过这个函数,才能更新这个模版缓存值。默认的行为下(GL_KEEP, GL_KEEP, GL_KEEP)也就是默认情况下无法更新模版缓存值。

注意:如果你想要更新模版缓存的值,那么这个函数你不能使用默认情况,并且,模版缓存位掩码必须位0xFF,否则也同样无法写入缓存。

也就是,设置允许写入模版缓存,如果设置了禁用写入,那么即使我们设置了上面这个函数对模版缓存的更新方式也无效

glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)

物体轮廓

现在,我们已经能够精确的去控制模版测试了

想要在渲染时渲染出物体轮廓,我们的思路是:

首先开启模版测试,并设置缓存更新方式

    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

我们想要渲染第一个物体时,让他通过深度测试并且通过模版测试时使用1来更新模版缓存值

这样一来,这个物体的片段上的模版缓存值就为1了。

其次,然后我们禁用模版写入,并且重新设置缓存通过条件,我们设置为模版缓存值不等于1时,通过。为什么这样做?我们马上会解释。这一次我们放大物体,没错,将其微微放大,那么,这次的片段就会覆盖之前物体的片段,但是之前物体的片段的模版缓存值已经设置为1了,而我们却将模版测试的条件设置为不等于1,因此所有和之前物体位置重复的片段都会被丢弃,只会渲染最外层的边界,而这就是我们想要的物体轮廓,然后我们使用固定的颜色去渲染这个轮廓,而不是使用物体颜色,不然看起来就不是轮廓了。

代码实现

首先开启模版测试

并设置更新模版缓存值的方式

    glEnable(GL_STENCIL_TEST);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
// floor,不写入地面的模型缓存
glStencilMask(0x00);
//渲染地面

渲染地面时,我们不写入模版缓存(禁用模版缓存写入),因为我们要画的是物体的边框,如果在渲染地面的时候写入模版缓存,边框就不对了。

然后,我们在渲染第一个物体时,首先设置模版测试通过条件为,直接通过,并设置值为1

由于我们在上面已经设置了,如果通过深度测试和模版测试,就会将ref写入模版缓存,而此时,就会将1写入模版缓存

glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
//渲染第一个物体

然后,我们在渲染第二个物体之前,禁用模版缓存写入,并更改模版测试通过条件即可

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//设置不等于1则通过测试
glStencilMask(0x00);//禁用模版写入
//渲染第二个物体(也就是轮廓)

最后,重新开启模版缓存写入

glStencilMask(0xFF);

这里要注意一点,模版缓存如果禁用了,那么对于模版缓存的清空(因为清空的本质就是把缓存值更改为0)也会被禁用,也就是,如果最后我们不重新开启模版缓存写入,会导致第二次循环无法清空模版缓存,就会出现不可预料的问题。

还有就是,在第一次循环没有任何模版缓存写入时,所有片段的缓存值为0

如果不设置模版测试的条件,默认未知。所以最好循环开始前手动设置模版测试条件。另外在循环结束时最好恢复最初的模版测试条件

glStencilFunc(GL_ALWAYS,1,0xFF);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值