OPENGL帧缓存和动画 
作为最后一关,我们将架设自己即时光影的动画,让没有VOODOO的玩家看看OPENGL 
这震撼(至少我是这么认为的吧)的效果,完成所有这将近20次灌水最终目标。 
 
我们前面(好象是第三还是第四次)讲过如何用几何变换实现动画。那时的效果 
现在看肯定不尽人意,因为频繁的闪烁不是我们需要的。因为那时(到这之前也 
是)采用的是单缓存模式。对正在显示的部分边计算边修改必然造成速度瓶颈, 
出现闪烁。一般正规的动画制作在实现上都是通过双缓存实现的(硬件也好,软 
件也好)大家可以参考《家用电脑与游戏机》的98-2中的一篇文章。当前台显示 
缓存用于显示时,后台缓存已经进行计算,计算完毕把所有内容通过缓存拷贝一 
次性完成,防止闪烁的出现。 
 
一 OPENGL帧缓存的实现 
 
1 颜色缓存(Color Buffer)其中内容可以是颜色索引或者RGBA数据,如果用的 
OPENGL系统支持立体图,则有左右两个缓存。 
 
2 深度缓存(Depth Buffer) 就是Z-BUFFER,用于保存象素Z方向的数值,深度 
大的被深度小的代替,用以实现消隐 
 
3 模板缓存(Stencil Buffer) 用以保持屏幕上某些位置图形不变,而其他部分 
重绘。例如大家熟悉的开飞机和赛车的游戏的驾驶舱视角,只有挡风外面的景物 
变化,舱内仪表等等并不变化。 
 
4 累计缓存(Accumulation Buffer) 只保存RGBA数据,用于合成图象,例如有某 
缓存中的位图调入这里合成一幅新图。 
 
二 帧缓存的清除 
对高分辨率模式清除缓存是工作量巨大的任务,OPENGL一般先寻求硬件同时完成, 
否则软件依次解决。我们前面每次必用的glClearColor()大家已经不陌生吧。 
 
首先设置清除值 

void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);  

void glClearIndex(GLfloat index);  

void glClearDepth(GLclampd depth);  

void glClearStencil(GLint s);  

void glClerAccum(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha);


 
然后进行清除 

void glClear(GLbitfield mask);  

mask: GL_COLOR_BUFFER_BIT|  

      GL_DEPTH_BUFFER_BIT|  

      GL_STENCIL_BUFFER_BIT|  

      GL_ACCUM_BUFFER_BIT


 
三 双缓存动画 
你可以把所有的变换工作看成后台缓存的计算,然后把所有结果拷贝到前台即可。 
因此我们只需两个新内容: 
 
首先初始化时调用 
auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA); 
用AUX_DOUBLE代替AUX_SINGLE设置成双缓存模式 
 
然后在绘制完毕(glFlush();后)调用 
auxSwapBuffers(); 
进行缓存拷贝。Easy like sunday morning!! 
当然不同系统下,这个函数也许不同(毕竟是辅助库函数么),例如X-WINDOWS 
下可以使用glxSwapBuffers(),意思完全一样。 
 
先说说下面这个例子的功能: 
有一个兰色的环作为主体,有一个黄色高亮的球表示光源的位置。 
小球不断从屏幕左方运动到右方,可以看出环状物上光影的变化。 
操作: 
     鼠标左键/右键:开始/停止光源的运动 
     键盘 上/下/左/右:控制环状物的 前进/后退/旋转 
 

//  

//sample.cpp  

#include "glos.h"  

#include <GL/gl.h>  

#include <GL/glaux.h>  

#include "windows.h"  

  

void myinit(void);  

void CALLBACK  display(void);  

void CALLBACK  reshape(GLsizei w,GLsizei h);  

void CALLBACK  stepDisplay(void);  

void CALLBACK  startIdleFunc(AUX_EVENTREC *event);  

void CALLBACK  stopIdleFunc(AUX_EVENTREC *event);  

  

//step是表示环状物旋转的参数;z是控制其前后坐标的参数  

static GLfloat step=0.0,z=0.0;  

//position是控制光源的位置的参数  

static GLfloat position[]={-20.0,0.0,-5.0,1.0};  

  

void myinit(void)  

{  

//初始化注意是双缓存模式  

        auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA);  

  

        auxInitPosition(0,0,500,500);  

        auxInitWindow("sample1");  

        glClearColor(0.0,0.0,0.0,0.0);  

        glClear(GL_COLOR_BUFFER_BIT);  

  

  

        glFrontFace(GL_CW);  

  

        glEnable(GL_LIGHTING);  

        glFrontFace(GL_CW);  

//      glEnable(GL_POLYGON_SMOOTH);  

//      glEnable(GL_BLEND);  

//      glBlendFunc(GL_SRC_ALPHA,GL_ONE);  

  

        glDepthFunc(GL_LESS);  

        glEnable(GL_DEPTH_TEST);  

//      glShadeModel(GL_FLAT);  

}  

  

void CALLBACK reshape(GLsizei w,GLsizei h)  

