一、提要
    今天的内容是OpenGL的编程实践—太阳系的模拟!
     红宝书上有相应的教程,但这里我们要实现得更全面一些。iPad上有一个很棒的应用,名字叫Solar System,我们尽量去达到它的效果。
    先来看一下最终效果:
    Qt下的OpenGL 编程(10)Solar System_编程
Qt下的OpenGL 编程(10)Solar System_编程_02  
 
 
  
 
 
  
思路:
     建立9个球体,分别赋予不同的材质,再通过动画不断变换它们的位置,就可以实现模拟了。
 
  
二、有关太阳系的知识
     太阳系有一颗恒星:太阳,8颗行星:水,金,地,火,木,土,天王,海王。9颗星球的位置如下:
Qt下的OpenGL 编程(10)Solar System_qt_03 
 
 
  
  
    然后去搜集了解一下各个星球的自转和公转周期,脑袋里面有个概念。
    接着可以去找一些星球贴图的素材了,我在网上找到了一些资源,太阳的贴图是自己用ps绘制的,然后把它们通通添加到资源文件中,就像这样:
    Qt下的OpenGL 编程(10)Solar System_filter_04
 
  
三、程序结构
    相比与之前的框架,这里添加了一个星球类,文件结构如下:
     程序还是比较清晰,直接贴代码了:
     
/* ----------------------------------------------------------------------------- Filename:   star.h ----------------------------------------------------------------------------- //星球类 -----------------------------------------------------------------------------   */ #ifndef STAR_H #define STAR_H #include <QtOpenGL> #include <GL/glut.h> class Star { public:     Star();     Star(int tex,GLfloat r,GLfloat x,GLfloat y,GLfloat revS,GLfloat rotS,GLfloat a[], GLfloat d[], GLfloat p[],GLfloat s);     ~Star();     //公转     void revolute();     //自转     void rotate();     //星球半径     float radious;     //星球位置     GLfloat disX;     GLfloat disY;     //纹理id     int texId;     //环境反射光     GLfloat *ambient;     //漫反射     GLfloat *diffuse;     //镜面反射     GLfloat *specular;     //镜面反射强度     GLfloat shinniness;     //公转速度     GLfloat revSpeed;     //自转速度     GLfloat rotSpeed;     //公转角度     float revAngle;     //自转角度     float rotAngle;     /*  //公转周期     float revPeriod;     //自转周期     float rotPeriod;                    //纹理属性     GLfloat WRAP_S;     GLfloat WRAP_T;     GLfloat MAG_FILTER;     GLfloat MIN_FILTER;     GLfloat ENV_MODE;     */ };  #endif // STAR_H 

/* ----------------------------------------------------------------------------- Filename:   star.cpp ----------------------------------------------------------------------------- //星球类 -----------------------------------------------------------------------------   */ #include "star.h"  Star::Star() { } Star::Star(int tex, GLfloat r, GLfloat x, GLfloat y, GLfloat revS, GLfloat rotS, GLfloat a[], GLfloat d[], GLfloat p[], GLfloat s) {     this->texId=tex;     this->radious=r;     this->disX=x;     this->disY=y;     this->ambient=a;     this->diffuse=d;     this->specular=p;     this->shinniness=s;     this->revSpeed=revS;     this->rotSpeed=rotS;     this->revAngle=0.0;     this->rotAngle=0.0;      } Star::~Star() {} void Star::revolute() {     this->revAngle= this->revAngle + this->revSpeed< 360 ? this->revAngle + this->revSpeed: 0; } void Star::rotate() {     this->rotAngle= this->rotAngle + this->rotSpeed< 360 ? this->rotAngle + this->rotSpeed: 0; } 

