OpenGL-动画

动画:就是连续播放一系列图片的过程,如果每秒播放超过24张图片则会认为是连续的,每秒播放的越多,则越平滑。

1.双缓冲技术

计算机上的动画和实际动画的不同之处:
	实际的动画都是提前画好了,播放的时候直接拿出来显示就可以。
	计算机动画则是,画一张,拿一张,并不是提前画好的。

这样会出现一个问题,就是对于简单动画,计算机画的速度可以跟上播放的速度,那么对于复杂的动画,则绘制时间较长,可能还没绘制完毕,就得显示,那么对于这种问题,提出了一种解决方案,称之为 双缓冲技术。

所谓的双缓冲技术就是:就是设置两块画面绘制区域,一块作为发送到显示器的数据,一块作为绘画的区域,在适当的时候交换它们,由于交换两块内存区域实际上只需交换两个指针即可,这一方法效率非常高,所以被广泛采用。
// 启用双缓冲功能, 使用GLUT 工具包 main函数里面这样写
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
// GLUT_SINGLE 表示单缓冲,改成GLUT_DOUBLE 就表示双缓冲

2. 实现连续动画

在实现连续动画的时候,一般方法是:如果你需要绘制窗口了,那就请调用
myDispaly函数。单由于我们的程序无法知道究竟在什么时候该绘制,因为
一般的窗口操作系统,都是支持多窗口同时显示。恰好你的程序窗口被别的
窗口遮住,然后用户又把原来的窗口移开,这样就需要对窗口进行重新绘制。
然而这一切,我们无法预料,只能委托操作系统代为办理。

这里我们使用glutIdleFunc 来实现在 cpu 空闲时间来调用绘制窗口函数,
重新绘制窗口,实现动画效果。
// 太阳 月亮 地球案例
#include <GL/glut.h>
static int day = 200;
void myDispay(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(75, 1, 1, 400000000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

	// 绘制红色的“太阳”
	glColor3f(1.0f, 0.0f, 0.0f);
	glutSolidSphere(69600000, 20, 20);
	// 绘制蓝色的“地球”
	glColor3f(0.0f, 0.0f, 1.0f);
	glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);
	glTranslatef(150000000, 0.0f, 0.0f);
	glutSolidSphere(15945000, 20, 20);
	// 绘制黄色的“月亮”
	glColor3f(1.0f, 1.0f, 0.0f);
	glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);
	glTranslatef(38000000, 0.0f, 0.0f);
	glutSolidSphere(4345000, 20, 20);

	glFlush();
	// 这里绘制完毕后,需要交换缓冲区
	glutSwapBuffers();
}
void myIdle(void)
{
	/*
		作用是把日期往后移动一天,并重新绘制 达到动画效果
	*/
	if(++day >= 360)
	{
		day = 0;
	}
	myDisplay(); // 重新绘制窗口
}
int main(int argc,char *argv[])
{
	glutInit(&argc,argv);
	// 使用双缓冲区
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowPostion(100,100);
	glutInitWindowSize(400,400);
	glutCreateWindow("太阳,地球和月亮");
	glutDisplayFunc(&myDisplay);
	// 该句 改变day 重新绘制窗口、 实现动画效果
	// glutIdleFunc 可以使得CPU 在空闲时间 调用某一函数干某些事情
	glutIdleFunc(&myIdle);
	glutMainLoop();
	return 0;
}

3. 垂直同步

由于显示器的刷新频率有限,如果让计算机绘制一个简单图形,则有可能一秒钟
绘制上万次,因此,如果最大限度的利用计算机处理能力,绘制出很多副画面,
但是显示器的刷新速度却跟不上,这样不仅造成性能的浪费,还有可能带来一些负
面影响(比如 当显示器只刷新到一半时候,需要绘制的内容变化了,由于显示器
是逐行刷新,所以会导致显示器上半部分和下班部分是来自两幅不同的画面。)

采用垂直同步技术可以解决这一问题。当只有在显示器刷新时候,才把绘制好的图
像传输显示。这样,计算机就不必去绘制大量的根本用不到的图像了。

单也存在问题:若刷新频率为60Hz,在简单场景,绘制一幅画需要的时间很短,
帧速可以恒定60FPS(60帧/s),若场景复杂,绘制一幅画的时间超过了1/60秒,则
帧速将急剧下降,如果绘制一幅画时间为 1/50,则在第一个1/ 60 秒时,显示器
需要刷新了,但由于新的图没有绘制好,所以只能显示原来的图画,等到下一个
1/60 秒时才显示新图画,于是显示一幅画实际上用了1 /30 秒,帧数为30FPS。

试想如果一幅图的复杂程度不一致,且绘制它们的时间都在1/60,则在1/60时间内
画完,则帧数为60FPS,,在1/60时间未完成,帧速为30FPS,这样就造成帧速跳动。

4. 计算帧速

定义: 帧速就是一秒钟内播放的画面数目(FPS)
#include <time.h>
double CalFrequency()
{
	/*帧速计算*/
	static int cout;
	static double save;
	static clock_t last,current;
	double timegap;

	++count;
	if(cout <= 50)
	{
		return save;
	}
	count = 0;
	last = current;
	current = clock();
	timegap = (current - last) / (double)CLK_TCK;
	save = 50.0 / timegap; // 按照 50FPS 计算 瞬时帧速
	return save;
}

天体运动完整代码

#include <GL/glut.h>
#include <stdio.h>
#include <time.h>

static int day = 200;
double CalFrequency()
{
	/* 计算帧速*/
	static int count;
	static double save;
	static clock_t last, current;
	double timegap;
	++count;
	if( count <= 50 )
	{
		return save;
	}
		
	count = 0;
	last = current;
	current = clock();
	timegap = (current-last)/(double)CLK_TCK;
	save = 50.0/timegap;
	return save;
}
void myDisplay(void)
{
	double FPS = CalFrequency();
	printf("FPS = %lf\n", FPS);
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(75, 1, 1, 400000000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

	// 绘制红色的“太阳”
	glColor3f(1.0f, 0.0f, 0.0f);
	glutSolidSphere(69600000, 20, 20);
	// 绘制蓝色的“地球”
	glColor3f(0.0f, 0.0f, 1.0f);
	glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);
	glTranslatef(150000000, 0.0f, 0.0f);
	glutSolidSphere(15945000, 20, 20);
	// 绘制黄色的“月亮”
	glColor3f(1.0f, 1.0f, 0.0f);
	glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);
	glTranslatef(38000000, 0.0f, 0.0f);
	glutSolidSphere(4345000, 20, 20);

	glFlush();
	glutSwapBuffers();
}
void myIdle(void)
{
	++day;
	if( day >= 360 )
	{
		day = 0;
	}
	myDisplay();
}
int main(int argc,char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(400, 400);
	glutCreateWindow("太阳,地球和月亮");
	glutDisplayFunc(&myDisplay);
	glutIdleFunc(&myIdle);
	glutMainLoop();
	return 0;
}
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值