深入了解OpenGL——模板测试

时间:2021-03-02 17:35:17

http://www.cocoachina.com/gamedev/opengl/2010/1025/2231.html


我们在用OpenGL绘图时往往想制作一些复合图形以及凹凸多边形,像五角星、大的矩形里再画一个小的矩形;另外有时还想做些镂空图或类似的效果。 这时我们可以开启模板测试功能来完

  我们在用OpenGL绘图时往往想制作一些复合图形以及凹凸多边形,像五角星、大的矩形里再画一个小的矩形;另外有时还想做些镂空图或类似的效果。

  这时我们可以开启模板测试功能来完成这些需求。

  由于OpenGL的一个绘制流水线特征以及GPU着色特征,像对于大矩形里画小矩形这种不能通过先绘制大矩形,然后再在其中绘制小矩形的方式进行绘制,否则当此复合图形在做一些旋转时,我们会发现小矩形部分会有非常严重的抖动。

  模板测试的一个基本的特性是:它基于在某个坐标位置的模板缓存值与一个参考值相比较的结果,有条件地废弃一个片断。这里要注意的是,这个判定是基于当前被测试的片断(而不是已在帧缓存的像素),并且当前片断的每个像素都会进行模板测试,并且如果某些像素测试失败的话,这些像素就会被废弃。

  下面介绍一下如何开启模板测试。

  对于Mac OS X下,对你的OpenGL视图的属性中选择模板缓存的位数即可,模板缓存只有8位这一个选项。在iOS的OpenGL ES下,则需要建立一个模板缓存,建立方法与建立深度缓存类似。
然后,我们在代码中用glEnable(GL_STENCIL_TEST);来开启模板测试。

  下面谈谈模板测试是如何工作的。一开始,我们用glClear(GL_STENCIL_BUFFER_BIT);来清除模板缓存的值,使这些值均为0(默认设置,也无须修改)。当我们用类似glDrawArrays这些函数来绘制某个图形后,被绘制的图形先被光栅化,然后得到相应的片断。我们指定一个参考值,一个掩模值,跟当前的模板缓存的值进行比较。

  如何比较呢?下面给出一段伪代码:

unsigned refValue = inputReferenceValue & mask;
unsigned stencilValue = currentStencilValue & mask;
BOOL isSuccessful = refValue <compare_operation> stencilValue;
currentStencilValue = updateStrategy(inputReferenceValue, currentStencilValue, isSuccessful);

  我们首先用自己指定的参考值与掩模值进行按位与操作,得到的结果和当前对应的模板值与掩模值的按位与的结果进行比较,比较函数也由我们自己指定。如果比较的结果是TRUE,即测试成功,当前的像素被保留;否则,测试失败,当前的像素被废弃。我们还可以指定在模板测试成功或失败后如何更新模板值。

  下面介绍上述操作过程所涉及到的一些接口:

void glStencilFunc(GLenum  func, GLint  ref, GLuint  mask);

  func是个枚举类型,它指定了比较操作,值可以是:GL_NEVER, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS。ref就是参考值。而mask就是掩模值。如果模板位数是8的话,那么mask最大值是0xff,即8个1;最小值是0。

void glStencilOp(GLenum  sfail, GLenum  dpfail, GLenum  dppass);

  这个函数指定了对模板值的更新操作。这三个参数所接受的枚举值都是同一组:GL_KEEP,GL_ZERO,GL_REPLACE,GL_INCR,GL_INCR_WRAP,GL_DECR,GL_DECR_WRAP, GL_INVERT。sfail说明当模板测试失败后,用何种方式更新模板值;dpfail说明当模板测试成功,但深度测试失败后用何种方式更新模板值;dppass说明当模板测试与深度测试均成功后用何种方式更新模板值。GL_ZERO表示用0来更新模板值;GL_KEEP说明保留原来的模板值;GL_REPLACE表明用参考值替代原来的模板值。