1.7 动画

动画是由一副一副静止的画面组成的,通过在静止画面之间快速切换,比如1秒钟切换24副画面,而这24副画面之间的差别较小,这样我们的大脑会将前一副画面和后一副画面连接起来,形成平滑的动画。小时候的连环画,就有点动画的意思了,快速翻看连环画,就看到画面仿佛动起来了。


双缓冲

由于计算机程序没有那么多的纸张来描画静止画面,它只有一个显示屏,所以就导致两幅画面切换之前,需要擦除原来的画面,然后画上新的画面。这就导致一个问题,前一副画面只擦除了一半,然后后一副画面描画了一半,这样的情况是会出现的。这样的情况,通常会导致屏幕的闪烁。为了防止这种现象,就出现了双缓冲这个概念。
双缓冲,其实就是两个颜色 缓冲区,一个用来显示前一个画面,一个用来绘制后一个画面。假设有A,B,C三个画面,做成连环画。两个缓冲区,分别叫M和N好了。最开始的时候,在M缓冲区显示A画面,然后在N缓冲区,绘制B画面,当B画面绘制完毕后,交换M和N缓冲区,这时N缓冲区在前,M在后。接着在M缓冲区继续绘制C画面,当C画面绘制完毕后,又交换两个缓冲区。这样,每次看到的都是绘制完毕的画面了,这样就不会看到不完整的画面了。
                       
最终结果如第二张图所示。当然,缓冲区M,N的名称是不会显示在图像中的,我们看到的最终就是 空图像->A->B->C。

暂停刷新

这个小节指的是每帧的绘制时间, 与视频刷新时间 不一致的时候,会出现什么样的情况。
每帧的绘制时间:每帧,就是指每一个静止的图像。要画一幅静止的图像,需要时间,每帧绘制时间,指的就是这个时间。
视频刷新时间: 每帧绘制完毕后,图像是放在缓冲区中,并没有真实显示出来。要显示出来,就需要由视频进行刷新。视频的刷新时间,是指视频将屏幕上的所有像素,刷新一次,需要的时间。视频的刷新时间,通常由视频刷新频率给出。

1. 每帧的绘制时间 < 视频刷新时间。 也就是说,缓冲区中的内容已经准备好了,视频还在刷新上一帧的数据。这其实可以接受,因为下一次视频刷新时,可以从缓冲区中取数据。
2. 每帧的绘制时间 > 视频刷新时间。 也就是说,我视频上一帧已经刷新完毕了,而缓冲区还没为我准备好下一帧的数据。这样视频刷新处于等待状态。这样的结果就是错过了视频刷新时间,只能等到下一次进行视频刷新了,这样画面就显得有点卡了。
3. 每帧的绘制时间 = 视频刷新时间。 按理说,这样的情况是最好的,谁也没等谁,两者同步了。但是,由于视频刷新时间是固定的,而每帧的绘制时间会有一些偏差。这样就会导致帧率的不稳定。帧率是指单位时间内的视频帧数。这样产生的效果就是,如果以一个人走路来打比方,就是走得时快时慢,并不匀速。

动画=重绘+交换

没什么可讲的了,直接看例子。
这个例子使用了双缓冲,使用双缓冲,有两处需要注意,一是main函数中的glutInitDisplayMode的参数,二是display函数末尾的glutSwapBuffers。
然后这个函数其他就是正常的绘制正方形。
只是多了一个正方形的旋转角度。spin这个角度在spinDisplay函数中增加,在display函数中使用。而spinDisplay函数,又是在mouse函数中指定为空闲函数。按下鼠标左键,当程序空闲时,就会调用spinDisplay,这个函数内部增加了spin的值,然后通过调用glutPostRedisplay函数,又让程序去调用display函数。这样,下一帧的正方形,就在上一帧的基础上,旋转了2度。只是现在的计算机足够快了,看起来这个正方形的旋转并不连贯。
#include <GL/glut.h>
#include <stdlib.h>

static GLfloat spin = 0.0;	//正方形的旋转角度

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);	//清除背景
	glPushMatrix();					//模型视图矩阵 入栈
	glRotatef(spin, 0.0, 0.0, 1.0);	//指定模型绕Z轴旋转,Z轴指向屏幕外面
	glColor3f(1.0, 1.0, 1.0);		//白色矩形
	glRectf(-25.0, -25.0, 25.0, 25.0);	//矩形
	glPopMatrix();					//模型视图矩阵出栈

	glutSwapBuffers();				//交换缓冲区
}

void spinDisplay(void)
{
	spin = spin + 2.0;				//旋转角+2度
	if (spin > 360.0)
		spin = spin - 360.0;		//避免旋转角超过360度
	glutPostRedisplay();			//重新绘制,这导致display函数被调用
}

void init(void)
{
	glClearColor(0.0, 0.0, 0.0, 0.0);	//背景清除颜色为黑色
	glShadeModel(GL_FLAT);				//使用单色绘制物体
}

void reshape(int w, int h)
{
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);	//指定视口大小
	glMatrixMode(GL_PROJECTION);				//进入投影矩阵
	glLoadIdentity();							//投影矩阵设置为单位矩阵
	glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);	//指定裁剪空间
	glMatrixMode(GL_MODELVIEW);					//返回模型视图矩阵
	glLoadIdentity();							//模型视图矩阵设置为单位矩阵
}

void mouse(int button, int state, int x, int y)
{
	switch (button) {
	case GLUT_LEFT_BUTTON:
		if (state == GLUT_DOWN)
			glutIdleFunc(spinDisplay);	//鼠标左键按下时,设置空闲函数为spinDisplay
		break;							//每当程序空闲时,就会调用spinDisplay函数
	case GLUT_MIDDLE_BUTTON:
		if (state == GLUT_DOWN)
			glutIdleFunc(NULL);			//鼠标中键按下时,设置空闲函数为空,空闲时不做任何事
		break;
	default:
		break;
	}
}

/*
*  Request double buffer display mode.
*  Register mouse input callback functions
*/
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);	//使用双缓冲模式,使用RGB颜色模式
	glutInitWindowSize(250, 250);		//指定窗口大小
	glutInitWindowPosition(100, 100);	//窗口初始位置
	glutCreateWindow(argv[0]);			//创建窗口
	init();								//初始化
	glutDisplayFunc(display);			//指定显示函数
	glutReshapeFunc(reshape);			//指定窗口尺寸变化时的函数
	glutMouseFunc(mouse);				//指定鼠标事件对应的处理函数
	glutMainLoop();						//进入消息循环
	return 0;   /* ANSI C requires main to return int. */
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值