转自: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….):

12

34

以下是完整的代码:

View Code
  1 /***************************** BallDefinition.h ******************************/
  2 #include <gl/glut.h>
  3 
  4 #ifndef __BALLDEFINITION
  5 #define __BALLDEFINITION
  6 
  7 // 数组type
  8 typedef GLfloat (Float2)[2];
  9 typedef GLfloat (Float3)[3];
 10 typedef GLfloat Float;
 11 typedef GLfloat (Float4)[4];
 12 
 13 // 对数组进行操作的宏
 14 //#define Float(name, value) (name)=(value)
 15 #define Float2(name, value0, value1) ((name)[0])=(value0), ((name)[1])=(value1)
 16 #define Float3(name, value0, value1, value2) ((name)[0])=(value0), \
 17     ((name)[1])=(value1), ((name)[2])=(value2)
 18 #define Float4(name, value0, value1, value2, value3) ((name)[0])=(value0), \
 19     ((name)[1])=(value1), ((name)[2])=(value2), ((name)[3])=(value3)
 20 
 21 // 对数组进行操作的宏
 22 //#define Float(name) (name)
 23 #define RFloat2(name) ((name)[0]), ((name)[1])
 24 #define RFloat3(name) ((name)[0]), ((name)[1]), ((name)[2])
 25 #define RFloat4(name) ((name)[0]), ((name)[1]), ((name)[2]), ((name)[3])
 26 
 27 class Ball {
 28 public:
 29     Float4 Color;
 30     Float Radius;
 31     Float SelfSpeed;
 32     Float Speed;
 33 
 34     // ParentBall是本球绕行的球
 35     // Center是本球的中心点,当有ParentBall和Distance的时候可以不使用
 36     // Distance是本球中心与ParentBall中心的距离
 37     // Center暂时没有使用
 38     //Float2 Center;        
 39     Float Distance;        
 40     Ball * ParentBall;
 41 
 42     virtual void Draw() { DrawBall(); }
 43     virtual void Update(long TimeSpan);
 44 
 45     Ball(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent);
 46 
 47     // 对普通的球体进行移动和旋转
 48     void DrawBall();
 49 
 50 protected:
 51     Float AlphaSelf, Alpha;
 52 };
 53 
 54 class MatBall : public Ball {
 55 public:
 56     virtual void Draw() { DrawMat(); DrawBall(); }
 57 
 58     MatBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, 
 59         Ball * Parent, Float3 color);
 60 
 61     // 对材质进行设置
 62     void DrawMat();
 63 };
 64 
 65 class LightBall : public MatBall {
 66 public:
 67     virtual void Draw() { DrawLight(); DrawMat(); DrawBall(); }
 68 
 69     LightBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, 
 70         Ball * Parent, Float3 color);
 71 
 72     // 对光源进行设置
 73     void DrawLight();
 74 };
 75 
 76 #endif
 77 
 78 
 79 /**************************** BallDefinition.cpp *****************************/
 80 #include "BallDefinition.h"
 81 
 82 Ball::Ball(Float Radius, Float Distance, Float Speed, Float SelfSpeed, Ball * Parent) {
 83     Float4(Color, 0.8f, 0.8f, 0.8f, 1.0f);
 84     this->Radius = Radius;
 85     this->SelfSpeed = SelfSpeed;
 86     if (Speed > 0)
 87         this->Speed = 360.0f / Speed;
 88     AlphaSelf = Alpha= 0;
 89     this->Distance = Distance;
 90     ParentBall = Parent;
 91 }
 92 
 93 #include <stdio.h>
 94 #include <math.h>
 95 #define PI 3.1415926535
 96 
 97 // 对普通的球体进行移动和旋转
 98 void Ball::DrawBall() {
 99 
100     glEnable(GL_LINE_SMOOTH);
101     glEnable(GL_BLEND);
102 
103     int n = 1440;
104 
105     glPushMatrix();
106     {
107         // 公转
108         if (ParentBall != 0 && ParentBall->Distance > 0) {
109             glRotatef(ParentBall->Alpha, 0, 0, 1); 
110             glTranslatef(ParentBall->Distance, 0.0, 0.0);
111 
112             glBegin(GL_LINES);
113             for(int i=0; i<n; ++i)
114                 glVertex2f(Distance * cos(2 * PI * i / n), 
115                     Distance * sin(2 * PI * i / n));
116             glEnd();
117 
118         } else {
119             glBegin(GL_LINES);
120             for(int i=0; i<n; ++i)
121                 glVertex2f(Distance * cos(2 * PI * i / n), 
122                     Distance * sin(2 * PI * i / n));
123             glEnd();
124         }
125         glRotatef(Alpha, 0, 0, 1);
126         glTranslatef(Distance, 0.0, 0.0);
127 
128         // 自转
129         glRotatef(AlphaSelf, 0, 0, 1);
130 
131         // 绘图
132         glColor3f(RFloat3(Color));
133         glutSolidSphere(Radius, 40, 32);
134     }
135     glPopMatrix();
136 }
137 
138 void Ball::Update(long TimeSpan) {
139     // TimeSpan 是天
140     Alpha += TimeSpan * Speed;
141     AlphaSelf += SelfSpeed;
142 }
143 
144 MatBall::MatBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, 
145     Ball * Parent, Float3 color) : Ball(Radius, Distance, Speed, SelfSpeed, Parent) {
146         Float4(Color, color[0], color[1], color[2], 1.0f);
147 }
148 
149 // 对材质进行设置
150 void MatBall::DrawMat() {
151     GLfloat mat_ambient[]  = {0.0f, 0.0f, 0.5f, 1.0f};
152     GLfloat mat_diffuse[]  = {0.0f, 0.0f, 0.5f, 1.0f};
153     GLfloat mat_specular[] = {0.0f, 0.0f, 1.0f, 1.0f};
154     //下面两句替换可以出现彩色或者蓝色的太阳系模型
155     //GLfloat mat_emission[] = {RFloat4(Color)};
156     GLfloat mat_emission[] = {.0f, .0f, .1f, 1.0f};
157     GLfloat mat_shininess  = 90.0f;
158 
159     glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
160     glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
161     glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
162     glMaterialfv(GL_FRONT, GL_EMISSION,  mat_emission);
163     glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);
164 }
165 
166 LightBall::LightBall(Float Radius, Float Distance, Float Speed, Float SelfSpeed, 
167     Ball * Parent, Float3 color)
168     : MatBall(Radius, Distance, Speed, SelfSpeed, Parent, color) {}
169 
170 // 对光源进行设置
171 void LightBall::DrawLight() {
172     GLfloat light_position[] = {0.0f, 0.0f, 0.0f, 1.0f};
173     GLfloat light_ambient[]  = {0.0f, 0.0f, 0.0f, 1.0f};
174     GLfloat light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};
175     GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
176     glLightfv(GL_LIGHT0, GL_POSITION, light_position);
177     glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
178     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
179     glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
180 }
181 
182 
183 /**************************** Main.cpp *****************************/
184 #include <stdlib.h>
185 #include "BallDefinition.h"
186 
187 #define WIDTH 700
188 #define HEIGHT 700
189 
190 // 每次更新 看做过去了 1 天
191 #define TimePast 1
192 
193 #include <math.h>
194 
195 // 对太阳系星球的参数进行调整用的宏
196 #define KK .000001
197 #define sk (.07 * KK)
198 #define k (.5 * KK)
199 #define vk (1.5 * KK)
200 #define fk (.5 * KK)
201 #define hfk (.4 * KK)
202 #define ffk (.3 * KK)
203 #define dk (1.07 * KK)
204 #define edk (1.12 * KK)
205 #define lsk (.3 * KK)
206 #define mk (15000 * KK)
207 #define mrk (1.6 * KK)
208 #define tk .3
209 #define ttk .2
210 #define tttk .1
211 
212 // 自转速度(都定义为定值)
213 #define SelfRotate 3
214 
215 #define ARRAY_SIZE 10
216 enum STARS {Sun, Mercury, Venus, Earth, Moon, Mars, Jupiter, Saturn, Uranus, Neptune};
217 Ball * Balls[ARRAY_SIZE];
218 
219 void init() {
220     Float3 Color;
221     // 定义星球,这些星球的数据是经过不同比例变化过的
222     // 太阳
223     Float3(Color, 1, 0, 0);
224     Balls[Sun] = new LightBall(sk * 696300000, 0, 0, SelfRotate, 0, Color);
225     // 水星
226     Float3(Color, .2, .2, .5);
227     Balls[Mercury] = new MatBall(
228         vk * 4880000, dk * 58000000, 87, SelfRotate, Balls[Sun], Color);
229     // 金星
230     Float3(Color, 1, .7, 0);
231     Balls[Venus] = new MatBall(
232         vk * 12103600, dk * 108000000, 225, SelfRotate, Balls[Sun], Color);
233     // 地球
234     Float3(Color, 0, 1, 0);
235     Balls[Earth] = new MatBall(
236         vk * 12756300, edk * 150000000, 365, SelfRotate, Balls[Sun], Color);
237     // 月亮
238     Float3(Color, 1, 1, 0);
239     Balls[Moon] = new MatBall(
240         mrk * 3844010.0f , mk * 1734.0f, 30, SelfRotate, Balls[Earth], Color);
241     // 火星
242     Float3(Color, 1, .5, .5);
243     Balls[Mars] = new MatBall(
244         vk * 6794000, KK * 228000000, 687, SelfRotate, Balls[Sun], Color);
245     // 木星
246     Float3(Color, 1, 1, .5);
247     Balls[Jupiter] = new MatBall(
248         lsk * 142984000,  fk * 778000000, tk * 4328, SelfRotate, Balls[Sun], Color);
249     // 土星
250     Float3(Color, .5, 1, .5);
251     Balls[Saturn] = new MatBall(
252         lsk * 120536000, fk * 1427000000, ttk * 10752, SelfRotate, Balls[Sun], Color);
253     // 天王星
254     Float3(Color, .4, .4, .4);
255     Balls[Uranus] = new MatBall(k * 51118000,  
256         hfk * 2870000000, tttk * 30664, SelfRotate, Balls[Sun], Color);
257     // 海王星
258     Float3(Color, .5, .5, 1);
259     Balls[Neptune] = new MatBall(k * 49532000,  
260         ffk * 4497000000, tttk * 60148, SelfRotate, Balls[Sun], Color);
261 }
262 
263 // 初始视角( 视点在(+z, -y)处 )
264 #define REST (700000000 * KK)
265 #define REST_Z (REST)
266 #define REST_Y (-REST)
267 
268 // lookAt参数
269 GLdouble eyeX = 0, eyeY = REST_Y, eyeZ= REST_Z; 
270 GLdouble centerX= 0, centerY= 0, centerZ= 0; 
271 GLdouble upX= 0, upY= 0, upZ= 1;
272 
273 void OnDraw(void) {
274     glClear(GL_COLOR_BUFFER_BIT  |  GL_DEPTH_BUFFER_BIT);
275     glClearColor(.7, .7, .7, .1);
276     glMatrixMode(GL_PROJECTION);
277     glLoadIdentity();
278     gluPerspective(75.0f, 1.0f, 1.0f, 40000000);
279     glMatrixMode(GL_MODELVIEW);
280     glLoadIdentity();
281     gluLookAt(eyeX, eyeY,eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
282 
283     glEnable(GL_LIGHT0);
284     glEnable(GL_LIGHTING);
285     glEnable(GL_DEPTH_TEST);
286 
287     // 实际绘制
288     for (int i=0; i<ARRAY_SIZE; i++)
289         Balls[i]->Draw();
290 
291     glutSwapBuffers();
292 }
293 
294 void OnUpdate(void) {
295     // 实际更新
296     for (int i=0; i<ARRAY_SIZE; i++)
297         Balls[i]->Update(TimePast);
298     OnDraw();
299 }
300 
301 // 每次按键移动的距离
302 #define OFFSET (20000000 * KK)
303 
304 // 按键操作变化视角
305 // w(+y方向)   a(-x方向)   d(+x方向)   x(-y方向)   s(+z 方向)   S(-z 方向)   r(reset)
306 void keyboard (unsigned char key, int x, int y) {
307     switch (key)     {
308     case 'w': eyeY += OFFSET; break;
309     case 's': eyeZ += OFFSET; break;
310     case 'S': eyeZ -= OFFSET; break;
311     case 'a': eyeX -= OFFSET; break;
312     case 'd': eyeX += OFFSET; break;
313     case 'x': eyeY -= OFFSET; break;
314     case 'r':
315         eyeX = 0; eyeY = REST_Y; eyeZ= REST_Z; 
316         centerX= 0; centerY= 0; centerZ= 0; 
317         upX= 0; upY= 0; upZ= 1;
318         break;
319     case 27: exit(0); break;
320     default: break;
321     }
322 }
323 
324 int main(int argc, char*  argv[]) {
325     init();
326 
327     glutInit(&argc, argv);
328     glutInitDisplayMode(GLUT_RGBA |  GLUT_DOUBLE);
329     glutInitWindowPosition(150, 50);
330     glutInitWindowSize(WIDTH, HEIGHT);
331     glutCreateWindow("SolarSystem   by Juwend");
332     glutDisplayFunc(&OnDraw);
333     glutIdleFunc(&OnUpdate);
334     glutKeyboardFunc(keyboard);
335     glutMainLoop();
336 
337     return 0;
338 }

这个模型还有很多需要增加的地方,比如He老师(任课老师)提出的,轨道可以使用椭圆,包括星球也可以更现实一些,另外就是球面纹理了,需要把星球的皮给披上去,这样就更容易看出自转了,还有就是因为最长的公转链就是太阳、地球、月亮,所以在实现公转和自转的时候,还有点问题的,假如最长公转链里有更多,比如10个球,则以上代码就会出问题了,但是修改代码解决这个问题并不是很困难的问题。关于这些问题,如果有时间再改吧。

关于OpenGL环境配置的问题,我想我应该会再写一篇短文来介绍的,只是不知是何时了……………………………

在此我也要感谢JiangTao同学在这方面提供了大量的无私的帮助! 啊,太谢谢你了~~~~~~~~~~~ 另外,这是我在计算机三维动画这门课交的最后的大作业,希望CV代码的朋友一定要注意这个问题,并且能够理解我的补充这么一句话的意思。

OpenGL实现太阳系模型 —— Juwend
Juwend’s – http://www.juwends.com
笔者水平有限,若有错漏,欢迎指正,欢迎转载以及CV操作,但希注明出处,谢谢!

posted on 2013-02-22 11:42  Juwendcnblgs  阅读(3626)  评论(0编辑  收藏  举报