自己总结:1个比较(参考值和mask的&操作结果 和 当前模版值和mask的&操作结果 的 比较)
1个规则(比较成功则片段保留,失败则丢弃)
2个函数(glStencilFunc,glStencilOp),
6个参数(比较类型,参考值,掩码值,模版失败的模版值处理,模版成功深度失败,模版成功深度成功),
我们在用OpenGL绘图时往往想制作一些复合图形以及凹凸多边形,像五角星、大的矩形里再画一个小的矩形;另外有时还想做些镂空图或类似的效果。
这时我们可以开启模板测试功能来完成这些需求。图解:
unsigned refValue = inputReferenceValue & mask;
unsigned stencilValue = currentStencilValue & mask;
BOOL isSuccessful = refValue stencilValue;
currentStencilValue = updateStrategy(inputReferenceValue, currentStencilValue, isSuccessful);
更快的计算方式:
OpenGL在模板缓冲区中为每个像素保存了一个“模板值”,当像素需要进行模板测试时,将设定的模板参考值与该像素的“模板值”进行比较,符合条件的通过测试,不符合条件的则被丢弃,不进行绘制。
条件的设置与Alpha测试中的条件设置相似。但注意Alpha测试中是用浮点数来进行比较,而模板测试则是用整数来进行比较。比较也有八种情况:始终通过、始终不通过、大于则通过、小于则通过、大于等于则通过、小于等于则通过、等于则通过、不等于则通过。
这段代码设置模板测试的条件为:“小于3则通过”。glStencilFunc的前两个参数意义与glAlphaFunc的两个参数类似,第三个参数的意义为:如果进行比较,则只比较mask中二进制为1的位。例如,某个像素模板值为5(二进制101),而mask的二进制值为00000011,因为只比较最后两位,5的最后两位为01,其实是小于3的,因此会通过测试。
GL_ZERO表示用0来更新模板值;GL_KEEP说明保留原来的模板值;GL_REPLACE表明用参考值替代原来的模板值。
下面给出代码,演示一下如何在一个大正方形中绘制一个小正方形。
static GLfloat vertices[] = {
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.2f, 0.2f, 0.0f,
-0.2f, -0.2f, 0.0f,
0.2f, 0.2f, 0.0f,
0.2f, -0.2f, 0.0f
};
// Drawing code here.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
附件: OpenGLTest.zip
镂空的正方形
其实这个思路也很简单。我们先用一个小正方形(被镂空部分)填充模板缓存,并且要使它测试失败,这样才能留住背景色。
然后再画大正方形的时,用GREATER进行比较,这样,周围的像素都能用大正方形的片断像素,而不是背景像素,而与小正方形的重叠部分,即镂空部分比较失败,这样这部分区域就可以保留背景色。下面的代码仍然只提供顶点以及绘制部分。
staticGLfloat vertices[] = {
-0.2f, 0.2f, 0.0f,
-0.2f, -0.2f, 0.0f,
0.2f, 0.2f, 0.0f,
0.2f, -0.2f, 0.0f,
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
staticGLfloat colors[] = {
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_EQUAL, 0x1, 0x1);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glStencilFunc(GL_GREATER, 0x1, 0x1);
glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
glFlush();
}