/* ----------------------------------------------------------------------------- Filename:   nehewidget.h ----------------------------------------------------------------------------- //opengl渲染窗口类 -----------------------------------------------------------------------------   */ #ifndef NEHEWIDGET_H #define NEHEWIDGET_H  #include <QGLWidget> #include <QtGui> #include <QtOpenGL> #include <QtCore> #include <GL/glut.h> #include<iostream> #include "star.h" #define PI 3.14159265 class NeHeWidget : public QGLWidget {     Q_OBJECT public:     explicit NeHeWidget(QWidget *parent = 0);     ~NeHeWidget();     void zoomOut();     void zoomIn();     void enableBlend();     void disableBLend();     void calFrequency();     void speedUp();     void speedDown();     void eyeXup();     void eyeXdown();     void eyeZup();     void eyeZdown(); protected:     //设置渲染环境     void initializeGL();     //绘制窗口     void paintGL();     //响应窗口的大小变化     void resizeGL( int width, int height );     //加载纹理     void loadGLTextures(QString filename,int id);     //绘制星球     void drawStar(Star *s);     //材质设置     void setMaterial(Star *s);     //正方体在三个方向上的旋转          QFont fpsFont;     GLfloat xRot, yRot, zRot;     //纹理存储数组     GLuint texture[13];     //场景深入屏幕的距离     GLfloat zoom;     //立方体在X轴和Y轴上旋转的速度     GLfloat xSpeed, ySpeed;     //计时器,实现动画     QTimer *timer;     //帧刷新时间     int fpsSpan;     GLfloat colorSpan;     GLUquadricObj *mySphere;          Star *sky;     Star *sun;     Star *mercury;     Star *venus;     Star *earth;     Star *mars;     Star *jupiter;     Star *saturn;          GLfloat eyeX;     GLfloat eyeY;     GLfloat eyeZ;      };  #endif // NEHEWIDGET_H 


