为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

OpenGL入门学习之六——动画的制作

2011-05-06 6页 pdf 137KB 19阅读

用户头像

is_602166

暂无简介

举报
OpenGL入门学习之六——动画的制作 OpenGL 入门学习之六——动画的制作 2009-01-07 11:45 本次课程,我们将进入激动人心的计算机动画世界。 想必大家都知道电影和动画的工作原理吧?是的,快速的把看似连续的画面一幅幅的呈现在 人们面前。一旦每秒钟呈现的画面超过 24 幅,人们就会错以为它是连续的。 我们通常观看的电视,每秒播放 25 或 30 幅画面。但对于计算机来说,它可以播放更多的画 面,以达到更平滑的效果。如果速度过慢,画面不够平滑。如果速度过快,则人眼未必就能 反应得过来。对于一个正常人来说,每秒 60~120 幅图画是比较合适的...
OpenGL入门学习之六——动画的制作
OpenGL 入门学习之六——动画的制作 2009-01-07 11:45 本次课程,我们将进入激动人心的计算机动画世界。 想必大家都知道电影和动画的工作原理吧?是的,快速的把看似连续的画面一幅幅的呈现在 人们面前。一旦每秒钟呈现的画面超过 24 幅,人们就会错以为它是连续的。 我们通常观看的电视,每秒播放 25 或 30 幅画面。但对于计算机来说,它可以播放更多的画 面,以达到更平滑的效果。如果速度过慢,画面不够平滑。如果速度过快,则人眼未必就能 反应得过来。对于一个正常人来说,每秒 60~120 幅图画是比较合适的。具体的数值因人而 异。 假设某动画一共有 n 幅画面,则它的工作步骤就是: 显示第 1 幅画面,然后等待一小段时间,直到下一个 1/24 秒 显示第 2 幅画面,然后等待一小段时间,直到下一个 1/24 秒 …… 显示第 n 幅画面,然后等待一小段时间,直到下一个 1/24 秒 结束 如果用 C 语言伪代码来描述这一过程,就是: for(i=0; i
示单缓冲,如果改成 GLUT_DOUBLE 就是双缓冲了。 当然还有需要更改的地方——每次绘制完成时,我们需要交换两个缓冲区,把绘制好的信息 用于屏幕显示(否则无论怎么绘制,还是什么都看不到)。如果使用 GLUT 工具包,也可以 很轻松的完成这一工作,只要在绘制完成时简单的调用 glutSwapBuffers函数就可以了。 2、实现连续动画 似乎没有任何疑问,我们应该把绘制动画的代码写成下面这个样子: for(i=0; i // 太阳、地球和月亮 // 假设每个月都是 30天 // 一年 12 个月,共是 360天 static int day = 200; // day 的变化:从 0 到 359 void myDisplay(void) { /**************************************************** 这里的内容照搬上一课的,只因为使用了双缓冲,补上最后这句 *****************************************************/ 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); // 修改了参数为 GLUT_DOUBLE glutInitWindowPosition(100, 100); glutInitWindowSize(400, 400); glutCreateWindow("太阳,地球和月亮"); // 改了窗口标题 glutDisplayFunc(&myDisplay); glutIdleFunc(&myIdle); // 新加入了这句 glutMainLoop(); return 0; } 3、关于垂直同步 代码是写好了,但相信大家还有疑问。某些朋友可能在运行时发现,虽然 CPU几乎都用上 了,但运动速度很快,根本看不清楚,另一些朋友在运行时发现 CPU使用率很低,根本就 没有把空闲时间完全利用起来。但对于上面那段代码来说,这些现象都是合理的。这里就牵 涉到关于垂直同步的问题。 大家知道显示器的刷新率是比较有限的,一般为 60~120Hz,也就是一秒钟刷新 60~120 次。 但如果叫计算机绘制一个简单的画面,例如只有一个三角形,则一秒钟可以绘制成千上万次。 因此,如果最大限度的利用计算机的处理能力,绘制很多幅画面,但显示器的刷新速度却跟 不上,这不仅造成性能的浪费,还可能带来一些负面影响(例如,显示器只刷新到一半时, 需要绘制的内容却变化了,由于显示器是逐行刷新的,于是显示器上半部分和下半部分实际 上是来自两幅画面)。采用垂直同步技术可以解决这一问题。即,只有在显示器刷新时,才 把绘制好的图象传输出去供显示。这样一来,计算机就不必去绘制大量的根本就用不到的图 象了。如果显示器的刷新率为 85Hz,则计算机一秒钟只需要绘制 85 幅图象就足够,如果场 景足够简单,就会造成比较多的 CPU空闲。 几乎所有的显卡都支持“垂直同步”这一功能。 垂直同步也有它的问题。如果刷新频率为 60Hz,则在绘制比较简单的场景时,绘制一幅图 画需要的时间很段,帧速可以恒定在 60FPS(即 60帧/秒)。如果场景变得复杂,绘制一幅 图画的时间超过了 1/60 秒,则帧速将急剧下降。 如果绘制一幅图画的时间为 1/50,则在第一个 1/60 秒时,显示器需要刷新了,但由于新的 图画没有画好,所以只能显示原来的图画,等到下一个 1/60 秒时才显示新的图画。于是显 示一幅图画实际上用了 1/30 秒,帧速为 30FPS。(如果不采用垂直同步,则帧速应该是 50FPS) 如果绘制一幅图画的时间更长,则下降的趋势就是阶梯状的:60FPS,30FPS,20FPS,…… (60/1,60/2,60/3,……) 如果每一幅图画的复杂程度是不一致的,且绘制它们需要的时间都在 1/60 上下。则在 1/60 时间内画完时,帧速为 60FPS,在 1/60 时间未完成时,帧速为 30FPS,这就造成了帧速的跳 动。这是很麻烦的事情,需要避免它——要么想办法简化每一画面的绘制时间,要么都延迟 一小段时间,以作到统一。 回过头来看前面的问题。如果使用了大量的 CPU 而且速度很快无法看清,则打开垂直同步 可以解决该问题。当然如果你认为垂直同步有这样那样的缺点,也可以关闭它。——至于如 何打开和关闭,因操作系统而异了。具体步骤请自己搜索之。 当然,也有其它办法可以控制动画的帧速,或者尽量让动画的速度尽量和帧速无关。不过这 里面很多内容都是与操作系统比较紧密的,况且它们跟OpenGL关系也不太大。这里就不做 介绍了。 4、计算帧速 不知道大家玩过 3D Mark 这个软件没有,它可以运行各种场景,测出帧速,并且为你的系统 给出评分。这里我也介绍一个计算帧速的方法。 根据定义,帧速就是一秒钟内播放的画面数目(FPS)。我们可以先测量绘制两幅画面之间 时间 t,然后求它的倒数即可。假如 t=0.05s,则 FPS 的值就是 1/0.05=20。 理论上是如此了,可是如何得到这个时间呢?通常 C 语言的 time函数精确度一般只到一秒, 肯定是不行了。clock函数也就到十毫秒左右,还是有点不够。因为 FPS 为 60 和 FPS 为 100 的时候,t 的值都是十几毫秒。 你知道如何测量一张纸的厚度吗?一个粗略的办法就是:用很多张纸叠在一起测厚度,计算 平均值就可以了。我们这里也可以这样办。测量绘制 50 幅画面(包括垂直同步等因素的等 待时间)需要的时间 t',由 t'=t*50 很容易的得到 FPS=1/t=50/t' 下面这段代码可以统计该函数自身的调用频率,(原理就像上面说的那样),程序并不复杂, 并且这并不属于 OpenGL 的内容,所以我不打算详细讲述它。 #include 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; } 最后,要把计算的帧速显示出来,但我们并没有学习如何使用 OpenGL 把文字显示到屏幕上。 ——但不要忘了,在我们的图形窗口背后,还有一个命令行窗口~使用 printf函数就可以轻 易的输出文字了。 #include double FPS = CalFrequency(); printf("FPS = %f\n", FPS); 最后的一步,也被我们解决了——虽然做法不太雅观,没关系,以后我们还会改善它的。 现在,我给出一个比较完整的程序,供大家参考。 #include #include #include // 太阳、地球和月亮 // 假设每个月都是 12天 // 一年 12 个月,共是 360天 static int day = 200; // day 的变化:从 0 到 359 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 = %f\n", FPS); glEnable(GL_DEPTH_TEST); 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; } 小结: OpenGL 动画和传统意义上的动画相似,都是把画面一幅一幅的呈现在观众面前。一旦画面 变换的速度快了,观众就会认为画面是连续的。 双缓冲技术是一种在计算机图形中普遍采用的技术,绝大多数 OpenGL 实现都支持双缓冲技 术。 通常都是利用 CPU空闲的时候绘制动画,但也可以有其它的选择。 介绍了垂直同步的相关知识。 介绍了一种简单的计算帧速(FPS)的方法。 最后,我们列出了一份完整的天体动画程序
/
本文档为【OpenGL入门学习之六——动画的制作】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索