模拟简单的太阳系,如图A.8所示。太阳在中心,地球每365天绕太阳转一周,月球每年绕地球转12周。另外,地球每天24个小时绕它自己的轴旋转。
图A.8 太阳系动画
本节实验绘制了一个简单的太阳系。为了编写这个程序,需要使用glRtate函数让这颗行星绕太阳旋转,并且绕自身的轴旋转。还需要使用glTranslate函数让这颗行星远离太阳系原点,移动到自己的轨道上。可以在glutWireSphere函数中使用适当的参数,在绘制两个球体时指定球体的大小。 为了绘制这个太阳系,首先需要设置一个投影变换和一个视图变换。在这个例子中,可以使用glutPerspective函数和gluLookAt函数。 绘制太阳比较简单,因为它位于全局固定坐标系统的原点,也就是球体函数进行绘图的位置。因此,绘制太阳时并不需要移动,可以使用glRotate*函数绕一个任意的轴旋转。绘制一颗绕太阳旋转的行星要求进行几次模型变换。这颗行星需要每天绕自己的轴旋转一周,每年沿着自己的轨道绕太阳旋转一周。 为了确定模型变换的顺序,可以从局部坐标系统的角度考虑。首先,调用初始的glRotate函数对局部坐标系统进行旋转,这个局部坐标系统最初与全局固定坐标系统是一致的。接着,可以调用glTranslate函数把局部坐标系统移动到行星轨道上的一个位置。移动的距离应该等于轨道的半径。因此,第一个glRotate函数实际上确定了这颗行星从什么地方开始绕太阳旋转(或者说,从一年的什么时候开始)。 第二次调用glRotate函数使局部坐标轴进行旋转,因此确定了这颗行星在一天中的时间。当调用了这些函数变换之后,就可以绘制这颗行星了。
双缓存技术能在一个屏幕之外的缓冲区内进行渲染,再用交换命令把图形放到屏幕上。双缓存技术的主要用途是: ①有些复杂图形绘制时间较长,但不需要显示绘制图形的所有步骤,只有整幅图像绘制完之后,才将其置于屏幕上; ②用于制作动画,动画中每一帧都再画面外缓冲区绘制,绘制完之后再交换到屏幕上。实际编程过程中,每个OpenGL支持的窗口系统都可以通过调用glutSwapBuffers()来实现前后缓冲区之间的交换。
#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); //画地球
glPopMatrix();
glPopMatrix();
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); //画月球
glLoadIdentity();
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;
}
(1)让实验6的茶壶旋转; (2)让实验7的机器人手臂不停旋转划圈。