完成一个初步的太阳系程序:三个球体,分别为太阳、地球和月亮。实现地球不停绕太阳旋转,月亮绕地球旋转。
扩展功能:实现两个月亮绕地球旋转,使轨道倾斜。
思路
如何实现自转和公转
在模型变换中使用
glTranslatef()
,glRotaef()
来操作,使用画球体函数gluWireSphere()来绘制。如果先旋转在绘制,则是表现为自转;而先绘制在旋转,再移动一定距离,则表现为公转。
变换的操作实际是对矩阵的操作
由于在实现坐标的变换是通过操作矩阵来实现的,即当前矩阵乘上对应操作的矩阵。当操作完一个变换函数后,矩阵也随之被改变。OpenGL中有
glPushMatrix()
,glPopMatrix()
两个函数,当变换后使用glPushMatrix()
来把变换后的位置和角度保存起来,当再做第二次变换时,使用glPopMatrix()
来把刚刚保存好的位置和角度等恢复。实现球体不停运动
通过使用一个定时器函数gluTimerFunc(),在单位时间内触发事件,调用glutPostRedisplay()来进行重绘。
还可以使用glutIdleFunc()函数,当循环队列处于空闲时则触发该事件。
下面来一些重要函数作出详细的说明。
一些重要函数的说明
glutWireSphere()
功能
用于渲染出球体(由经纬线构成)。球体球心位于原点,即是在窗口客户区的中心。
函数原型
1 | void glutWireSphere(GLdouble radius, GLint slices, GLint stacks); |
参数
radius球的半径
slices:以Z轴上线段为直径分布的圆周线的条数(将Z轴看成地球的地轴,类似于经线)
stacks:围绕在Z轴周围的线的条数(类似于地球上纬线)
类似的函数为:glutSolidSphere
glRotatef()
功能
以点(0,0,0)到点(x,y,z)为轴,旋转angle角度。
函数原型
1 | void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) |
参数
- angle:为旋转的角度,单位为度。
- x,y,z:为对应xyz轴的布尔值变量。0.0表示假,而非零参数则表示真。
做(0,0,0)到(x,y,z)的向量,用右手握住这条向量,大拇指指向向量的正方向,四指环绕的方向就是旋转的方向
如果设置的旋转值(x,y,z的值)为正数,那么旋转的方向是逆时针的,如果旋转值是负数,那么旋转的方向是顺时针的。
glTranslatef()
功能
将你绘点坐标的原点在当前原点的基础上平移一个(x,y,z)向量。
函数原型
1 | void glTranslatef(GLfloat x,GLfloat y,GLfloat z); |
glPushMatrix() && glPopMatrix()
glPushMatrix()
,glPopMatrix()
这两个函数是搭配使用的,中间放置几何变换。glPushMatrix
的作用是把矩阵压入栈中保存起来,留着以后再用。消除了上一次变换对这一次的影响,使本次变换基于世界坐标系的原点为参考点。
gluTimerFunc()
功能
定时器函数,为时球体能不停运动。
函数原型
1 | glutTimerFunc(unsigned int millis, void (*func)(int value), int value); |
参数
glutTimerFunc(毫秒数, 回调函数指针, 区别值);
用法
- 写自己的回调函数 void OnTimer(int value),用value来区分是哪个定时器
- 在函数里添加和位置有关的变量,然后调用glutPostRedisplay()来重绘
- 最后再次调用glutTimerFunc,因为glut的定时器是调用一次才产生一次定时,所以如果要持续产生定时的话,需要在内部再次调用该方法。
实现两个月亮的绘制
红宝书中有对绘制多个行星的解析
如果打算绘制几颗卫星绕着同一行星旋转,需要在移动每颗卫星的位置之前保存坐标系统,并且在绘制每颗卫星之后恢复坐标系统。
所以要对两个月亮的变换都增加glPushMatrix()
和 glPopMatrix()
来实现矩阵的保存和恢复。
注意的地方是改变移动的距离方向,不然就和之前绘制的月亮重合了。
代码如下:
1 | //the Moon1 |
实现轨道倾斜
实现轨道倾斜就是把行星的轴倾斜。在球体的变化操作完之后,再调用glRotatef()函数让轨道倾斜即可。
举例对月球的轨道倾斜:
1 | glPushMatrix(); |
程序完整代码
1 |
|