0评论

OpenGL之模板测试

文章来自https://www.cnblogs.com/ctin/p/6182001.html 2019-03-12 26浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏开发行业精英群711501594

模板测试与深度测试类似,但在渲染管线中发生在深度测试之前。模板测试也会丢弃掉一些片段,只是丢弃的片段数量比深度测试少。

同时该测试也是基于另一个缓冲区 --- 模板缓冲区(stencil buffer),同理该缓冲区也是由我们创建窗口库创建的,我使用的库是GLFW库。该模板缓冲区中的模板值大小为8位所以每个像素有256种不同的模板值,就好比RGBA每一个中颜色有256 = 2^8种,所以总的大小为32 位。所以我们就根据模板值来决定是否丢弃或保留。

模板测试例子:

首先要使用模板测试,必须先开启glEnable(GL_STENCIL_TEST),然后清除上一帧的模板缓冲glClear(GL_STENCIL_BUFFER_BIT)设置所有片段的模板值为0,然后开启矩形片段用1填充。场景中的模板值为1的那些片段才会被渲染,其他的都被丢弃。

使用模板缓冲的过程:

  • 开启模板缓冲写入。glEnable(GL_STENCIL_TEST)
  • 渲染物体,更新模板缓冲。模板缓冲函数后面介绍
  • 关闭模板缓冲写入。glDisable(GL_STENCIL_TEST)
  • 渲染(其他)物体,这次基于模板缓冲内容丢弃特定片段。

模板缓冲的写入:

与深度测试函数管理glDepthMask()相似,模板缓冲有个函数glStencilMask()来设置是否可以对缓冲区进行写入,其原理是给模板值设置一个位遮罩(Bitmask),它与模板值进行按位与(and)运算决定缓冲是否可写。默认参数设置的位遮罩是0xFF(1),这样就不会影响输出,但是如果我们设置为0x00,所有写入深度缓冲最后都是0,说明模板缓冲区不可写入

配置模板测试函数:

void glStencilFunc(GLenum func, GLint ref, GLuint mask)函数有三个参数:
  • func:设置模板测试操作。这个测试操作应用到已经储存的模板值和glStencilFunc的ref值上即判断模板测试通过的条件,例如我func设置为GL_EQUAL,ref为1,那么模板测试通过的条件就是模板值等于1,可用的选项是:GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS,它们的语义和深度缓冲的相似。
  • ref:指定模板测试的引用值。模板值会与这个值按func方式进行对比。
  • mask:指定一个遮罩,在 模板测试 对比  引用值ref和储存的模板值  前,对它们进行按位与(and)操作,初始设置为1。

glStencilFunc只描述了OpenGL对模板缓冲做什么,而不是描述我们如何更新缓冲区,此时就出现了glStencilOp()

void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)函数包含三个选项,我们可以指定每个选项的动作:
  • sfail: 如果模板测试失败将采取的动作。
  • dpfail: 如果模板测试通过,但是深度测试失败时采取的动作。
  • dppass: 如果深度测试和模板测试都通过,将采取的动作。(此处有点迷惑就是深度测试不是发生在模板测试之后吗,为什么会出现这个选项)
glStencilOp函数默认设置为 (GL_KEEP, GL_KEEP, GL_KEEP) ,所以任何测试的任何结果,模板缓冲都会保留它的值。总之使用glStencilFunc和glStencilOp,我们就可以指定在什么时候以及我们打算怎么样去更新模板缓冲了,我们也可以指定何时让测试通过或不通过。

模板测试的应用:制作物体的轮廓(就好比在策略游戏中鼠标点击的物体身边会出现光圈)

具体的步骤如下:

1.在绘制物体前,把模板方程设置为GL_ALWAYS,用1更新物体将被渲染的片段。//使用glStencilOp选项中的GL_REPLACE将模板值设置成glStencilFunc中的ref值

2.渲染物体,写入模板缓冲。

3.关闭模板写入和深度测试。

4.每个物体放大一点点。

5.使用一个不同的片段着色器用来输出一个纯颜色。

6.再次绘制物体,但只是当它们的片段的模板值不为1时才进行。

7.开启模板写入和深度测试。

代码如下:

代码分析:

1.首先开启模板测试

2.如果任何测试失败我们值保持深度缓冲中当前所储存着的值。如果模板测试和深度测试都成功了,我们就将储存着的模板值替换为1,我们要用glStencilFunc(等到该函数执行时还功能才实现)来做这件事。

3.清楚上一帧的颜色缓冲,深度缓冲,模板缓冲值

4.绘制地板时禁止对模板缓冲的写入

5.6.绘制地板

7.设置模板测试都能通过,且ref为1即将模板缓冲区值设置成1

8.设置模板缓冲区是可写入的

9.绘制两个容器

10.我们把模板方程设置为GL_NOTEQUAL,它保证我们只箱子上不等于1的部分,这样只绘制前面绘制的箱子外围的那部分

11.注意,我们也要关闭深度测试,这样放大的的箱子也就是边框才不会被地面覆盖。

12.再次开启深度缓冲。