{  

  

glViewport(0,0,w,h);  

glMatrixMode(GL_PROJECTION);  

glLoadIdentity();  

/*if(w<=h*3)  

 glOrtho(-2.0,2.0,-2.0*(GLfloat)h/(GLfloat)w,  

         2.0*(GLfloat)h/(GLfloat)w,-10.0,10.0);  

else  

  glOrtho(-2.0*(GLfloat)h/(GLfloat)w,  

         2.0*(GLfloat)h/(GLfloat)w,-2.0,2.0,-10.0,10.0);  

*/  

  

//启用立体的视景,具有近大远小的效果  

glFrustum(-6.0,6.0,-6.0,6.0,3.0,20.0);    

glMatrixMode(GL_MODELVIEW);  

glLoadIdentity();  

  

}  

  

  

void CALLBACK display(void)  

{  

  

  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  

  

//首先在当前位置设置光源    

    glPushMatrix();  

        GLfloat light_ambient[]={0.3,0.5,0.3};  

        GLfloat light_diffuse[]={1.0,1.0,1.0};  

        GLfloat light_specular[]={0.8,0.8,0.0};  

        glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);  

        glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);  

        glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);  

        glLightfv(GL_LIGHT0,GL_POSITION,position);  

        glEnable(GL_LIGHTING);  

        glEnable(GL_LIGHT0);  

//在光源旁边绘制黄色高亮的小球,标志光源位置  

//小球和光源位置由position[]决定  

        glTranslatef(position[0],position[1],position[2]-1.0);  

        GLfloat mat_ambient[]={1.0,0.0,0.0,1.0};  

        GLfloat mat_diffuse[]={1.0,1.0,0.0,1.0};  

        GLfloat mat_specular[]={1.0,1.0,0.0,1.0};  

        GLfloat mat_shininess[]={50.0};  

        glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);  

        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);  

        glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);  

        glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);  

        auxSolidSphere(0.5);  

    glPopMatrix();  

  

//在当前位置绘制兰色环状物,其位置由step z共同决定    

    glPushMatrix();  

        glTranslatef(0.0,0.0,-8.0+z);  

        glRotatef(135.0+step,0.0,1.0,0.0);  

        GLfloat mat2_ambient[]={0.0,0.0,1.0,1.0};  

        GLfloat mat2_diffuse[]={0.2,0.0,0.99,1.0};  

        GLfloat mat2_specular[]={1.0,1.0,0.0,1.0};  

        GLfloat mat2_shininess[]={50.0};  

        glMaterialfv(GL_FRONT,GL_AMBIENT,mat2_ambient);  

        glMaterialfv(GL_FRONT,GL_DIFFUSE,mat2_diffuse);  

        glMaterialfv(GL_FRONT,GL_SPECULAR,mat2_specular);  

        glMaterialfv(GL_FRONT,GL_SHININESS,mat2_shininess);  

        auxSolidTorus(2.0,3.5);  

    glPopMatrix();  

          

  glFlush();  

//绘制完毕,缓存交换  

  auxSwapBuffers();  

  

}  

void CALLBACK Up(void)  

{  

//键盘“上”的处理  

z=z+0.05;  

}  

void CALLBACK Down(void)  

{  

//键盘“下”的处理  

z=z-0.05;  

}  

void CALLBACK Left(void)  

{  

//键盘“左”的处理  

step=step+2.0;  

}  

void CALLBACK Right(void)  

{  

//键盘“右”的处理  

step=step-2.0;  

}  

  

void CALLBACK stepDisplay(void)  

{  

//系统闲时的调用过程  

  position[0]=position[0]+0.5;  

  if(position[0]>20.0) position[0]=-20.0;  

  display();  

}  

  

void CALLBACK startFunc(AUX_EVENTREC *event)  

{  

//鼠标左键的处理  

        auxIdleFunc(stepDisplay);  

}  

  

void CALLBACK stopIdleFunc(AUX_EVENTREC *event)  

{  

//鼠标右键的处理  

  auxIdleFunc(0);  

}  

  

void main(void)  

{  

        myinit();  

      

        auxReshapeFunc(reshape);  

        auxIdleFunc(stepDisplay);  

        auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,startFunc);  

        auxMouseFunc(AUX_RIGHTBUTTON,AUX_MOUSEDOWN,stopIdleFunc);  

        auxKeyFunc(AUX_UP,Up);  

        auxKeyFunc(AUX_DOWN,Down);  

        auxKeyFunc(AUX_LEFT,Left);  

        auxKeyFunc(AUX_RIGHT,Right);  

        auxMainLoop(display);  

}  

//end of sample


 
其中用到大量CALLBACK函数,分别处理不同消息 
比较前面演示的动画效果,闪烁的现象明显改善乐(你可以把这个程序两个关于 
双缓存的地方改成单缓存:设置AUX_SINGLE和去掉auxSwapBuffers(),看看闪烁 
的多么厉害),至于本身的绘图可能的拖尾现象,只能怪自己机器不好乐。 
写到这里,终于打穿乐OPENGL!!!实现当初提出的“长长见识”的设想。 
今后有什么新内容我还会尽量补充,大家也可以自由补充。