您的位置:首页 > 运维架构

OpenGLES demo - 16. 蒙板 Stencil

2014-08-18 21:24 381 查看
在OpenGLES的创建中,和颜色缓冲以及深度缓冲相比,蒙板缓冲(Stencil Buffer)不是一个必要的,在实际应用中也出现的少,甚至很多程序不使用蒙板。但是一些特殊的效果有时候会依赖于蒙板缓冲,常见的效果是实现一个镜子,所以我们今天讲一讲简单的蒙板。

蒙板是什么?可以看做是一个像素级别的Mask。它可以按照需求,在屏幕上任意地方、任何形状的一块区域设置一个值,这块区域就是蒙板。然后在接下来的渲染中,开发者可以通过参数的配置,来决定最终渲染的图像是只在这块区域显示,或者是不在这块区域显示。而设置蒙板的这个值,通常是一个8位的整型值。蒙板的原理很简单,下面主要看下在IOS上面怎么实现蒙板的渲染吧。

看过代码的同学应该知道,最开始在AppDelegate里面创建OpenGLESView的时候会设置值,这里讲depth改成24,stencil由0改为8 [_glView Initialize:8 GreenSize:8 BlueSize:8 AlphaSize:8 DepthSize:24 StencilSize:8 SamplesSize:0];
在创建深度缓冲的时候,由原来的GL_DEPTH_COMPONENT16就改为了GL_DEPTH24_STENCIL8_OES<p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; color: rgb(61, 29, 129);"><span style="color: #000000">    </span>glGenRenderbuffers<span style="color: #000000">(</span><span style="color: #272ad8">1</span><span style="color: #000000">, &</span><span style="color: #4f8187">_depthRenderBuffer</span><span style="color: #000000">);</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; color: rgb(61, 29, 129);"><span style="color: #000000">    </span>glBindRenderbuffer<span style="color: #000000">(</span><span style="color: #78492a">GL_RENDERBUFFER</span><span style="color: #000000">, </span><span style="color: #4f8187">_depthRenderBuffer</span><span style="color: #000000">);</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; min-height: 15px;">    </p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New';">    <span style="color: #703daa">GLuint</span> size = <span style="color: #272ad8">0</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; color: rgb(120, 73, 42);"><span style="color: #000000">    size = </span>GL_DEPTH24_STENCIL8_OES<span style="color: #000000">;</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; min-height: 15px;">
</p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New';">    <span style="color: #3d1d81">glRenderbufferStorage</span>(<span style="color: #78492a">GL_RENDERBUFFER</span>, size, <span style="color: #4f8187">_viewWidth</span>, <span style="color: #4f8187">_viewHeight</span>);</p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; min-height: 15px;">    </p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: 'Courier New'; color: rgb(61, 29, 129);"><span style="color: #000000">    </span>glBindRenderbuffer<span style="color: #000000">(</span><span style="color: #78492a">GL_RENDERBUFFER</span><span style="color: #000000">, </span><span style="color: #272ad8">0</span><span style="color: #000000">);</span></p>
一般情况下,stencil和depth是公用一块内存,所以depth和stencil的创建也是一起的。

在创建后缓冲的FBO的时候,我们原来已经设置了深度缓冲glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
在这里,由于_depthRenderBuffer里面也包含了Stencil,所以我们还需要再用_depthRenderBuffer去设置一次stencil的attachmentglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
到这里,我们初始话蒙板缓冲就结束了,下面开始渲染物体了。用到的Shader很简单,Vertex shader接受一个坐标和一个颜色值,讲颜色值传给Fragment shader,Fragment shader显示这个颜色值。深度缓冲清空为1.0,深度比较为GL_LESS, 我们先在屏幕中心画一个深度为0.0的绿色正方形 float stencilArea[] =
{
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
};

glVertexAttribPointer(aLocPos, 4, GL_FLOAT, 0, sizeof(float)*8, &stencilArea[0]);
glVertexAttribPointer(aLocColor, 4, GL_FLOAT, 0, sizeof(float)*8, &stencilArea[4]);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);


然后在左边画一个大的红色三角形,深度为-0.5,这样,红色三角形和绿色正方形就有一部分重叠,并覆盖正方形 float quadArea[] =
{
-1.0f, -1.0f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
};

glVertexAttribPointer(aLocPos, 4, GL_FLOAT, 0, sizeof(float)*8, &quadArea[0]);
glVertexAttribPointer(aLocColor, 4, GL_FLOAT, 0, sizeof(float)*8, &quadArea[4]);

glDrawArrays(GL_TRIANGLES, 0, 3);



上面就是没有蒙板的最终效果。在使用蒙板之前,我们要清空蒙板缓冲,并设置初始值为0 glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
然后我们开始开启蒙板,第一个绿色正方形是我们要设置的蒙板的形状,所以我们讲蒙板的比较函数设置为GL_ALWAYS,并讲蒙板的值写为1,并开启蒙板遮罩,这就是下面这个函数要实现的几个功能 glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1);
这只是一部分,蒙板缓冲还需要知道,当比较失败了和比较成功之后,蒙板的值要怎么变化,这就需要glStencilOp函数来完成。它的第一个参数是失败了之后蒙板的值怎么变化,第二个参数是我们画的物体没有通过深度测试之后蒙板值怎么变化,而第三个参数是物体通过了深度测试之后值怎么变化。在这个程序中,我们讲蒙板缓冲初始值清空为0,所以没有通过深度测试的话,我们就使用GL_KEEP,即没有画到物体的地方保持0这个初始值,而最后一个参数使用GL_REPLACE,则通过了深度测试的地方,使用我们上一个函数的第二个参数的值,即1。那么,画完绿色正方形之后,正方形的部分蒙板值为1,非正方形的部分蒙板值则为0. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
画完绿色正方形后,整个屏幕的蒙板值已经发生了变化,接着再
4000
设置画红色三角形的蒙板参数。我需要只在绿色正方形的区域画,所以glStencilFunc的第一个参数使用GL_EQUAL,即必须要等于一个蒙板值,才能通过,那么等于多少呢?第二个参数使用1,因为我们刚才已经设置了绿色正方形的蒙板值也为1,所以就和这个值来比较是否相等。然后就是glStencilOp,这里不管是否通过,我们都不再改变蒙板值,所以全部使用GL_KEEP glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
最后记得关闭蒙板测试 glDisable(GL_STENCIL_TEST);
来总结一下,最开始全屏的蒙板值是0,然后我们采用总是通过的方式画了绿色正方形,并设置蒙板值为1,这之后,绿色正方形的部分蒙板值为1,剩下地方仍然为0。接着以相等的形式来画红色三角形,比较值为1,这样,只用红色三角形和绿色正方形相交的地方,红色三角形才能画上去。看看最终效果



代码地址 http://download.csdn.net/detail/hoytgm/7779963
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  opengles ios stencil 蒙板