OPENGL帧缓存和动画

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 <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>

#pragma comment(lib, "OpenGl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")

#pragma warning(disable : 4244)		// MIPS
#pragma warning(disable : 4136)		// X86
#pragma warning(disable : 4051)		// ALPHA
 
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!!!实现当初提出的“长长见识”的设想。 
今后有什么新内容我还会尽量补充,大家也可以自由补充。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值