/* ----------------------------------------------------------------------------- Filename:   nehewidget.cpp ----------------------------------------------------------------------------- //opengl渲染窗口类 -----------------------------------------------------------------------------   */ #include "nehewidget.h"  NeHeWidget::NeHeWidget(QWidget *parent) :     QGLWidget(parent) {     xRot = yRot = zRot = 0.0;     zoom = -5.0;     xSpeed = ySpeed = 0.0;     fpsFont=QFont("Times", 20);     colorSpan=0;     fpsSpan=50;     timer = new QTimer(this);          timer->start(fpsSpan);     connect(timer,SIGNAL(timeout()),this,SLOT(updateGL()));     mySphere=gluNewQuadric();          eyeX=0.0;     eyeY=0.0;     eyeZ=190.0;     //夜空参数设置     GLfloat  sky_ambient[]={0.0,0.0,0.0,1.0};     GLfloat  sky_diffuse[]={0.0,0.0,0.0,1.0};     GLfloat  sky_specular[]={0.0,0.0,0.0,1.0};     GLfloat  sky_shininess=0.0;     GLfloat  sky_radious=290.0;     // GLfloat sky_rotSpeed= (GLfloat)360/58/100;     sky=new Star(0,sky_radious,0,0,0,0,sky_ambient,sky_diffuse,sky_specular,sky_shininess);          //太阳参数设置     GLfloat  sun_ambient[]={0.0,0.0,0.0,1.0};     GLfloat  sun_diffuse[]={0.0,0.0,0.0,1.0};     GLfloat  sun_specular[]={0.0,0.0,0.0,1.0};     GLfloat  sun_shininess=20.0;     GLfloat  sun_radious=10.0;     GLfloat sun_rotSpeed= (GLfloat)360/58/100;     sun=new Star(1,sun_radious,0,0,0,sun_rotSpeed,sun_ambient,sun_diffuse,sun_specular,sun_shininess);          //水星     GLfloat  mercury_ambient[]={0.0,0.0,0.0,1.0};     GLfloat  mercury_diffuse[]={0.5,0.5,0.5,1.0};     GLfloat  mercury_specular[]={0.0,0.0,0.0,1.0};     GLfloat  mercury_shininess=20.0;     GLfloat  mercury_radious=0.7;     GLfloat mecury_revSpeed=(GLfloat)360/88;     GLfloat mecury_rotSpeed= (GLfloat)360/58/100;     mercury=new Star(2,mercury_radious,15.2,0,mecury_revSpeed,mecury_rotSpeed,mercury_ambient,mercury_diffuse,mercury_specular,mercury_shininess);          //金星     GLfloat  venus_ambient[]={0.0,0.0,0.0,1.0};     GLfloat  venus_diffuse[]={0.8,0.8,0.8,1.0};     GLfloat  venus_specular[]={0.0,0.0,0.0,1.0};     GLfloat  venus_shininess=20.0;     GLfloat  venus_radious=1.24;     GLfloat venus_revSpeed=(GLfloat)360/224;     GLfloat venus_rotSpeed= (GLfloat)360/243/100;     venus=new Star(3,venus_radious,19.2,0,venus_revSpeed,venus_rotSpeed,venus_ambient,venus_diffuse,venus_specular,venus_shininess);          //地球     GLfloat  earth_ambient[]={0.1,0.1,0.1,1.0};     GLfloat  earth_diffuse[]={0.4,0.4,0.8,1.0};     GLfloat  earth_specular[]={0.0,0.0,0.0,1.0};     GLfloat  earth_shininess=20.0;     GLfloat  earth_radious=1.24;     GLfloat earth_revSpeed=(GLfloat)360/365;     GLfloat earth_rotSpeed= (GLfloat)360/1/100;     earth=new Star(4,earth_radious,26,0,earth_revSpeed,earth_rotSpeed,earth_ambient,earth_diffuse,earth_specular,earth_shininess);          //火星     GLfloat  mars_ambient[]={0.1,0.1,0.1,1.0};     GLfloat  mars_diffuse[]={0.6, 0.6, 0.6, 1.0};     GLfloat  mars_specular[]={0.0,0.0,0.0,1.0};     GLfloat  mars_shininess=20.0;     GLfloat  mars_radious=1.0;     GLfloat mars_revSpeed=(GLfloat)360/687;     GLfloat mars_rotSpeed= (GLfloat)360/1/100;     mars=new Star(5,mars_radious,31,0,mars_revSpeed,mars_rotSpeed,mars_ambient,mars_diffuse,mars_specular,mars_shininess);          //木星     GLfloat  jupiter_ambient[]={0.0, 0.0, 0.0,1.0};     GLfloat  jupiter_diffuse[]={0.6, 0.6, 0.6, 1.0};     GLfloat  jupiter_specular[]={0.0,0.0,0.0,1.0};     GLfloat  jupiter_shininess=20.0;     GLfloat  jupiter_radious=4.0;     GLfloat jupiter_revSpeed=(GLfloat)360/4329;     GLfloat jupiter_rotSpeed= (GLfloat)360/0.3/100;     jupiter=new Star(6,jupiter_radious,43,0,jupiter_revSpeed,jupiter_rotSpeed,jupiter_ambient,jupiter_diffuse,jupiter_specular,jupiter_shininess);     //土星     GLfloat  saturn_ambient[]={0.0, 0.0, 0.0,1.0};     GLfloat  saturn_diffuse[]={0.6, 0.6, 0.6, 1.0};     GLfloat  saturn_specular[]={0.0,0.0,0.0,1.0};     GLfloat  saturn_shininess=20.0;     GLfloat  saturn_radious=3.5;     GLfloat saturn_revSpeed=(GLfloat)360/10768;     GLfloat saturn_rotSpeed= (GLfloat)360/1.4/100;     saturn=new Star(7,saturn_radious,56.5,0,saturn_revSpeed,saturn_rotSpeed,saturn_ambient,saturn_diffuse,saturn_specular,saturn_shininess);           } NeHeWidget::~NeHeWidget() {} void NeHeWidget::loadGLTextures(QString filename, int id) {     QImage tex, buf;     if ( !buf.load(filename ) )     {         //如果载入不成功,自动生成一个128*128的32位色的绿×××片。         qWarning("Could not read image file!");         QImage dummy( 128, 128,QImage::Format_RGB32 );         dummy.fill( Qt::green );         buf = dummy;     }     //转换成纹理类型     tex = QGLWidget::convertToGLFormat( buf );     //创建纹理     glGenTextures( 1, &texture[id] );     //使用来自位图数据生成的典型纹理,将纹理名字texture[0]绑定到纹理目标上     glBindTexture( GL_TEXTURE_2D, texture[id] );     glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,                   GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } void NeHeWidget::drawStar(Star *s) {     glPushMatrix();     //公转     glRotatef(s->revAngle,0.0,0.0,1.0);     glTranslatef(s->disX, s->disY, 0.0);     //自转     glRotatef(s->rotAngle,0.0,0.0,1.0);     gluSphere(mySphere, s->radious, 32, 16);     //设置材质属性     glMaterialfv(GL_BACK, GL_AMBIENT, s->ambient);     glMaterialfv(GL_BACK, GL_DIFFUSE, s->diffuse);     glMaterialfv(GL_BACK, GL_SPECULAR, s->specular);     glMaterialf(GL_BACK, GL_SHININESS, s->shinniness);     //     glPopMatrix(); } void NeHeWidget::setMaterial(Star *s) {      }  void NeHeWidget::initializeGL() {     //载入纹理     loadGLTextures( ":/data/sun.jpg",sun->texId);     loadGLTextures( ":/data/mercury.bmp",mercury->texId);     loadGLTextures( ":/data/venus.jpg",venus->texId);     loadGLTextures( ":/data/earth2.jpg",earth->texId);     loadGLTextures( ":/data/mars.bmp",mars->texId);     loadGLTextures( ":/data/saturn.jpg",saturn->texId);     loadGLTextures( ":/data/jupiter.bmp",jupiter->texId);     loadGLTextures( ":/data/sky.jpg",sky->texId);     //loadGLTextures( ":/data/neptune.bmp",neptune->texId);          // 启用阴影平滑     glShadeModel( GL_SMOOTH );     // 黑色背景     glClearColor( 0.0, 0.0, 0.0, 0.0 );     // 设置深度缓存     glClearDepth( 1.0 );     // 启用深度测试     glEnable( GL_DEPTH_TEST );     //启用纹理     glEnable( GL_TEXTURE_2D );     // 所作深度测试的类型     glDepthFunc( GL_LEQUAL );     // 告诉系统对透视进行修正     glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );     //    开启剔除操作效果     //glEnable(GL_CULL_FACE);     // 使用平滑法线     gluQuadricNormals(mySphere, GL_SMOOTH);     // 使用纹理     gluQuadricTexture(mySphere, GL_TRUE);     // 设置球纹理映射      } void NeHeWidget::paintGL() {          // 清除屏幕和深度缓存     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );     //glRotatef( yRot, 0.0,  0.0,  1.0 );     glColor3f(1.0,1.0,1.0);     glBindTexture(GL_TEXTURE_2D, texture[sky->texId]);     drawStar(sky);          glBindTexture(GL_TEXTURE_2D, texture[sun->texId]);     drawStar(sun);          glBindTexture(GL_TEXTURE_2D, texture[mercury->texId]);     drawStar(mercury);          glBindTexture(GL_TEXTURE_2D, texture[venus->texId]);     drawStar(venus);          glBindTexture(GL_TEXTURE_2D, texture[earth->texId]);     drawStar(earth);          glBindTexture(GL_TEXTURE_2D, texture[mars->texId]);     drawStar(mars);          glBindTexture(GL_TEXTURE_2D, texture[jupiter->texId]);     drawStar(jupiter);          glBindTexture(GL_TEXTURE_2D, texture[saturn->texId]);     drawStar(saturn);               //旋转速度     yRot += 0.4;     sun->rotate();     mercury->revolute();     mercury->rotate();     venus->revolute();     venus->rotate();     earth->revolute();     earth->rotate();     mars->revolute();     mars->rotate();     jupiter->revolute();     jupiter->rotate();     saturn->revolute();     saturn->rotate();     glLoadIdentity();     gluLookAt (eyeX, eyeY, eyeZ, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0);     // gluLookAt (80.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0);     glFlush();     //fps的字体颜色     glColor3f(0.0f,0.0f,1.0f);     //计算FPS     calFrequency();      } // 重置OpenGL窗口大小 void NeHeWidget::resizeGL(int width, int height) {     // 防止窗口大小变为0     if ( height == 0 )     {         height = 1;     }     // 重置当前的视口     glViewport( 0, 0, (GLint)width, (GLint)height );     // 选择投影矩阵     glMatrixMode( GL_PROJECTION );     // 重置投影矩阵     glLoadIdentity();     // 设置视口的大小     gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 600.0 );     // 选择模型观察矩阵     glMatrixMode( GL_MODELVIEW );     glLoadIdentity(); } void NeHeWidget::speedUp() {     fpsSpan+=1;     qDebug()<<fpsSpan;     timer->setInterval(fpsSpan);     //timer->start(fpsSpan);     updateGL(); } void NeHeWidget::speedDown() {     if(fpsSpan>1) fpsSpan-=1;     else fpsSpan=1;     qDebug()<<fpsSpan;     timer->setInterval(fpsSpan);     updateGL(); } void NeHeWidget::eyeXup() {     eyeX+=1; } void NeHeWidget::eyeXdown() {     // if(eyeX>10) eyeX-=1;     //else eyeX=10;     eyeX-=1; } void NeHeWidget::eyeZup() {     eyeZ+=1; } void NeHeWidget::eyeZdown() {     // if(eyeX>10) eyeX-=1;     //else eyeX=10;     eyeZ-=1; } void NeHeWidget::zoomOut() {     zoom+= 0.2;          updateGL(); } void NeHeWidget::zoomIn() {     zoom -= 0.2;     updateGL(); } void NeHeWidget::calFrequency() {     static  QString tmp="";     static float framesPerSecond=0.0f;//fps的数值     static float frames    = 0.0f;       // 用于存储渲染的帧数     static float lastTime   = 0.0f;       // 前一秒的时刻     float currentTime =  glutGet(GLUT_ELAPSED_TIME)* 0.001f;//程序运行的时间     ++frames;     if( currentTime - lastTime > 1.0f )//,每秒刷新一次     {         framesPerSecond=frames;         tmp.setNum(framesPerSecond);         lastTime = currentTime;         frames= 0;     }     renderText(100,100,"FPS: "+tmp,fpsFont);//最终结果在窗口中渲染 } 
 
