【转】OpenGL实现太阳系模型
2013-02-22 11:42
239 查看
转自:http://www.juwends.com/tech/opengl/opengl-solar-system.html
OpenGL是一个非常强大的图形引擎。传说当下最流行的图形引擎有两套,其中之一就是Windows平台上最常用的DirectX(而且只能在Microsoft的平台上使用,可以看下百度百科关于DirectX的介绍),而另外一套则是OpenGL了,可以用于非常多的平台(可以参看百度百科关于OpenGL的介绍),至少我是这么被告知的。说到OpenGL,就不得不提到NeHe(读音有点像“妮褐”,不过我平时都叫它“呵呵”),据我的浅薄认知来看,NeHe提供了大概48个使用OpenGL的例子,这些例子涉及了OpenGL编程非常多的方面,传说是掌握这些例子就可以无敌了,详细可以去NeHe官网看看,最右边有个“Legacy Tutorials”(嗯,对了,我确定是有48个例子了)就是所有的例子,所有的例子可以下载不同IDE(集成开发环境,比如像vs,vc,devc等)的源码。
关于OpenGL实现太阳系模型是因为选了三维动画的课,最后交的结课作业,为了不太浪费资源,所以写一篇文章来保留这些劳动成果,也为后来的人做个小小的参考,因为初涉OpenGL,模型设计实现不妥之处还望高手指教。以下是简要的设计描述:
为简便起见,简化模型: 太阳为光源星球,并且为太阳系行星的中心; 所有星球(除太阳以外)以圆形轨道绕行; 所有星球均为正球体。 对具体星球而言,具有以下属性: 颜色(Color); 半径(Radius); 自转速度(SelfSpeed); 公转速度(Speed); 距离太阳中心距离(Distance); 绕行星球(ParentBall); 当前自转角度(AlphaSelf); 当前公转角度(Alpha)。 设计描述星球的类及关键实现: 描述普通的能够自转并且绕某个点公转的球(class Ball); 描述具有材质属性的球(class MatBall); 描述具有发光属性的球(class LightBall); 每个星球类独立的处理自己的运动; 类中实现绘图方法(Draw)和更新方法(Update)用于绘制、更新星球; Draw()方法中需要处理自己绕行点(ParentBall)的关系; 对于星球的属性数据需要案一定比例进行调整以符合观看需要。 程序流程如下: 使用Console模式开启程序; 初始化星球对象; 初始化OpenGL引擎,实现绘制函数(OnDraw)和更新函数(OnUpdate); 在绘制函数中调用每个星球对象的Draw()方法; Draw()方法根据星球的属性进行变换并绘制; 在更新函数中调用每个星球对象的Update()方法; Update()方法处理自转数据和公转数据; 实现按键监控,可以通过调整视角对太阳系模型进行观察。
下面是运行程序的截图(本来是彩色的,不过呢,因为word打印需要变成灰度图来看效果,又不想去再截图了,So….):
View Code
这个模型还有很多需要增加的地方,比如He老师(任课老师)提出的,轨道可以使用椭圆,包括星球也可以更现实一些,另外就是球面纹理了,需要把星球的皮给披上去,这样就更容易看出自转了,还有就是因为最长的公转链就是太阳、地球、月亮,所以在实现公转和自转的时候,还有点问题的,假如最长公转链里有更多,比如10个球,则以上代码就会出问题了,但是修改代码解决这个问题并不是很困难的问题。关于这些问题,如果有时间再改吧。
关于OpenGL环境配置的问题,我想我应该会再写一篇短文来介绍的,只是不知是何时了……………………………
在此我也要感谢JiangTao同学在这方面提供了大量的无私的帮助! 啊,太谢谢你了~~~~~~~~~~~ 另外,这是我在计算机三维动画这门课交的最后的大作业,希望CV代码的朋友一定要注意这个问题,并且能够理解我的补充这么一句话的意思。
OpenGL实现太阳系模型 —— Juwend
Juwend’s – http://www.juwends.com 笔者水平有限,若有错漏,欢迎指正,欢迎转载以及CV操作,但希注明出处,谢谢!
OpenGL是一个非常强大的图形引擎。传说当下最流行的图形引擎有两套,其中之一就是Windows平台上最常用的DirectX(而且只能在Microsoft的平台上使用,可以看下百度百科关于DirectX的介绍),而另外一套则是OpenGL了,可以用于非常多的平台(可以参看百度百科关于OpenGL的介绍),至少我是这么被告知的。说到OpenGL,就不得不提到NeHe(读音有点像“妮褐”,不过我平时都叫它“呵呵”),据我的浅薄认知来看,NeHe提供了大概48个使用OpenGL的例子,这些例子涉及了OpenGL编程非常多的方面,传说是掌握这些例子就可以无敌了,详细可以去NeHe官网看看,最右边有个“Legacy Tutorials”(嗯,对了,我确定是有48个例子了)就是所有的例子,所有的例子可以下载不同IDE(集成开发环境,比如像vs,vc,devc等)的源码。
关于OpenGL实现太阳系模型是因为选了三维动画的课,最后交的结课作业,为了不太浪费资源,所以写一篇文章来保留这些劳动成果,也为后来的人做个小小的参考,因为初涉OpenGL,模型设计实现不妥之处还望高手指教。以下是简要的设计描述:
为简便起见,简化模型: 太阳为光源星球,并且为太阳系行星的中心; 所有星球(除太阳以外)以圆形轨道绕行; 所有星球均为正球体。 对具体星球而言,具有以下属性: 颜色(Color); 半径(Radius); 自转速度(SelfSpeed); 公转速度(Speed); 距离太阳中心距离(Distance); 绕行星球(ParentBall); 当前自转角度(AlphaSelf); 当前公转角度(Alpha)。 设计描述星球的类及关键实现: 描述普通的能够自转并且绕某个点公转的球(class Ball); 描述具有材质属性的球(class MatBall); 描述具有发光属性的球(class LightBall); 每个星球类独立的处理自己的运动; 类中实现绘图方法(Draw)和更新方法(Update)用于绘制、更新星球; Draw()方法中需要处理自己绕行点(ParentBall)的关系; 对于星球的属性数据需要案一定比例进行调整以符合观看需要。 程序流程如下: 使用Console模式开启程序; 初始化星球对象; 初始化OpenGL引擎,实现绘制函数(OnDraw)和更新函数(OnUpdate); 在绘制函数中调用每个星球对象的Draw()方法; Draw()方法根据星球的属性进行变换并绘制; 在更新函数中调用每个星球对象的Update()方法; Update()方法处理自转数据和公转数据; 实现按键监控,可以通过调整视角对太阳系模型进行观察。
下面是运行程序的截图(本来是彩色的,不过呢,因为word打印需要变成灰度图来看效果,又不想去再截图了,So….):
View Code
/***************************** BallDefinition.h ******************************/ #include <gl/glut.h> #ifndef __BALLDEFINITION #define __BALLDEFINITION // 数组type typedef GLfloat (Float2)[2]; typedef GLfloat (Float3)[3]; typedef GLfloat Float; typedef GLfloat (Float4)[4]; // 对数组进行操作的宏 //#define Float(name, value) (name)=(value) #define Float2(name, value0, value1) ((name)[0])=(value0), ((name)[1])=(value1) #define Float3(name, value0, value1, value2) ((name)[0])=(value0), \ ((name)[1])=(value1), ((name)[2])=(value2) #define Float4(name, value0, value1, value2, value3) ((name)[0])=(value0), \ ((name)[1])=(value1), ((name)[2])=(value2), ((name)[3])=(value3) // 对数组进行操作的宏 //#define Float(name) (name) #define RFloat2(name) ((name)[0]), ((name)[1]) #define RFloat3(name) ((name)[0]), ((name)[1]), ((name)[2]) #define RFloat4(name) ((name)[0]), ((name)[1]), ((name)[2]), ((name)[3]) class Ball { public: Float4 Color; Float Radius; Float SelfSpeed; Float Speed; // ParentBall是本球绕行的球 // Center是本球的中心点,当有ParentBall和Distance的时候可以不使用 // Distance是本球中心与ParentBall中心的距离 // Center暂时没有使用 //Float2 Center; Float Distance; Ball * ParentBall; virtual void Draw() { DrawBall(); } virtual void Update(long TimeSpan); Ball(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent); // 对普通的球体进行移动和旋转 void DrawBall(); protected: Float AlphaSelf, Alpha; }; class MatBall : public Ball { public: virtual void Draw() { DrawMat(); DrawBall(); } MatBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent, Float3 color); // 对材质进行设置 void DrawMat(); }; class LightBall : public MatBall { public: virtual void Draw() { DrawLight(); DrawMat(); DrawBall(); } LightBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent, Float3 color); // 对光源进行设置 void DrawLight(); }; #endif /**************************** BallDefinition.cpp *****************************/ #include "BallDefinition.h" Ball::Ball(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent) { Float4(Color, 0.8f, 0.8f, 0.8f, 1.0f); this->Radius = Radius; this->SelfSpeed = SelfSpeed; if (Speed > 0) this->Speed = 360.0f / Speed; AlphaSelf = Alpha= 0; this->Distance = Distance; ParentBall = Parent; } #include <stdio.h> #include <math.h> #define PI 3.1415926535 // 对普通的球体进行移动和旋转 void Ball::DrawBall() { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); int n = 1440; glPushMatrix(); { // 公转 if (ParentBall != 0 && ParentBall->Distance > 0) { glRotatef(ParentBall->Alpha, 0, 0, 1); glTranslatef(ParentBall->Distance, 0.0, 0.0); glBegin(GL_LINES); for(int i=0; i<n; ++i) glVertex2f(Distance * cos(2 * PI * i / n), Distance * sin(2 * PI * i / n)); glEnd(); } else { glBegin(GL_LINES); for(int i=0; i<n; ++i) glVertex2f(Distance * cos(2 * PI * i / n), Distance * sin(2 * PI * i / n)); glEnd(); } glRotatef(Alpha, 0, 0, 1); glTranslatef(Distance, 0.0, 0.0); // 自转 glRotatef(AlphaSelf, 0, 0, 1); // 绘图 glColor3f(RFloat3(Color)); glutSolidSphere(Radius, 40, 32); } glPopMatrix(); } void Ball::Update(long TimeSpan) { // TimeSpan 是天 Alpha += TimeSpan * Speed; AlphaSelf += SelfSpeed; } MatBall::MatBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent, Float3 color) : Ball(Radius, Distance, Speed, SelfSpeed, Parent) { Float4(Color, color[0], color[1], color[2], 1.0f); } // 对材质进行设置 void MatBall::DrawMat() { GLfloat mat_ambient[] = {0.0f, 0.0f, 0.5f, 1.0f}; GLfloat mat_diffuse[] = {0.0f, 0.0f, 0.5f, 1.0f}; GLfloat mat_specular[] = {0.0f, 0.0f, 1.0f, 1.0f}; //下面两句替换可以出现彩色或者蓝色的太阳系模型 //GLfloat mat_emission[] = {RFloat4(Color)}; GLfloat mat_emission[] = {.0f, .0f, .1f, 1.0f}; GLfloat mat_shininess = 90.0f; glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess); } LightBall::LightBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent, Float3 color) : MatBall(Radius, Distance, Speed, SelfSpeed, Parent, color) {} // 对光源进行设置 void LightBall::DrawLight() { GLfloat light_position[] = {0.0f, 0.0f, 0.0f, 1.0f}; GLfloat light_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f}; GLfloat light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); } /**************************** Main.cpp *****************************/ #include <stdlib.h> #include "BallDefinition.h" #define WIDTH 700 #define HEIGHT 700 // 每次更新 看做过去了 1 天 #define TimePast 1 #include <math.h> // 对太阳系星球的参数进行调整用的宏 #define KK .000001 #define sk (.07 * KK) #define k (.5 * KK) #define vk (1.5 * KK) #define fk (.5 * KK) #define hfk (.4 * KK) #define ffk (.3 * KK) #define dk (1.07 * KK) #define edk (1.12 * KK) #define lsk (.3 * KK) #define mk (15000 * KK) #define mrk (1.6 * KK) #define tk .3 #define ttk .2 #define tttk .1 // 自转速度(都定义为定值) #define SelfRotate 3 #define ARRAY_SIZE 10 enum STARS {Sun, Mercury, Venus, Earth, Moon, Mars, Jupiter, Saturn, Uranus, Neptune}; Ball * Balls[ARRAY_SIZE]; void init() { Float3 Color; // 定义星球,这些星球的数据是经过不同比例变化过的 // 太阳 Float3(Color, 1, 0, 0); Balls[Sun] = new LightBall(sk * 696300000, 0, 0, SelfRotate, 0, Color); // 水星 Float3(Color, .2, .2, .5); Balls[Mercury] = new MatBall( vk * 4880000, dk * 58000000, 87, SelfRotate, Balls[Sun], Color); // 金星 Float3(Color, 1, .7, 0); Balls[Venus] = new MatBall( vk * 12103600, dk * 108000000, 225, SelfRotate, Balls[Sun], Color); // 地球 Float3(Color, 0, 1, 0); Balls[Earth] = new MatBall( vk * 12756300, edk * 150000000, 365, SelfRotate, Balls[Sun], Color); // 月亮 Float3(Color, 1, 1, 0); Balls[Moon] = new MatBall( mrk * 3844010.0f , mk * 1734.0f, 30, SelfRotate, Balls[Earth], Color); // 火星 Float3(Color, 1, .5, .5); Balls[Mars] = new MatBall( vk * 6794000, KK * 228000000, 687, SelfRotate, Balls[Sun], Color); // 木星 Float3(Color, 1, 1, .5); Balls[Jupiter] = new MatBall( lsk * 142984000, fk * 778000000, tk * 4328, SelfRotate, Balls[Sun], Color); // 土星 Float3(Color, .5, 1, .5); Balls[Saturn] = new MatBall( lsk * 120536000, fk * 1427000000, ttk * 10752, SelfRotate, Balls[Sun], Color); // 天王星 Float3(Color, .4, .4, .4); Balls[Uranus] = new MatBall(k * 51118000, hfk * 2870000000, tttk * 30664, SelfRotate, Balls[Sun], Color); // 海王星 Float3(Color, .5, .5, 1); Balls[Neptune] = new MatBall(k * 49532000, ffk * 4497000000, tttk * 60148, SelfRotate, Balls[Sun], Color); } // 初始视角( 视点在(+z, -y)处 ) #define REST (700000000 * KK) #define REST_Z (REST) #define REST_Y (-REST) // lookAt参数 GLdouble eyeX = 0, eyeY = REST_Y, eyeZ= REST_Z; GLdouble centerX= 0, centerY= 0, centerZ= 0; GLdouble upX= 0, upY= 0, upZ= 1; void OnDraw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(.7, .7, .7, .1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(75.0f, 1.0f, 1.0f, 40000000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(eyeX, eyeY,eyeZ, centerX, centerY, centerZ, upX, upY, upZ); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); // 实际绘制 for (int i=0; i<ARRAY_SIZE; i++) Balls[i]->Draw(); glutSwapBuffers(); } void OnUpdate(void) { // 实际更新 for (int i=0; i<ARRAY_SIZE; i++) Balls[i]->Update(TimePast); OnDraw(); } // 每次按键移动的距离 #define OFFSET (20000000 * KK) // 按键操作变化视角 // w(+y方向) a(-x方向) d(+x方向) x(-y方向) s(+z 方向) S(-z 方向) r(reset) void keyboard (unsigned char key, int x, int y) { switch (key) { case 'w': eyeY += OFFSET; break; case 's': eyeZ += OFFSET; break; case 'S': eyeZ -= OFFSET; break; case 'a': eyeX -= OFFSET; break; case 'd': eyeX += OFFSET; break; case 'x': eyeY -= OFFSET; break; case 'r': eyeX = 0; eyeY = REST_Y; eyeZ= REST_Z; centerX= 0; centerY= 0; centerZ= 0; upX= 0; upY= 0; upZ= 1; break; case 27: exit(0); break; default: break; } } int main(int argc, char* argv[]) { init(); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutInitWindowPosition(150, 50); glutInitWindowSize(WIDTH, HEIGHT); glutCreateWindow("SolarSystem by Juwend"); glutDisplayFunc(&OnDraw); glutIdleFunc(&OnUpdate); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }
这个模型还有很多需要增加的地方,比如He老师(任课老师)提出的,轨道可以使用椭圆,包括星球也可以更现实一些,另外就是球面纹理了,需要把星球的皮给披上去,这样就更容易看出自转了,还有就是因为最长的公转链就是太阳、地球、月亮,所以在实现公转和自转的时候,还有点问题的,假如最长公转链里有更多,比如10个球,则以上代码就会出问题了,但是修改代码解决这个问题并不是很困难的问题。关于这些问题,如果有时间再改吧。
关于OpenGL环境配置的问题,我想我应该会再写一篇短文来介绍的,只是不知是何时了……………………………
在此我也要感谢JiangTao同学在这方面提供了大量的无私的帮助! 啊,太谢谢你了~~~~~~~~~~~ 另外,这是我在计算机三维动画这门课交的最后的大作业,希望CV代码的朋友一定要注意这个问题,并且能够理解我的补充这么一句话的意思。
OpenGL实现太阳系模型 —— Juwend
Juwend’s – http://www.juwends.com 笔者水平有限,若有错漏,欢迎指正,欢迎转载以及CV操作,但希注明出处,谢谢!
相关文章推荐
- OpenGL实现太阳系模型 —— Juwend
- 太阳系模型——OpenGL
- 【循序渐进学图形学之】OpenGL绘制太阳系模型
- OpenGL实现3D模型自由旋转——之代码解析
- OpenGL高级特性之利用Image内存模型&计算着色器&原子操作实现(直方图模型)通用计算
- OpenGL实现3D模型自由旋转——之代码解析
- opengl光照 Blinn-Phone模型与实现
- OpenGL实现3DS文件中的模型自由旋转-备用
- OpenGL 实现模拟太阳系运动
- OpenGL(5)--太阳系和的日月地实现
- OpenGL实现3DS文件中的模型自由旋转
- OpenGL实现3DS文件中的模型自由旋转
- 项目实践与用OpenGL实现OBJ模型文件的读入
- OpenGL实现3DS文件中的模型自由旋转
- 探究OpenGL光照模型的着色器实现
- 基于Opengl的太阳系动画实现
- OpenGL简单实现太阳系模拟
- Java实现的简易太阳系模型
- 计算机图形学——MFC和OpenGL两种方法实现光照模型
- OpenGL编程指南10:组合运动示例1—创建太阳系模型