转自: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….):
以下是完整的代码:
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操作,但希注明出处,谢谢!