在上一节中,我们知道了怎么在OpenGL程序里,画一些基本的图形,并且画了一个钟表出来。然而,这个钟的时间是我们事先给它的,所以它只能显示一个固定的时间。所以,这一节我们将来学习怎么让画面动起来!
我希望这个表能够获取系统时间,并且随着系统时间的改变,指针的读数也会改变。
在实现动画这一块,OpenGL给我们提供了双缓冲。当A缓冲区显示时,B缓冲区正在绘制图形。当B绘制完毕时,就交换缓冲区,此时B显示,而A就进行绘画。从而实现平滑地显示每一帧地显示,观众就永远看不到没有完成的画面。计算机的在交换缓冲区的时候,速度很快,所以一般用户是觉察不到的。
为了能让我们的程序能够使用双缓冲,我们需要改变main函数中glutInitDisplayMode()函数的参数,把GLUT_SINGLE改为GLUT_DOUBLE,表示我们现在要使用双缓冲(double)了,以前是单缓冲(single)。
glutInitDisplayMode(GLUT_RGB| GLUT_DOUBLE);
在openGL中,并没有提供交换缓冲这个函数,因为有些硬件不支持这个特性。对于不同的操作系统,有不同的方法。我们使用的GLUT辅助库,帮我们解决了这个问题,我们只用调用glutSwapBuffers();函数就OK了。
所以我们再把Draw函数中的glFlush()函数换成glutSwapBuffers()。就可以了。
这个时候编译运行,会同样的出现钟表,画面并没有动起来,因为Draw函数只调用了一次,我们希望程序能不停地显示。怎么做呢?
我们在main函数中使用glutIdleFunc(&Update)函数,可以设置一个回调函数Update。设置了以后,Update便会在循环中不断地被调用,直到有窗口消息产生。这里暂时不管窗口消息是啥,我们就知道Update会不断被调用就是了。
于是我们写一个Update函数.
void Update()
{
glutPostRedisplay();
}
glutPostRedisplay函数,就和它的名字一样,它发送了一个消息给glut窗口,告诉它重新绘制一下画面,结果就是Draw函数会被调用。
这时我们再编译运行一次,发现同样可以显示画面。其实这个时候,画面是在不断地更新的,只是你感觉不出来而已。
指针并没有动,因为指针的读数和h,m,s三个参数有关,而这三个参数的值是固定的。把这三个参数改为全局变量,让其在其他函数中也可以被使用,然后在Update函数中,更新h,m,s的值为当前系统的时间就可以了。最后记得去掉draw函数中的
h=10;
m=25;
s=25;
void Update()
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
h=timeinfo->tm_hour;
m=timeinfo->tm_min;
s=timeinfo->tm_sec;
glutPostRedisplay();
}
这个时候,在编译运行,效果就出来了!
附本节全部代码:
#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#define PI 3.1415926
float h,m,s;
void Draw()
{
int i;
float R,TR,h_Angle,m_Angle,s_Angle,count,h_Length,m_Length,s_Length;
R=0.5;
TR=R-0.05;
glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(5);
glBegin(GL_LINE_LOOP);
for (i=0; i<100; i++)
{
glVertex2f(R*cos(2*PI/100*i),R*sin(2*PI/100*i));
}
glEnd();
glLineWidth(2);
for (i=0; i<12; i++)
{
glBegin(GL_LINES);
glVertex2f(TR*sin(2*PI/12*i),TR*cos(2*PI/12*i));
glVertex2f(R*sin(2*PI/12*i),R*cos(2*PI/12*i));
glEnd();
}
glLineWidth(1);
h_Length=0.2;
m_Length=0.3;
s_Length=0.4;
count=60;
s_Angle=s/count;
count*=60;
m_Angle=(m*60+s)/count;
count*=12;
h_Angle=(h*60*60+m*60+s)/count;
glLineWidth(1);
glBegin(GL_LINES);
glVertex2f(0.0f,0.0f);
glVertex2f(s_Length*sin(2*PI*s_Angle),s_Length*cos(2*PI*s_Angle));
glEnd();
glLineWidth(5);
glBegin(GL_LINES);
glVertex2f(0.0f,0.0f);
glVertex2f(h_Length*sin(2*PI*h_Angle),h_Length*cos(2*PI*h_Angle));
glEnd();
glLineWidth(3);
glBegin(GL_LINES);
glVertex2f(0.0f,0.0f);
glVertex2f(m_Length*sin(2*PI*m_Angle),m_Length*cos(2*PI*m_Angle));
glEnd();
glLineWidth(1);
glBegin(GL_POLYGON);
for (i=0; i<100; i++)
{
glVertex2f(0.03*cos(2*PI/100*i),0.03*sin(2*PI/100*i));
}
glEnd();
glutSwapBuffers();
}
void Update()
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
h=timeinfo->tm_hour;
m=timeinfo->tm_min;
s=timeinfo->tm_sec;
glutPostRedisplay();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("HelloOpenGL");
glutIdleFunc(&Update);
glutDisplayFunc(&Draw);
glutMainLoop();
return 0;
}