1、提要
今天的內容是OpenGL的編程實踐—太陽系的模擬!
紅寶書上有相應的教程,但這裏咱們要實現得更全面一些。iPad上有一個很棒的應用,名字叫Solar System,咱們儘可能去達到它的效果。
先來看一下最終效果:
、
思路:
創建9個球體,分別賦予不一樣的材質,再經過動畫不斷變換它們的位置,就能夠實現模擬了。
2、有關太陽系的知識
太陽系有一顆恆星:太陽,8顆行星:水,金,地,火,木,土,天王,海王。9顆星球的位置以下:
而後去搜集了解一下各個星球的自轉和公轉週期,腦殼裏面有個概念。
接着能夠去找一些星球貼圖的素材了,我在網上找到了一些資源,太陽的貼圖是本身用ps繪製的,而後把它們統統添加到資源文件中,就像這樣:
3、程序結構
相比與以前的框架,這裏添加了一個星球類,文件結構以下:
程序仍是比較清晰,直接貼代碼了:
/* ----------------------------------------------------------------------------- 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 參考手冊ios
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