/* ----------------------------------------------------------------------------- Filename:   mainwindow.h ----------------------------------------------------------------------------- //主窗口类 -----------------------------------------------------------------------------   */  #ifndef MAINWINDOW_H #define MAINWINDOW_H  #include <QtGui/QMainWindow> #include <QKeyEvent> #include "nehewidget.h" class MainWindow : public QMainWindow {     Q_OBJECT      public:     MainWindow(QWidget *parent = 0);     ~MainWindow(); protected:     bool fullscreen;     //处理键盘事件     void keyPressEvent( QKeyEvent *e ); private:     NeHeWidget *neheWidget ; };  #endif // MAINWINDOW_H 


/* ----------------------------------------------------------------------------- Filename:   mainwindow.cpp ----------------------------------------------------------------------------- //主窗口类 -----------------------------------------------------------------------------   */ #include "mainwindow.h"  MainWindow::MainWindow(QWidget *parent)     : QMainWindow(parent) {     neheWidget = new NeHeWidget();     fullscreen = true;     setGeometry(100,100,1000,768);     setWindowTitle(tr("NeHe's OpenGL Framework"));     setCentralWidget(neheWidget); }  MainWindow::~MainWindow() {      } void MainWindow::keyPressEvent(QKeyEvent *e) {     switch ( e->key() )     {     case Qt::Key_F2:         fullscreen = !fullscreen;         if ( fullscreen )         {             showFullScreen();         }         else         {             showNormal();         }         neheWidget->updateGL();         break;     case Qt::Key_Escape:         close();         break;     case Qt::Key_PageUp:         neheWidget->zoomOut();         break;     case Qt::Key_PageDown:         neheWidget->zoomIn();         break;     case Qt::Key_Down:         neheWidget->speedUp();         break;     case Qt::Key_Up:         neheWidget->speedDown();         break;     case Qt::Key_W:         neheWidget->eyeXup();         break;     case Qt::Key_S:         neheWidget->eyeXdown();         break;     case Qt::Key_E:         neheWidget->eyeZup();         break;     case Qt::Key_D:         neheWidget->eyeZdown();         break;     } } 


//main函数 #include <QtGui/QApplication> #include "mainwindow.h"  int main(int argc, char *argv[]) {     QApplication a(argc, argv);     MainWindow w;     glutInit(&argc, argv);     w.show();          return a.exec(); } 
 四。程序中未完善的地方      这个程序应该只能算是一个大致的框架,还是有很多地方可以改进,比如:添加天王星,海王星,冥王星,添加每个星球的倾角,添加月球....有兴趣的同学可以继续完善,我们可以继续讨论。  参考资料

1.      《 OpenGL Reference Manual 》, OpenGL 参考手册

2.      《 OpenGL 编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波译,机械工业出版社

3.         《win32 OpenGL编程 》   一个大牛的博客     http://blog.csdn.net/vagrxie/article/category/628716/3