模板缓冲区与模板测试

总述

《Realtime Rendering 4rd》第二章详细介绍了整个图形渲染管线,在融合(Merging)阶段提到了Alpha测试、模板测试、深度测试。本文将主要对模板测试进行分析,主要分为以下4个部分:

  1. 模板缓冲区的介绍
  2. 模板缓冲区的作用
  3. 基于模板实现物体轮廓绘制
  4. 总结

一、模板缓冲区介绍

模板缓冲中的模板值(Stencil Value)通常是8位的,因此每个片段/像素共有256种不同的模板值(译注:8位就是1字节大小,因此和char的容量一样是256个不同值)。模板缓冲区与深度缓冲区的大小相同。在使用的过程中,一般先开启模板缓冲,绘制一个物体作为我们的模板,这个过程实际上就是写入模板缓冲的过程;接着我们利用模板缓冲中的值决定是丢弃还是保留后续绘图中的片元。

  1. 开启模板缓冲写入。
  2. 渲染物体,更新模板缓冲。
  3. 关闭模板缓冲写入。
  4. 渲染(其他)物体,这次基于模板缓冲内容丢弃特定片段。

如上图所示,最左边为颜色缓冲区,最右边为经过模板测试之后所绘制的图像,即绘制中间图模板值为1对应的像素。

二、模板缓冲区的作用

通过对模板缓冲区的设置,我们可以实现一些不错的特殊效果

  1. 绘制物体的轮廓
  2. 实现镜面效果
  3. 实现阴影效果(Shadow Volume)

三、基于模板实现物体轮廓绘制

首先我们看下实现效果

要实现轮廓效果,我们的思路是:

  1. 绘制两个box。
  2. 再绘制两个更大的box,但是1中绘制box像素位置不再绘制2中box,这样就得到轮廓。

在介绍这两个步骤实现之前,先介绍两个opengl中用于模板缓冲区写入的函数:

  1. glStencilMask(para); 当para = 0xFF时模板值与它进行按位与运算结果是模板值,模板缓冲可写。当para = 0x00时模板值与它进行按位与运算结果是模板值,模板缓冲禁止写入。
  2. glStencilFunc(GLenum func, GLint ref, GLuint mask)的运算为:(ref & mask) func 缓冲区模板值&mask, 如果结果成立则片段通过测试,否则丢弃。

接下介绍绘制过程,以及绘制过程中模板缓冲区的变化:

  1. 绘制之前先清空缓冲区:glClear(GL_STENCIL_BUFFER_BIT),此时模板缓冲区为:

此时缓冲区的值都为0.

2. 开启模板缓冲区写入,绘制两个box:

glStencilFunc(GL_ALWAYS, 1, 0xFF); //GL_ALWAYS总是测试通过
//并且glStencilMask(0xFF);表示可写入,则将1写入模板缓冲区
glStencilMask(0xFF);
DrawTwoContainers();//绘制两个box

此时的模板缓冲区为:

3. 绘制两个放大的box

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//模板缓冲区中不等于1的模板对应的片段测试通过
glStencilMask(0x00); //模板禁止写入
glDisable(GL_DEPTH_TEST);//绘制两个放大的box

四、总结

模板缓冲区在实际应用的比较广泛,对模板缓冲区的理解关键在于每次进行模板的写入之后能够准确的知道模板缓冲区中模板值的分布。

模板缓冲区运用熟练的话,可以制造更多意想不到的效果。如下图,背景龙的绘制效果