OpenGL 模板缓冲区的理解

OpenGL缓冲区

  • 颜色缓冲区
    • 包含颜色索引或者RGBA颜色数据
  • 深度缓冲区
    • 存储每个像素的深度值。
    • 当启动深度测试时,片段像素深度值和深度缓冲区深度值比较,决定片段哪些像素点数据可以替换到颜色缓冲区中。
  • 模板缓冲区
    • 为屏幕上的每个像素点保存一个无符号整数值。
    • 在渲染过程中,可以用缓冲区保存的值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新响应的像素点的颜色值。这个比较的过程称为模板测试。
    • 模板测试发生在透明度测试(alpha test)之后,深度测试(depth test)之前。
模板测试.png

中间小的纸板的T字位置是镂空的,当用喷漆喷在纸板上时,镂空的部分有喷漆通过,没镂空的就被纸板挡住,所以最终显示在大纸板上就是一个T字。模板缓冲区的原理也是如此:通过模板测试的片段像素点颜色会被替换到颜色缓冲区(相当于喷漆通过镂空的部分显示到大纸板上),未通过的则不会保存到颜色缓冲区(相当于没镂空的部分就被小纸板挡住了,显示不到大纸板上)。

  • 累积缓冲区
    • 允许你把渲染到颜色缓冲区的值,拷贝到累积缓冲区。在多次拷贝操作到累积缓冲区时,可以用不同方式的把颜色缓冲区内容和当前累积缓冲区的内容进行重复混合。

模板缓冲区相关函数

  • 开启模板测试
glEnable(GL_STENCIL_TEST);
  • 设置模板缓冲区的写入掩码(默认为0xff)
glStencilMask(GLuint mask)
  • 清除模板缓冲区的值(默认为0)
glClearStencil(GLint s)
  • 设置模板测试是否通过的规则
glStencilFunc(GLenum func, GLint ref, GLuint mask)
枚举值 描述
GL_NEVER 永远不能通过
GL_ALWAYS 永远可以通过
GL_LESS 小于参考值可以通过
GL_LEQUAL 小于或者等于参考值可以通过
GL_EQUAL 等于参考值通过
GL_GEQUAL 大于等于参考值通过
GL_GREATER 大于参考值通过
GL_NOTEQUAL 不等于通过
 ref: 参考值
 mask: 掩码(和模板缓冲区的值进行与操作,结果再和参考值作比较,判断比较结果是否符合设定的规则)  
  • 设置根据测试结果,如何修改模板缓冲区的值
glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
fail: 模板测试未通过时该如何变化。
zfail: 模板测试通过,但深度测试未通过该如何变化。
zpass: 模板测试和深度测试均通过该如何变化。
枚举值 描述
GL_KEEP 不改变,默认值
GL_ZERO 变回零
GL_REPLACE 使用测试条件中的设定值来代替当前模板值
GL_INCR 增加1,如果已经是最大值,则保持不变
GL_INCR_WRAP 增加1,但如果已经是最大值,则从零重新开始
GL_DECR 减少1,但如果已经是零,则保持不变
GL_DECR_WRAP 减少1,但如果已经是零,则重新设置为最大值
GL_INVERT 按位取反
  • 清空模板缓冲区
glClear(GL_STENCIL_BUFFER_BIT)

例子

#include 
#ifdef __APPLE__
#include 
#else
#define FREEGLUT_STATIC
#include 
#endif

void init()
{
    glClearColor(0.f, 0.f, 0.f, 0.f);
    glClearStencil(0);
    glClearDepth(0);
    glEnable(GL_STENCIL_TEST);
}

void reshape(int w, int h)
{
    if (h == 0) {
        h = 1;
    }
    glViewport(0, 0, w, h);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(35.f, (w * 1.f) / h, 1.f, 100.f);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void drawRect()
{
    glColor3f(1.f, 0.f, 0.f);
    glRectf(-5.f, -5.f, 5.f, 5.f);
}

void drawSpin()
{
    glColor3f(0.f, 0.f, 0.f);
    float dRadius = 5.f * (sqrt(2.0)/2.0);
    glBegin(GL_LINE_STRIP);
    for (float dAngel = 0; dAngel < 10.f; dAngel += 0.1) {
        glVertex2d(dRadius * cos(dAngel), dRadius * sin(dAngel));
        dRadius *= 1.003;
    }
    glEnd();
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0, 0, -20.f);

/*
    永远无法通过模板测试,drawSpin()绘制的内容的像素点颜色不会替换到颜色缓冲区中。
    
    因为无法通过模板测试,所以glStencilOp()中的第一个参数起作用,把模板缓冲区相对应的值+1。所以drawSpin()后,形成一个背景为0,中间螺旋线是1的模板缓冲区。
*/
    glStencilFunc(GL_NEVER, 0x0, 0x0);
    glStencilOp(GL_INCR, GL_KEEP, GL_KEEP);
    drawSpin();
    
/*
    当 stencil buffer 像素值的最后 1 位( mask 参数是 0x1) ,与 0x1 这个 ref 值作比较, 如果 not equal 不相等时,才会绘制下面的内容.

所以 drawRect() 在绘制的时候,有些部分是会绘制的,因为这部分的 stencil buffer 像素的值是 0 ; 而 stencil buffer 像素值是 1 的部分,也就是上一步,绘制螺旋线圈的部分,因为 stencil buffer 里对应的像素的 值是 1 ,所以这部分不会绘制。也就是会出现 drawRect()绘制的图像有镂空的情形(改变背景颜色就更清楚了)。

因为这一步的绘制不想修改模板缓存的值,所以无论通过与否,都希望模板缓存的值保持不变,因此 glStencilOp() 的 3个参数都是 GL_KEEP
*/
    glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    drawRect();
    
    glutSwapBuffers();
}

int main(int argc, char** argv) {
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL | GLUT_DEPTH);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(600, 600);
    glutCreateWindow("stencil buffer test");
    
    init();
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutMainLoop();
    
    return 0;
}
效果图.png

参考博客

https://blog.csdn.net/csxiaoshui/article/details/23457273
https://blog.csdn.net/korekara88730/article/details/42213217

你可能感兴趣的:(OpenGL 模板缓冲区的理解)