OpenGLES demo - 16. 蒙板 Stencil


在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

    glGenRenderbuffers(1, &_depthRenderBuffer);

    glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);

    

    GLuint size = 0;

    size = GL_DEPTH24_STENCIL8_OES;

    glRenderbufferStorage(GL_RENDERBUFFER, size, _viewWidth, _viewHeight);

    

    glBindRenderbuffer(GL_RENDERBUFFER, 0);

一般情况下,stencil和depth是公用一块内存,所以depth和stencil的创建也是一起的。


在创建后缓冲的FBO的时候,我们原来已经设置了深度缓冲

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);

在这里,由于_depthRenderBuffer里面也包含了Stencil,所以我们还需要再用_depthRenderBuffer去设置一次stencil的attachment

glFramebufferRenderbuffer(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);
OpenGLES demo - 16. 蒙板 Stencil_第1张图片


然后在左边画一个大的红色三角形,深度为-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);
OpenGLES demo - 16. 蒙板 Stencil_第2张图片


上面就是没有蒙板的最终效果。在使用蒙板之前,我们要清空蒙板缓冲,并设置初始值为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);

画完绿色正方形后,整个屏幕的蒙板值已经发生了变化,接着再设置画红色三角形的蒙板参数。我需要只在绿色正方形的区域画,所以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,这样,只用红色三角形和绿色正方形相交的地方,红色三角形才能画上去。看看最终效果

OpenGLES demo - 16. 蒙板 Stencil_第3张图片


代码地址 http://download.csdn.net/detail/hoytgm/7779963

你可能感兴趣的:(OpenGLES)