OpenGL的模板缓冲

发布时间 2023-12-14 00:23:53作者: overxus

  注意看,利用OpenGL的模板缓冲,可以轻松实现很多酷炫的效果。当然,它用起来也很简单。下面就跟着博主小编,一起来看看吧!?

 

模板缓冲的使用

  假设有个大小为800x600的窗口,那么模板缓冲也是一个800x600的数组,每个值为一个字节,可以保存0x00~0xFF内的值,这就是模板缓冲的全部啦。

  之前介绍过深度测试,有了模板缓冲后,就可以启用模板测试。启用后,对于每一个待处理的点,只有通过模板测试后,才能绘制到窗口上。

 

  使用前,需要先启用模板测试:

glEnable(GL_STENCIL_TEST);

  

  对于模板缓冲,最基本的操作是将其全部清零:

glClear(GL_STENCIL_BUFFER_BIT);

  

  启用模板测试后,绘制物体到窗口的同时会写入模板缓冲。我们可以将某个值写入模板缓冲,但是这个值在写入前会与一个掩码进行逐位and运算,这个掩码的值可通过以下函数设置:

glStencilMask(GLuint mask);

最常用的掩码值是0x00和0xFF, 掩码的默认值为0xFF, 学过位运算的朋友都知道,0xFF不改变写入的值。

  

  写入模板的操作主要是通过以下函数指定:

glStencilFunc(GLenum func, GLint ref, GLuint mask);

模板缓冲中的值会与参考值ref执行某种运算,运算通过参数func指定,例如大于(GL_GREATER)、等于(GL_EQUAL)等等。如果比较的结果是true,则“通过了模板测试”。mask是一个掩码,用法和上面提到的基本相同。对于参数func,除了上面这些比较运算,还有两个另类的操作GL_ALWAYSGL_NEVER,从名字就可以看出,前者总是通过模板测试,后者总是无法通过模板测试。

  

  模板缓冲的写入还受到另一个函数的影响:

glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);

    sfail表示没有通过模板测试后应该做什么;

    dpfail表示模板测试通过,但深度测试没通过应该做什么;

    dppass表示模板和深度测试都通过后应该做什么。

以上描述中的“应该做什么”,其实是一个枚举值,常见的有GL_KEEP(保存当前值)、GL_REPLACE(替换为glStencilFunc中的参数ref)等等。这三个参数默认都是GL_KEEP, 即不修改模板缓冲的值。因此,如果想要写入模板缓冲,至少需要设置其中的某个参数。

以上函数中,深度测试与缓冲测试同时出现。我不太清楚这样设计的动机,不过从下面的两个例子可以看出,使用模板测试时,经常需要考虑深度缓冲。

  

  上面这些可能看上去还是让人云里雾里,下面来看两个使用模板测试的例子吧。

 

遮罩效果

  遮罩是一种常见的动画效果,例如射击游戏中的望远镜视角。下图展示了一红一绿两个正方形,其中绿色正方形在红色正方形的后面:

现在我想以红色正方形作为遮罩层,也就是说,只能看到被红色正方形遮挡的那一部分。

  启用模板测试后,确保模板测试可以写入模板缓冲:

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

这里设置为:只有模板和深度测试都通过后,才向模板缓冲写入值ref,所以最后一个参数设置为GL_REPLACE.

 

  初始时清空模板缓冲:

glClear(GL_STENCIL_BUFFER_BIT);

 

  在绘制红色正方形前,设置写入模板缓冲的条件:

glStencilFunc(GL_ALWAYS, 66, 0xFF);

这里,我们使用的是GL_ALWAYS, 即总是通过模板测试。因此,红色正方形在模板缓冲中的相应位置都会写入值66. (66是我随便设置的数字,我觉得这个数字还不错)。

  

  在绘制绿色正方形之前,先清空深度缓冲,否则这些被遮挡的绿色部分无法通过深度测试。

glClear(GL_DEPTH_BUFFER_BIT);

  

  现在,我们就可以根据模板缓冲来绘制啦!当前的模板缓冲中,红色正方形对应的位置被写入了66,我们只绘制模板缓冲中值为66的这部分区域:

glStencilFunc(GL_EQUAL, 66, 0xFF);

此函数表示,“在模板缓冲中对应位置为66”的像素点才能通过模板测试,最终被画出来。

 

  完成后,效果应该是下面这个样子:

  可以看到,只有通过红色正方形可以看到的部分被绘制出来。如果不希望出现左下角的红色部分,我能想到的简单解决方案是,在绘制红色正方形完成后,清空下颜色缓冲。

 

边缘绘制

  这是Learn OpenGL教程中的一个例子,用于绘制物体被选中时的的效果。

 

  首先启用模板缓冲,在绘制物体前,设置为“总是写入模板缓冲”,然后写入某个值,比如说写入1. 现在我们画一个机器人:

机器人画好后,机器人所占的这块区域在模板缓冲的对应位置都会写入1.

 

  最精彩的部分来了:我们把这个机器人稍微放大一些,然后仅在模板缓冲取值不为1的那些地方绘制像素点,写一个着色器,将这些点画成绿色。最后的效果如下:

上图的绿色部分就是模板缓冲中取值不为1的区域,里面的机器人所在的区域也就是模板缓冲中取值为1的区域。

 

小结

  模板缓冲其实很简单,可能我写得不是太清楚。就是这么简单的东西,能做出各种酷炫的视觉效果。如果有什么写得不对的地方,欢迎在评论区批评指正。我们下次,不见不散!