阅读:1211回复:0
OpenGL系列讲座(26)
第七章 OpenGL帧缓存和动画
本章将简要地讲述OpenGL帧缓存和动画的内容。通过这一章的学习,读者将初步了解 OpenGL帧缓存的组成和基本操作以及掌握OpenGL动画设计的基础编程方法。 7.1 帧缓存 屏幕上所绘的图形都是由象素组成的,每个象素都有一个固定的颜色或带有相应点的其 它信息,如深度等。因此在绘制图形时,内存中必须为每个象素均匀地保存数据,这块为所 有象素保存数据的内存区就叫缓冲区,又叫缓存(buffer)。不同的缓存可能包含每个象素的 不等数位的数据,但在给定的一个缓存中,每个象素都被赋予相同数位的数据。存贮一位象 素信息的缓存叫位面(bitplane)。系统中所有的缓存统称为帧缓存(Framebuffer),可以利用 这些不同的缓存进行颜色设置、隐藏面消除、场景反走样和模板等操作。 7.1.1 帧缓存组成 OpenGL帧缓存由以下四种缓存组成: 一、颜色缓存(Color Buffer)。颜色缓存通常指的是图形要画入的缓存,其中内容可以 是颜色索引,也可以是RGB颜色数据(包含Alpha值也可)。若读者所用OpenGL系统支持立 体视图,则有左、右两个缓存;若不支持立体视图,则只有左缓存。同样,双缓存OpenGL 系统有前台和后台两个缓存,而单缓存系统只有前台缓存。每个OpenGL系统都必须提供一个 左前颜色缓存。 二、深度缓存(Depth Buffer)。深度缓存保存每个象素的深度值。深度通常用视点到物 体的距离来度量,这样带有较大深度值的象素就会被带有较小深度值的象素替代,即远处的 物体被近处的物体遮挡住了。深度缓存也称为z-buffer,因为在实际应用中,x、y常度量屏 幕上水平与垂直距离,而z常被用来度量眼睛到屏幕的垂直距离。 三、模板缓存(Stencil Buffer)。模板缓存可以保持屏幕上某些部位的图形不变,而其 它部位仍然可以进行图形绘制。比如说,可以通过模板缓存来绘制透过汽车挡风玻璃观看车 外景物的画面。首先,将挡风玻璃的形状存贮到模板缓存中去,然后再绘制整个场景。这样, 模板缓存挡住了通过挡风玻璃看不见的任何东西,而车内的仪表及其它物品只需绘制一次。 因此,随着汽车的移动,只有外面的场景在不断地更改。 四、累积缓存(Accumulation Buffer)。累积缓存同颜色缓存一样也保存颜色数据,但它 只保存RGBA颜色数据,而不能保存颜色索引数据(因为在颜色表方式下使用累积缓存其结果 不确定)。这个缓存一般用于累积一系列图像,从而形成最后的合成图像。利用这种方法,可 以进行场景反走样操作。 7.1.2 缓存清除 基础篇的前面几章内容里已经提到缓存清除的应用,这里将详细解释这些函数。在许多 图形程序中,清屏或清除任何一个缓存,一般来说操作开销都很大。例如,在一个1280*1024 的屏幕上,它要求对成千上万个象素进行操作。通常,对于一个简单的绘图程序,清除操作 可能要比绘图所花费的时间多得多。如果不仅仅只清除颜色缓存,而还要清除深度和模板等 缓存的话,则将花费三四倍的时间开销。因此,为了解决这个问题,许多机器都用硬件来实 现清屏或清除缓存操作。OpenGL清除缓存操作过程是:先给出要写入每个缓存的清除值,然 后用单个函数命令执行操作,传入所有要清除的缓存表,若硬件能同时清除,则这些清除操 作可以同时进行;否则,各个操作依次进行。 下面这个函数为每个缓存设置清除值: void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha); void glClearIndex(GLfloat index); void glClearDepth(GLclampd depth); void glClearStencil(GLint s); void glClearAccum(GLflaot red,GLfloat green,GLfloat blue,GLfloat alpha); 以上函数分别为RGBA方式下的颜色缓存、颜色表方式下的颜色缓存、深度缓存、模板缓 存和累积缓存说明当前的清除值。GLclampf和GLclampd(约简的GLfloat和GLdouble)类 型的数据被约简到[0.0,1.0]之间,缺省的深度清除值为0.0。用清除函数命令设置的清除值 一直保持有效,直到它们被更改为止。 选择了要清除的缓存及其清除值后,就可以调用glClear()来完成清除的操作了。这个 清除函数为: void glClaear(Glbitfield mask); 清除指定的缓存。参数mask可以是下面这些位逻辑的或, GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT GL_STENCIL_BUFFER_BIT GL_ACCUM_BUFFER_BIT 这些位逻辑确定所要清除的缓存。注意,GL_COLOR_BUFFER_BIT清除RGBA方式颜色缓存 还是清除颜色表方式颜色缓存,要依赖当前系统设置的颜色方式。当清除颜色缓存后,所有 启动写操作的颜色缓存都被清除。 7.2 OpenGL动画 OpenGL提供了双缓存,可以用来制作动画。也就是说,在显示前台缓存内容中的一帧画 面时,后台缓存正在绘制下一帧画面,当绘制完毕,则后台缓存内容便在屏幕上显示出来, 而前台正好相反,又在绘制下一帧画面内容。这样循环反复,屏幕上显示的总是已经画好的 图形,于是看起来所有的画面都是连续的。 在OpenGL中,设计这样的动画程序很简单,只需掌握一个重点函数,即: void auxSwapBuffers(void); 设置交换缓存。即执行完一次绘制过程,便交换前后台缓存,以便让下一帧图形在屏幕后 绘制完成。当然,使用不同的窗口系统设置交换缓存的函数也可能不一样,比如在X窗口系 统下,就最好用glxSwapBufferS()。 此外,在窗口显示模式中还应设置双缓存模式。下面看一个双缓存动画例子dbufcolr.c, 这个程序是在本篇第三章的nmlcolr.c的基础上改制而成的,读者可以对比起来看看,就会 发现OpenGL动画程序设计其实是件极其容易和有趣的事情。 例3-20 双缓存动画例程 dbufcolr.c #include "glos.h" #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> /* 定义旋转和比例因子的初始值 */ GLfloat step=0.0,s=0.1; /* 定义立方体的顶点坐标值 */ static GLfloat p1[]={0.5,-0.5,-0.5}, p2[]={0.5,0.5,-0.5}, p3[]={0.5,0.5,0.5}, p4[]={0.5,-0.5,0.5}, p5[]={-0.5,-0.5,0.5}, p6[]={-0.5,0.5,0.5}, p7[]={-0.5,0.5,-0.5}, p8[]={-0.5,-0.5,-0.5}; /* 定义立方体的顶点方向值 */ static GLfloat m1[]={1.0,0.0,0.0}, m2[]={-1.0,0.0,0.0}, m3[]={0.0,1.0,0.0}, m4[]={0.0,-1.0,0.0}, m5[]={0.0,0.0,1.0}, m6[]={0.0,0.0,-1.0}; /* 定义立方体的顶点颜色值 */ static GLfloat c1[]={0.0,0.0,1.0}, c2[]={0.0,1.0,1.0}, c3[]={1.0,1.0,1.0}, c4[]={1.0,0.0,1.0}, c5[]={1.0,0.0,0.0}, c6[]={1.0,1.0,0.0}, c7[]={0.0,1.0,0.0}, c8[]={1.0,1.0,1.0}; void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK stepDisplay (void); void CALLBACK startIdleFunc (AUX_EVENTREC *event); void CALLBACK stopIdleFunc (AUX_EVENTREC *event); void CALLBACK display(void); void DrawColorBox(void); void myinit(void) { GLfloat light_ambient[]={0.3,0.2,0.5}; GLfloat light_diffuse[]={1.0,1.0,1.0}; GLfloat light_position[] = { 2.0, 2.0, 2.0, 1.0 }; GLfloat light1_ambient[]={0.3,0.3,0.2}; GLfloat light1_diffuse[]={1.0,1.0,1.0}; GLfloat light1_position[] = { -2.0, -2.0, -2.0, 1.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); s+=0.005; if(s>1.0)s=0.1; glPushMatrix(); glScalef(s,s,s); glRotatef(step,0.0,1.0,0.0); glRotatef(step,0.0,0.0,1.0); glRotatef(step,1.0,0.0,0.0); DrawColorBox(); glPopMatrix(); glFlush(); auxSwapBuffers(); /* 交换缓存 */ } void CALLBACK stepDisplay (void) { step = step + 1.0; if (step > 360.0) step = step - 360.0; display(); } void CALLBACK startIdleFunc (AUX_EVENTREC *event) { auxIdleFunc(stepDisplay); } void CALLBACK stopIdleFunc (AUX_EVENTREC *event) { auxIdleFunc(0); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.50*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); } void DrawColorBox(void) { glBegin (GL_QUADS); glColor3fv(c1); glNormal3fv(m1); glVertex3fv(p1); glColor3fv(c2); glVertex3fv(p2); glColor3fv(c3); glVertex3fv(p3); glColor3fv(c4); glVertex3fv(p4); glColor3fv(c5); glNormal3fv(m5); glVertex3fv(p5); glColor3fv(c6); glVertex3fv(p6); glColor3fv(c7); glVertex3fv(p7); glColor3fv(c8); glVertex3fv(p8); glColor3fv(c5); glNormal3fv(m3); glVertex3fv(p5); glColor3fv(c6); glVertex3fv(p6); glColor3fv(c3); glVertex3fv(p3); glColor3fv(c4); glVertex3fv(p4); glColor3fv(c1); glNormal3fv(m4); glVertex3fv(p1); glColor3fv(c2); glVertex3fv(p2); glColor3fv(c7); glVertex3fv(p7); glColor3fv(c8); glVertex3fv(p8); glColor3fv(c2); glNormal3fv(m5); glVertex3fv(p2); glColor3fv(c3); glVertex3fv(p3); glColor3fv(c6); glVertex3fv(p6); glColor3fv(c7); glVertex3fv(p7); glColor3fv(c1); glNormal3fv(m6); glVertex3fv(p1); glColor3fv(c4); glVertex3fv(p4); glColor3fv(c5); glVertex3fv(p5); glColor3fv(c8); glVertex3fv(p8); glEnd(); } void main(void) { auxInitDisplayMode (AUX_DOUBLE | AUX_RGBA); /* 设置双缓存和RGBA颜色模式*/ auxInitPosition (0, 0, 500,400); auxInitWindow ("Double_Buffer_Animation"); myinit(); auxReshapeFunc (myReshape); auxIdleFunc (stepDisplay); auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, startIdleFunc); auxMouseFunc (AUX_RIGHTBUTTON, AUX_MOUSEDOWN, stopIdleFunc); auxMainLoop(display); } 以上程序运行结果是在屏幕上显示一个不断旋转和由小到大变化的五彩立方体。 图 3-7-2 双缓存动画 |
|
|