对模板缓存的一些个人理解

说来惭愧,好几年以前就知道模板缓存这个东西,但是一直没有理解特别透彻。

这几天偶尔一次机会再仔细看一遍一些网上的文章,突然很快就理解了。。 不知道是不是以前看这部份的内容时脑子进水了。

中间参考了许多网页,包括这个代码示例也是参考其他网页的代码写的,具体来源无从考证,就结合一个例子记录一下自己的理解和个人认为容易理解有误的地方。


stencil buffer 是opengl 几个 buffer 中的一个,用于做 stencil test。如果stencil test 不通过,则下面即将绘制的东西,不会绘制到 color buffer 上。

stencil buffer 个人认为可以理解成一个 长方形,和 viewport 大小一样大,类似 color buffer .但不同的是 color buffer 上面存储 viewport 每个像素的颜色,stencil buffer 存储每个像素的 模板值。

如果想改变stencil buffer 上的内容,只需要做绘制即可,比如绘制一个正方形,那么 stencil buffer 里面这个长方形显示区域上的模板值就被修改了。

比如相机是侧着看这个正方形的, 也就是说这个正方形在 viewport 上面看起来并不是一个正方形,那么这个绘制这个正方形时候,那么也只会修改 stencil buffer 中绘制区域的不规则图形的部分。(这话说的有些拗口,但是只要我自己将来回头再看能看明白就行。。)


glStencilFunc(GLenum func,GLint ref, GLuint mask)

glStencilOp(GLenum fail, GLenum zfail, GLenum zpass )


glStencilFunc 就是规定测试通过与否的规则, 第一个参数可以选择GL_NEVER,,GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,,GL_EQUAL, GL_NOTEQUAL和 GL_ALWAYS。初始化的值是GL_ALWAYS。

意思是 模板缓存中的值得 与 mask 参数作与, 然后再与 ref 参数作比较,得出的比较结果,和第一个enum 参数比较,如何符合enum的规则,则认为是通过了模板缓存,反之不通过。

也就是说,在我们绘制的过程中,只要开启了模板检测,则就会根据 glStencilFunc 规定的方式,进行模板检测, 只有模板检测通过了的内容,才会绘制到 color buffer 上。


上面说的 glStencilFunc 是用来规定是否通过了 stencil test 的规则。向 stencil buffer 中写内容的规则 ,是 glStencilOp 规定的。

当opengl 绘制内容时,会根据当前像素是否通过了模板检测, 再 根据 glStencilOp()  3个参数所规定的规则,在 stencil buffer 上写内容。

这3个参数可取的值是 GL_KEEP GL_ZERO GL_REPLACE GL_INCR GL_DECR GL_INVERT

当当前像素绘制时,没有通过根据 glStencilFunc 所制定规则的  Stencil Test 时, 会在 stencil buffer 相应的像素上,根据 fail 参数,修改 stencil buffer 的值;

当当前像素绘制时,没通过了 Stencil Test  但没有通过 depth test时, 会在 stencil buffer 相应的像素上,根据 zfail 参数,修改 stencil buffer 的值;

当当前像素绘制时,没有通过了tencil Test  和 Depth Test , 会在 stencil buffer 相应的像素上,根据 zpass 参数,修改 stencil buffer 的值


所以说, 在开启模板缓存后, 绘制内容时, 会做两件事:

1. 检测当前像素是否通过了 stencil test.  测试规则是 glStencilFunc 规定的

2. 当前像素如何修改stencil buffer 的值。 规则是 glStencilFunc 和 glStencilOp 共同规定的。

以前我不是非常理解,就是因为我没有认识到做一次绘制时,会同时发生以上两件事。


下面贴一个例子

#define FREEGLUT_STATIC
#include <GL/glut.h>
#include <math.h>

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

void reshape(int w,int h)
{
	glViewport(0,0,w,h);
	float aspect = (w * 1.0f) / h;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60,aspect,1,100);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void drawRect()
{
	glColor3f(1.0f,0.0f,0.0f);
	glRectf(-5,-5,5,5);
}

void drawSpin()
{
	glColor3f(1.0f,1.0f,1.0f);
	float dRadius = 5.0*(sqrt(2.0)/2.0);
	glBegin(GL_LINE_STRIP);
	for (float dAngel=0;dAngel < 380.0;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);


	glStencilFunc(GL_NEVER,0x0,0x0);
	glStencilOp(GL_INCR,GL_KEEP,GL_KEEP);

	drawSpin();

	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;
}

这个例子的结果是这样:


如果不开启模板测试,结果是这样:



解释一下原理:

1. glClearStencil(0) 设置默认的每个像素模板缓冲值是  0


2. 绘制前调用 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );

其中 GL_STENCIL_BUFER_BIT ,意思是根据  glClearStecil() 设置的值,刷一遍 stencil buffer .

至此 stencil buffer 中所有像素的值都是 0 


3.

glStencilFunc(GL_NEVER,0x0,0x0)

glStencilOp(GL_INCR,GL_KEEP,GL_KEEP)

drawSpin()

这3行的含义是

(1). 永远无法通过模板测试,所以drawSpin()绘制的内容不会绘制在 color buffer 上

(2). 由于永远无法通过 stencil test  ,所以 glStencilOp() 中只有第1个参数起作用, GL_INCR 的意思是把模板缓存对应的值 + 1 。所以 drawSpin() 时,绘制的内容会把模板缓存里面相应像素的值 + 1. 这时就形成一个背景全是 0 , 中间螺旋线圈部分是 1 的模板缓存


4.

glStencilFunc(GL_NOTEQUAL,0x1,0x1);
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
drawRect();

这3行的含义是:

(1) . 当 stencil buffer 像素值的最后 1 位( mask 参数是 0x1) ,与 0x1 这个 ref 值作比较, 如果 not equal 不相等时,才会绘制下面的内容.

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

(2). 因为这一步的绘制不想修改模板缓存的值,所以无论通过与否,都希望模板缓存的值保持不变,因此 glStencilOp() 的 3个参数都是 GL_KEEP



至此我已经说完了。

打了好多字,所说的内容其实是许多书许多网页上说的很清楚的东西,只是自己以前理解的不够透彻。

所以在这转化成自己的大白话,给自己做个备忘,用这种通俗又有点啰嗦的语言,帮助到像我一样遇到过理解困难的 opengl 初学者.



  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值