OpenGL模板缓存

上一章给大家介绍了深度缓存,而模板缓存是深度缓存的扩充,当你需要控制哪一个像素需要被渲染,哪一个像素需要被忽略时,模板缓存能够为你提供更多的方法。和深度缓存一样,模板缓存存储了所有像素的模板值,但是这次你必须手动控制这些值如何改变。记住,如果一个像素深度测试失败了的话,模板测试将不会再决定该像素是否绘制,而像素会反过来影响模板缓存中的值。

为了让大家对模板缓存有一个直观的认识,让我们来分析一下下面这个例子:

模板缓存实例

如上图所示:模板缓存首先被清0,我们的立方体将正常显示,然后我们将模板缓存中的一个矩形区域设置为1,我们的立方体在绘制时,我们将只绘制模板值为1的像素区域,从而达到控制像素绘制与否的目的。现在你对模板缓存的作用有了一个初步了解了,让我们来看看OpenGL中是如何使用它的:

和深度缓存一样,首先你要启动模板测试来让模板缓存生效,

启动模板测试

设置模板值

然后需要设置缓存中各个像素的模板值, 我们在确定模板的形状后,我们通常只需要调用一次绘制命令就可以达到利用模板裁剪画面的效果,如果你想像上面的例子那样创建一个矩形的裁剪区域,只需要绘制一个2D的矩形区域就行了。只不过在你绘制之前需要使用以下几个API来控制一下模板缓存的值:

首先是glStencilFunc,这个api是用来模板测试使用的比较函数以及参数的,参数如下:

func:比较函数

ref: 比较函数用来比较的参数值。

mask: 掩码,如果模板缓存包含了s个位平面,那么mask参数中较低的s个位数据将分别于模板缓存中的值,以及ref值进行位与操作,然后再进行具体的比较。

第一个参数中的比较函数可以是以下任意一种:

GL_NEVER:无论模板值为何值,都不能通过模板测试

GL_LESS:测试模板值是否小于ref值

GL_LEQUAL:测试模板值是否小于等于ref值

GL_GREATER:测试模板值是否大于ref值

GL_GEQUAL:测试模板值是否大于等于ref值

GL_EQUAL:测试模板值是否等于ref值

GL_NOTEQUAL:测试模板值是否不等于ref值

GL_ALWAYS:无论模板值为何值,总是能通过模板测试。

例如:如果你不想让小于2的模板值通过模板测试,你只需要这样调用:

glStencilFunc调用示例

然后是glStencilOp,这个api是用来指定深度测试以及模板测试以后对模板值的操作,参数如下:

sfail: 模板测试失败后执行的操作

dpfail: 模板测试通过但深度测试不通过时候执行的操作。

dppass: 模板测试和深度测试都通过后,或者深度测试未开启时执行的操作。

以上三个参数的值可以是以下任意一种:

GL_KEEP: 保持当前值

GL_ZERO: 将模板值清0

GL_REPLACE: 模板值将会被替换为glStencilFunc中的ref参数值。

GL_INCR: 如果模板值小于最大值,模板值将会+1

GL_INCR_WRAP: 和GL_INCR一样,只是遇到最大值时,模板值会清0。

GL_DECR:如果模板值大于0,模板值将会-1。

GL_DECR_WRAP: 和GL_DECR一样,只是当模板值减到0时,会变为最大值。

GL_INVERT: 将模板值按位取反。

最后,glStencilMask可以为模板缓存设置掩码,由于模板缓存中每个模板值是一个无符号的整形数,因此我们的掩码也是一个0-0xFFFFFFFF范围内的数,每个模板值都会与掩码做与运算后再使用,默认掩码为0xFFFFFFFF。

现在回到我们的例子,我们要把一个矩形区域的模板值设置为1,我们需要进行如下操作:

模板缓存使用示例

这里我们可以看到,我们通过glStencilFunc函数让任意模板值都能通过模板测试,这样如果深度测试也通过的话就会根据glStencilOp的第三个参数设置的GL_REPLACE将模板值都替换为ref参数值1,从而达到了将所有模板值设置为1的目的。而图中的矩形区域并不需要真正的绘制,它只需要用来指定哪些像素的模板值生效,这样就不需要我们去逐像素的去设置模板缓存值了。

屏蔽颜色和深度缓存

glColorMask函数允许你指定哪些数据输入颜色缓存,4个参数分别对应RGBA值,这里我们将4个参数都置为GL_FALSE,意味着所有颜色数据都不会输入到颜色缓存中。而深度缓存需要另外调用glDepthMask并传入GL_FALSE参数来屏蔽深度值。这样我们矩形区域外的所有像素的颜色和深度都被屏蔽了,所以我们的绘制就不会影响到这些像素。这样操作操作比调用glClear来清除颜色和深度缓存要干净得多。

绘制中使用模板值

在了解了如何设置模板缓存后,使用模板缓存变得非常简单。你只需要通过一个测试函数来决定哪些像素需要绘制,然后将之前禁用的深度和颜色重新启用就可以了。

模板测试函数

如果你调用这个方法来设置了测试函数,只有模板缓存中值为1的像素能通过这个测试。一个像素只有在通过深度测试和模板测试后才能绘制,所以没有必要去设置glStencilOp。在例子中,只有那个矩形区域中的模板值被设置为1,所以只有在这个区域中的像素会被绘制。

设置掩码

一个小细节需要注意的是,我们在绘制我们的立方体时,仍会影响到模板缓存中的值,为了避免这种影响,我们将模板缓存的掩码设置为0。这样一来,我们以后的任何写入操作都会被屏蔽掉。

镜面反射的例子

现在我们在我们绘制的立方体下方加入一个反射镜面,首先在立方体的顶点数组中添加如下数据:

顶点数组

然后,在绘制立方体过后单独绘制这个镜面:

绘制镜面

现在我们创建一个反射效果,只需要将原有的立方体向下平移并相对Z轴翻转就行了:

创建反射

这里将反射面的颜色设置为黑色,这样就不会有纹理图片能够通过这个反射面。绘制效果如下:

平移旋转后的立方体

有两个值得一提的地方:

1:下方的立方体被反射面遮住是因为未能通过上一章我们提到的深度测试。

2:超出反射面范围的立方体依然能够被看见

第一个问题很好解决,只需要临时屏蔽掉向深度缓存写入值就可以了:

屏蔽深度缓存

第二个问题通过控制深度缓存似乎不好解决,我们需要屏蔽掉反射面范围之外的那些像素,所以我们的模板缓存就可以一展身手了!

我们可以将整个绘制过程细分为以下几个阶段,以便我们分析每个阶段应该做什么:

1.绘制上方的立方体

2.打开模板测试,再将我们需要绘制区域的模板值写成1。

3.绘制反射面

4.设置模板函数,让模板值为1的像素通过模板测试。

5.绘制一个颠倒的立方体。

6.关闭模板测试。

绘制代码如下:

镜面反射绘制代码

最后我们只需要将反射面上的图像灰化,这需要在我们的片段着色器中实现,overrideColor是用来灰化图案的颜色:

灰化反射图案

然后在绘制我们的反射立方体时传入一个灰度值,uniColor是glGetUniformLocation的返回值:

传入灰度值

大功告成!我们只用最基本的OpenGL的API就可以实现一个看似很复杂的效果,很神奇吧!

最终结果

最后,如果需要完整代码,或者有任何疑问,请联系我们,我们的QQ群是:280689979

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容