OpenGL学习笔记(6)第一个动画
2010-10-17 16:24 Clingingboy 阅读(11603) 评论(0) 编辑 收藏 举报首先再说明几个回调函数
- glutMouseFunc用于捕获鼠标事件
- glutKeyboardFunc用于捕获键盘事件
- glutMotionFunc用于鼠标按下又移动鼠标的事件(MouseMove)
- glutIdleFunc事件,当循环队列处于空闲时则触发该事件
- glutTimerFunc单位时间内内触发事件
第一个动画
#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); 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; if (spin > 360.0) spin = spin - 360.0; glutPostRedisplay(); } 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); break; case GLUT_MIDDLE_BUTTON: case GLUT_RIGHT_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); 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. */ }
当鼠标左键按下时则开始不断执行spinDisplay方法,这动画将使得矩形不断的旋转
下面我们来看相关代码
static GLfloat spin = 0.0; void display(void) { glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); glRotatef(spin, 0.0, 0.0, 1.0); 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; if (spin > 360.0) spin = spin - 360.0; glutPostRedisplay(); }
矩阵堆栈
每次都将spin加2,这样的话spin其实是矩形旋转度数的总和,但如何维护矩形初始化(未旋转前)的矩阵呢?
即将为旋转前的当前矩阵压入(glPushMatrix)矩阵堆栈中,然后当旋转结束后又弹出(glPopMatrix)堆栈恢复,当spin增加时,又以原始矩阵来合并,这样就不会出现什么问题了.
还有一种做法则是去掉矩阵堆栈的压入与弹出,使得spin每次都是等于2,但度数就可能会超过360,造成数字过大转换,如
void display(void) { glClear(GL_COLOR_BUFFER_BIT); //glPushMatrix(); glRotatef(spin, 0.0, 0.0, 1.0); glColor3f(1.0, 1.0, 1.0); glRectf(-25.0, -25.0, 25.0, 25.0); //glPopMatrix(); glutSwapBuffers(); } void spinDisplay(void) { spin = 2.0; if (spin > 360.0) spin = spin - 360.0; glutPostRedisplay(); }
glutPostRedisplay方法将调用display方法进行强制刷新
使用glutTimerFunc
glutTimerFunc回调事件只执行一次,如果想一直执行的话就需要在内部再次调该方法,下面用glutTimerFunc来替代glutIdleFunc实现同样的效果
void spinDisplay(int value) { spin = spin + 2.0; if (spin > 360.0) spin = spin - 360.0; glutPostRedisplay(); glutTimerFunc(10, spinDisplay, 1); }
当然首先要外部先调用一次spinDisplay方法才可以
双缓冲技术
下面这段话是转载
总体意思为两个区域一个显示,一个则负责画图,然后连续切换
在计算机上的动画与实际的动画有些不同:实际的动画都是先画好了,播放的时候直接拿出来显示就行。计算机动画则是画一张,就拿出来一张,再画下一张,再拿出来。如果所需要绘制的图形很简单,那么这样也没什么问题。但一旦图形比较复杂,绘制需要的时间较长,问题就会变得突出。
让我们把计算机想象成一个画图比较快的人,假如他直接在屏幕上画图,而图形比较复杂,则有可能在他只画了某幅图的一半的时候就被观众看到。而后面虽然他把画补全了,但观众的眼睛却又没有反应过来,还停留在原来那个残缺的画面上。也就是说,有时候观众看到完整的图象,有时却又只看到残缺的图象,这样就造成了屏幕的闪烁。
如何解决这一问题呢?我们设想有两块画板,画图的人在旁边画,画好以后把他手里的画板与挂在屏幕上的画板相交换。这样以来,观众就不会看到残缺的画了。这一技术被应用到计算机图形中,称为双缓冲技术。即:在存储器(很有可能是显存)中开辟两块区域,一块作为发送到显示器的数据,一块作为绘画的区域,在适当的时候交换它们。由于交换两块内存区域实际上只需要交换两个指针,这一方法效率非常高,所以被广泛的采用。
注意:虽然绝大多数平台都支持双缓冲技术,但这一技术并不是OpenGL标准中的内容。OpenGL为了保证更好的可移植性,允许在实现时不使用双缓冲技术。当然,我们常用的PC都是支持双缓冲技术的。
要启动双缓冲功能,最简单的办法就是使用GLUT工具包。我们以前在main函数里面写:
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
其中GLUT_SINGLE表示单缓冲,如果改成GLUT_DOUBLE就是双缓冲了。
当然还有需要更改的地方——每次绘制完成时,我们需要交换两个缓冲区,把绘制好的信息用于屏幕显示(否则无论怎么绘制,还是什么都看不到)。如果使用GLUT工具包,也可以很轻松的完成这一工作,只要在绘制完成时简单的调用glutSwapBuffers函数就可以了。