Qt下的OpenGL 编程(10)Solar System

一、提要
 今天的内容是OpenGL的编程实践—太阳系的模拟!
 红宝书上有相应的教程,但这里我们要实现得更全面一些。iPad上有一个很棒的应用,名字叫Solar System,我们尽量去达到它的效果。
 先来看一下最终效果:
    Qt下的OpenGL 编程(10)Solar System_第1张图片
Qt下的OpenGL 编程(10)Solar System_第2张图片


 
 


 
 
思路:
 建立9个球体,分别赋予不同的材质,再通过动画不断变换它们的位置,就可以实现模拟了。

 
 
二、有关太阳系的知识
 太阳系有一颗恒星:太阳,8颗行星:水,金,地,火,木,土,天王,海王。9颗星球的位置如下:
Qt下的OpenGL 编程(10)Solar System_第3张图片


 
 
 
 
 然后去搜集了解一下各个星球的自转和公转周期,脑袋里面有个概念。
 接着可以去找一些星球贴图的素材了,我在网上找到了一些资源,太阳的贴图是自己用ps绘制的,然后把它们通通添加到资源文件中,就像这样:
    Qt下的OpenGL 编程(10)Solar System_第4张图片

 
 
三、程序结构
 相比与之前的框架,这里添加了一个星球类,文件结构如下:
 程序还是比较清晰,直接贴代码了:
 
/*
-----------------------------------------------------------------------------
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
 
 
 
 
 
 
 
 
 
 
 

你可能感兴趣的:(编程,filter,sun,qt,360,float)