用上周的作业:画一个太阳、地球、月亮的运动模型来剖析OpenGL中变换乃至整个绘制的秘密

下面是上周的作业的要求:

基本功能:
	完成一个初步的太阳系程序。
	场景中有三个球体,一个表示太阳,一个表示地球,一个表示月亮;
	地球不停地绕太阳旋转,月亮绕地球旋转。 

进一步扩展功能(选作下面的1项或几项功能):
	如果地球有两个月亮呢?
	画上轨道线;
	让轨道倾斜; 
	
提示:
	画球体的函数: glutWireSphere(1.0, 20, 16);
	如何让物体不停运动呢?void glutTimerFunc( )glutPostRedisplay()glutIdleFunc()


中我总结了上周学到所有的变换的原理以及对应的函数。下面我们就要通过这个作业来剖析OpenGL中这些变换函数的具体细节。
因为是刚开始学,第三周就直接上手很难受,感觉无从下手,所以我找了度娘的一个实现程序,通过老师课上讲的与这个程序中每处用到变换的细节对比就可以理解真谛所在。
在看这段程序之前,我总结了三点理解OpenGL中具体程序运行的个人观点:
(1)OpenGL中并无绝对单位,只有相对大小!!!
(2)OpenGL中所有的变化都是局部变化,也就是说OpenGL中所有物体的变换都以当前物体的中心为原点,只不过有时候他自己的中心恰好在默认的原点。
(3)OpenGL是先对物体进行操作(先罗列一堆变换等函数),再画这个物体
以上三点对于OpenGL程序的理解至关重要,一定要带着这三点去理解下面的程序和函数,其中一些观念与我们主观会不同,要慢慢接受。

#include <windows.h>
#include <GL/freeglut.h>
static int day=200;
void display()
{
	glEnable(GL_DEPTH_TEST);//用户初始化
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除屏幕和深度缓冲	
   
    glMatrixMode(GL_PROJECTION);//设置矩阵模型(投影变换)	
    glLoadIdentity();//载入单位化矩阵
    //透视变换角度75度,长宽比1:1,最近可视距离1,最远400000两倍地球公转半径
	gluPerspective(75,1,1,400000);
	
	glMatrixMode(GL_MODELVIEW);//设置矩阵模型(模型、视点变换)
	glLoadIdentity();//载入单位化矩阵
	//整体布局,视角位(这里是45度倾角),物体位置,z轴正向
	gluLookAt(0,-200000,-200000,0,0,0,0,0,1);
	
	//太阳
	glColor3f(1.0f, 0.0f, 0.0f);
	glutWireSphere(69600,20,20);
	//地球
	glColor3f(0.0f, 0.0f, 1.0f);
	glRotatef(day,0,0,-1);
	glTranslatef(150000,0,0);
	glutWireSphere(15945,20,20);
	//月亮
	glColor3f(1.0f, 1.0f, 0.0f);
	glRotatef(day/30*360-day,0,0,-1);
	glTranslatef(38000,0,0);
	glutWireSphere(4345,20,20);
	
	//交换前后缓冲区
	glutSwapBuffers();
}
void timer(int p)
{
	day++;
	if(day>360) day=0;

	//注册按一定时间间隔触发的定时器回调函数
	glutTimerFunc(50,timer,0);

	//在图像绘制的所有操作之后,要加入 glutPostRedisplay() 来重绘图像,否则图像只有响应鼠标或键盘消息时才会更新图像。
	//有点像窗口重绘函数一样。它要求当前的回调函数返回时执行显示回调函数glutMainLoop():使程序进入事件处理循环。
	glutPostRedisplay();
}

