1.实验目的:
- 熟悉颜色缓存、深度缓存、模板缓存、累计缓存的内容,掌握缓存清除的方法;
- 建立太阳、地球、月亮的运动模型;
- 利用双缓存技术,用动画方式显示模型,加深读者对几何变换、投影变换以及观察变换的理解,并提高利用图形软件包绘制图形的能力。
2.实验内容:
模拟简单的太阳系,如图A.8所示。太阳在中心,地球每365天绕太阳转一周,月球每年绕地球转12周。另外,地球每天24个小时绕它自己的轴旋转。
3.实验原理:
(1)主要用三维平移变换、旋转变换实现太阳、地球、月亮的相对运动。
本节实验绘制了一个简单的太阳系。为了编写这个程序,需要使用glRtate函数让这颗行星绕太阳旋转,并且绕自身的轴旋转。还需要使用glTranslate函数让这颗行星远离太阳系原点,移动到自己的轨道上。可以在glutWireSphere函数中使用适当的参数,在绘制两个球体时指定球体的大小。
为了绘制这个太阳系,首先需要设置一个投影变换和一个视图变换。在这个例子中,可以使用glutPerspective函数和gluLookAt函数。
绘制太阳比较简单,因为它位于全局固定坐标系统的原点,也就是球体函数进行绘图的位置。因此,绘制太阳时并不需要移动,可以使用glRotate*函数绕一个任意的轴旋转。绘制一颗绕太阳旋转的行星要求进行几次模型变换。这颗行星需要每天绕自己的轴旋转一周,每年沿着自己的轨道绕太阳旋转一周。
为了确定模型变换的顺序,可以从局部坐标系统的角度考虑。首先,调用初始的glRotate函数对局部坐标系统进行旋转,这个局部坐标系统最初与全局固定坐标系统是一致的。接着,可以调用glTranslate函数把局部坐标系统移动到行星轨道上的一个位置。移动的距离应该等于轨道的半径。因此,第一个glRotate函数实际上确定了这颗行星从什么地方开始绕太阳旋转(或者说,从一年的什么时候开始)。
第二次调用glRotate函数使局部坐标轴进行旋转,因此确定了这颗行星在一天中的时间。当调用了这些函数变换之后,就可以绘制这颗行星了。
(2)利用双缓存技术实现动画效果。
双缓存技术能在一个屏幕之外的缓冲区内进行渲染,再用交换命令把图形放到屏幕上。双缓存技术的主要用途是:
①有些复杂图形绘制时间较长,但不需要显示绘制图形的所有步骤,只有整幅图像绘制完之后,才将其置于屏幕上;
②用于制作动画,动画中每一帧都再画面外缓冲区绘制,绘制完之后再交换到屏幕上。实际编程过程中,每个OpenGL支持的窗口系统都可以通过调用glutSwapBuffers()来实现前后缓冲区之间的交换。
4.实验代码:
#include <gl/glut.h>
float fEarth = 2.0f; //地球绕太阳的旋转角度
float fMoon = 24.0f; //月球绕地球的旋转角度
void myInit()
{
glEnable(GL_DEPTH_TEST); //启用深度测试
glClearColor(0.0f, 0.0f, 0.0f, 0.8f); //背景为黑色
}
void myReshape(int w, int h)
{
if (0 == h)
h = 1;
glViewport(0, 0, w, h); //设置视区尺寸
glMatrixMode(GL_PROJECTION); //指定当前操作投影矩阵堆栈
glLoadIdentity(); //重置投影矩阵
//指定透视投影的观察空间
gluPerspective(45.0f, (float)w / (float)h, 1.0f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void myDisplay(void)
{
//清除颜色和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); //指定当前操作模型视图矩阵堆栈
glLoadIdentity(); //重置模型视图矩阵
glTranslatef(0.0f, 0.0f, -500.0f); //将图形沿z轴负向移动
glColor3f(1.0f, 0.0f, 0.0f); //画太阳
glutSolidSphere(50.0f, 20, 20);
glColor3f(0.0f, 0.0f, 1.0f);
glRotatef(23.27,0.0,0.0,1.0); //地球与太阳的黄赤交角
glRotatef(fEarth, 0.0f, 1.0f, 0.0f);
glTranslatef(200.0f, 0.0f, 0.0f);
glutSolidSphere(20.0f, 20, 20); //画地球
glRotatef(6.0f, 1.0f, 1.0f, 1.0f);
glRotatef(fMoon, 0.0f, 1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 0.0f);
glTranslatef(30.0f, 0.0f, 0.0f);
glutSolidSphere(5.0f, 20, 20); //画月球
glFlush();
glutSwapBuffers();
}
void myIdle(void) //在空闲时调用,达到动画效果
{
fEarth += 0.03f; //增加旋转步长,产生动画效果
if (fEarth > 360.0f)
fEarth = 2.0f;
fMoon += 0.24f;
if (fMoon > 360.0f)
fMoon = 24.0f;
myDisplay();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
//窗口使用RGB颜色,双缓存和深度缓存
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(100,100);
glutInitWindowSize(600, 400);
glutCreateWindow("太阳系动画");
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);
glutIdleFunc(&myIdle);
myInit();
glutMainLoop();
return 0;
}
5.实验提高
(1)让实验6的茶壶旋转;
(2)让实验7的机器人手臂不停旋转划圈。