OpenGL动画

来源:互联网 发布:点手机域名注册 编辑:程序博客网 时间:2024/04/28 04:37

OpenGL动画和传统意义上的动画相似,都是把画面一幅一幅的呈现在观众面前。一旦画面变换的速度快了,观众就会认为画面是连续的。双缓冲技术是一种在计算机图形中普遍采用的技术,绝大多数OpenGL实现都支持双缓冲技术。通常都是利用CPU空闲的时候绘制动画,但也可以有其它的选择。

  双缓冲技术

在计算机上的动画与实际的动画有些不同:实际的动画都是先画好了,播放的时候直接拿出来显示就行。

计算机动画则是画一张,就拿出来一张,再画下一张,再拿出来。

如果所需要绘制的图形很简单,那么这样也没什么问题。但一旦图形比较复杂,绘制需要的时间较长,问题就会变得突出。

让我们把计算机想象成一个画图比较快的人,假如他直接在屏幕上画图,而图形比较复杂,则有可能在他只画了某幅图的一半的时候就被观众看到。而后面虽然他把画补全了,但观众的眼睛却又没有反应过来,还停留在原来那个残缺的画面上。也就是说,有时候观众看到完整的图象,有时却又只看到残缺的图象,这样就造成了屏幕的闪烁

如何解决这一问题呢?我们设想有两块画板,画图的人在旁边画,画好以后把他手里的画板与挂在屏幕上的画板相交换。这样以来,观众就不会看到残缺的画了。

这一技术被应用到计算机图形中,称为双缓冲技术。即:在存储器(很有可能是显存)中开辟两块区域,一块作为发送到显示器的数据,一块作为绘画的区域,在适当的时候交换它们。由于交换两块内存区域实际上只需要交换两个指针,这一方法效率非常高,所以被广泛的采用。

注意:虽然绝大多数平台都支持双缓冲技术,但这一技术并不是OpenGL标准中的内容。OpenGL为了保证更好的可移植性,允许在实现时不使用双缓冲技术。当然,我们常用的PC都是支持双缓冲技术的。

要启动双缓冲功能,最简单的办法就是使用GLUT工具包。我们以前在main函数里面写:

glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

其中GLUT_SINGLE表示单缓冲,如果改成GLUT_DOUBLE就是双缓冲了。

当然还有需要更改的地方——每次绘制完成时,我们需要交换两个缓冲区,把绘制好的信息用于屏幕显示(否则无论怎么绘制,还是什么都看不到)。如果使用GLUT工具包,也可以很轻松的完成这一工作,只要在绘制完成时简单的调用glutSwapBuffers函数就可以了。


glMatrixMode

这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对什么进行操作了,这个什么glMatrixMode“()”里的选项(参数)有,GL_PROJECTIONGL_MODELVIEWGL_TEXTURE

如果参数是GL_PROJECTION,这个是投影的意思,就是要对投影相关进行操作,也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如 glFrustum() 或 gluPerspective()

如果参数是GL_MODELVIEW,这个是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像 gluLookAt() 这样的函数;

若是GL_TEXTURE,就是对纹理相关进行操作;

OpenGL里面的操作,很多是基于对矩阵的操作的,比如位移,旋转,缩放。

所以,这里其实说的规范一点就是glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标,GL_PROJECTION是对投影矩阵操作,GL_MODELVIEW是对模型视景矩阵操作,GL_TEXTURE是对纹理矩阵进行随后的操作。

==========================================================

下面这个函数的解释来自:http://www.cppblog.com/COOOOOOOOL/archive/2009/12/28/104255.html   解释的真很好哇~~哈哈

gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar)
设置透视投影矩阵
首先得设置gluPerspective,来看看它的参数都表示什么意思
fovy,这个最难理解,我的理解是,眼睛睁开的角度,即,视角的大小,如果设置为0,相当你闭上眼睛了,所以什么也看不到,如果为180,那么可以认为你的视界很广阔,
aspect,这个好理解,就是实际窗口的纵横比,即x/y
zNear,这个呢,表示你近处,的裁面,
zFar表示远处的裁面,

如果还没有理解就继续看,
我们知道,远处的东西看起来要小一些,近处的东西看起来会大一些,这就是透视原理
如下图所示

================================================================================================

下面这个函数 来自 百度百科:http://baike.baidu.com/view/2341537.htm

 视点转换

  函数原型:
  void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);

参数:

 gluLookAt()共有九个参数,分别是眼睛的位置,眼睛朝向的位置,以及相片朝上的方向。

  该函数定义了视点矩阵,并用该矩阵乘以当前矩阵。eyex、eyey、eyez定义了视点的位置;centerx、centery和centerz变量指定了参考点的位置,该点通常为相机所瞄准的场景中心轴线上的点;upx、upy、upz变量指定了向上向量的方向。

说明:

 通常,视点转换操作在模型转换操作之后发出,以便模型转换先对物体发生作用。场景中物体的顶点经过模型转换之后移动到所希望的位置,然后再对场景进行视点定位等操作。模型转换和视点转换共同构成模型视景矩阵

  这个函数是对模型矩阵进行变换(GL_MODELVIEW),而gluPerspective函数是对投影矩阵进行变换(GL_PROJECTION),这一点一定要搞清楚。
  你可以用gluPerspective函数设置近平面、远平面

介绍了垂直同步的相关知识。

介绍了一种简单的计算帧速(FPS)的方法。最后,我们列出了一份完整的天体动画程序清单。

code:

[cpp] view plaincopy#include <GL/glut.h>  #include <stdio.h>  #include <time.h>    // 太阳、地球和月亮  // 假设每个月都是12天  // 一年12个月,共是360天  static int day = 100; // 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;  }  

result:


地球围绕太阳转。月球围绕地球和太阳转。



0 0
原创粉丝点击