void processNormalKeys(unsigned char key,int x,int y)
{
	if(key==27){	//"esc"
		exit(0);
	}
}
int main(int argc, char* argv[])
{
	glutInit(&argc, argv);//初始化freeglut
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);//初始化OPENGL显示方式
	//设定OPENGL窗口位置和大小
	glutInitWindowSize (400, 400); 
	glutInitWindowPosition (100, 100);		
	//打开窗口
	glutCreateWindow ("太阳地球月亮");
	//按esc退出
	glutKeyboardFunc(processNormalKeys);
	//开始OPENGL的循环
	glutDisplayFunc(display); 
	
	//glutIdleFunc(myIdle);可以用来代替下面的glutTimerFunc让物体一直动
	//设置全局的回调函数,当没有窗口事件到达时,FREEGLUT程序功能可以执行后台处理任务或连续动画。
	//如果启用,这个idle function会被不断调用,直到有窗口事件发生。回调函数没有参数。
	//当前的窗口和菜单在执行idlefunc之前不会改变。当程序依赖多窗口或菜单时最好不要依赖于当前设定。
	
	glutTimerFunc(50,timer,0);//按一定时间间隔触发的定时器回调函数
	glutMainLoop();
	return 0;
}
/*void myIdle(void)
{
    ++day;
    if(day>=360)
        day=0;    
    display();
}*/

上面这个程序的效果是月亮围着地球转,二者又同时围着太阳转,月亮、地球、太阳都没有自转。对于程序中OpenGL一定会用到的框架类型的函数,就不解释了,可以看https://blog.csdn.net/derbi123123/article/details/104619415这个文章。下面我们就来剖析这个程序中所有用到OpenGL变换相关的函数的细节,也就是display函数中的内容。

如果看不同上面一些变换函数的参数或者不知道它在干嘛,可以看OpenGL中各种变换的原理以及对应的OpenGL函数里面总结了所有变换相关的函数。

涉及到投影、模型、视点变换的函数:

	glMatrixMode(GL_PROJECTION);//设置成模型矩阵模式(投影变换)	
    glLoadIdentity();//载入单位化矩阵
    //透视变换角度75度,长宽比1:1,最近可视距离1,最远400000两倍地球公转半径
	gluPerspective(75,1,1,400000);
	
	glMatrixMode(GL_MODELVIEW);//设置成模型矩阵模式(模型、视点变换)
	glLoadIdentity();//载入单位化矩阵
	//整体布局,视角位(这里是45度倾角),物体位置,z轴正向
	gluLookAt(0,-200000,-200000,0,0,0,0,0,1);

对象的变换都是通过矩阵来实现的,在进行矩阵操作前,需用glMatrixMode()指定当前操作的矩阵对象,GL_PROJECTION告诉电脑是要进行投影变换了,GL_MODELVIEW告诉电脑是要进行模型、视点变换了。
glLoadIdentity()用来载入单位化矩阵(重载矩阵),是为了想让之后进行的变换不受之前变换的影响。
gluPerspective()就是投影变换,gluLookAt()是视点变换,这里可能有些懵逼,这些乱七八糟的参数是怎么得到的?说白了还是要清楚一点OpenGL中并无绝对单位,只有相对大小!!! 比如投影变换的第四个参数是400000,也就是远平面到视点的距离是400000个单位,而视点变换的前三个参数是视点位置,程序中是(0,-200000,-200000),就是是我们的视点,而后三个参数是头朝上的方向,程序中是(0,0,1),也就是说我们头朝z轴,我们来看看效果:
在这里插入图片描述
一定要仔细看太阳(红色的),上面的顶点在我们看不见的那侧,你会发现我们在仰视这个太阳,也就是原点在屏幕中心,我们在(0,-200000,-200000)看它,是这样的效果,为了体现出参数设置细节,我们不妨把gluLookAt(0,-200000,-200000,0,0,0,0,0,1);改成
gluLookAt(0,200000,200000,0,0,0,0,0,1);,我们再来看效果:
在这里插入图片描述
我们可以看到上面的顶点在我们视野的下方了,下面的顶点反而看不见了,我们是在俯视这个太阳,这样我们就可以体会到视点变换函数前三个参数它具体的作用。

涉及到几何变换的函数:

一定要带着下面的思想取体会这段代码。
OpenGL中并无绝对单位,只有相对大小!!!
OpenGL中所有的变化都是局部变化,也就是说OpenGL中所有物体的变换都以当前物体的中心为原点,只不过有时候他自己的中心恰好在默认的原点。
OpenGL是先对物体进行操作(先罗列一堆变换等函数),再画这个物体

	//太阳
	glColor3f(1.0f, 0.0f, 0.0f);
	glutWireSphere(69600,20,20);
	//地球
	glColor3f(0.0f, 0.0f, 1.0f);
	glRotatef(day,0,0,-1);
	glTranslatef(150000,0,0);
	glutWireSphere(15945,20,20);
	//月亮
	glColor3f(1.0f, 1.0f, 0.0f);
	glRotatef(day/30*360-day,0,0,-1);
	glTranslatef(38000,0,0);
	glutWireSphere(4345,20,20);

就是这段代码产生了地球围着太阳转,月亮既围着地球转又围着太阳转的静态效果,让物体不停运动的函数glutTimerFunc()不是这里的重点,可以百度了解。注意上述代码得到的就是一个静态图(也许和下面不一样,反正是静态的):
在这里插入图片描述
关键在于这段代码这样写的缘由:
(1)首先是理解OpenGL中所有的变化都是局部变化,也就是说OpenGL中所有物体的变换都以当前物体的中心为原点,我们可以看到画了太阳之后,对地球的变换都是相对于太阳做的,画了地球之后对月亮的变换都是相对于地球做的。由此我们要体会画图的顺序是怎样的。
(2)其次要理解OpenGL是先对物体进行操作(先罗列一堆变换等函数),再画这个物体,这个很容易,我们看程序也能看出来。
(3)最后理解OpenGL中并无绝对单位,只有相对大小!!!
先看一下这个画球体的函数:

glutWireSphere(半径,球体纬线的条数,球体经线的条数)

其作用是画一个可透视的球体。
我们可以看到太阳的半径是69600,地球是15945,月亮是4345,他就是一个相对大小,只要你愿意把它设成1,2,3都行(但要确保前后其他函数的比例也与1,2,3一致),这就是相对的含义,还有地球glTranslatef(150000,0,0);是相对于太阳沿x轴z方向平移150000个单位,前面也看到了,投影函数设置的远平面是400000个单位,所以说这都是相对的,一定要理解这个。
至于旋转变换函数的参数取值我只能说地球绕太阳一圈要一年我们为了方便计算用的360天,月亮绕地球一圈要27天我们为了方便计算用的30天。

最后,再加上可以让物体一直运动的函数后实现的效果是月亮围着地球转,二者又同时围着太阳转,但月亮、地球、太阳都没有自转,所以大家去思考尝试实现月亮、地球、太阳的自转可以发现更多的奥秘!!!

OpenGL绘制太阳地球月亮运动模型,可以按照以下步骤进行实现: 首先,我们需要设置视图、投影和模型矩阵,以便将三个天体的坐标和运动应用于OpenGL场景。 然后,我们可以通过创建三个球体模型来表示太阳地球月亮。可以使用OpenGL绘制函数(如glBegin(GL_TRIANGLE_STRIP)和glVertex3f)来绘制这些球体,可以根据需要调整球体的半径和细节级别以获得逼真的效果。 接下来,我们需要定义三个球体的初始位置和大小。可以使用OpenGL变换函数(如glTranslatef和glScalef)来实现这一点。太阳位于世界坐标系的心,地球位于太阳左边一定的距离,并绕着太阳旋转,月亮位于地球的一侧,并绕着地球旋转。 然后,我们需要为每个天体定义其自身的旋转和公转速度。可以使用OpenGL变换函数和时间函数来实现这一点。以地球为例,可以通过旋转和平移变换来实现自转和公转。月亮则继承了地球的旋转和平移变换,并有自己的公转速度。 最后,我们需要在OpenGL的主循环更新每个天体的位置和旋转角度,然后再次渲染场景。可以使用OpenGL变换函数和时间函数来实现这一点。这样,太阳地球月亮就会按照指定的运动模型OpenGL场景展示出来。 需要注意的是,上述步骤仅仅是一个简单的示例,实际使用时还需要考虑更多的细节和优化。另外,为了更好地展示运动效果,通常会在渲染过程增加光照、纹理等效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值