1. 程式人生 > >Qt下的OpenGL 程式設計(11)Qt例項hellogl程式碼解析

Qt下的OpenGL 程式設計(11)Qt例項hellogl程式碼解析

一、提要

    還記得我們在第一篇教程中執行的例子嗎?那是那個可以到處轉的大Q,今天我們就來分析一下這個Qt自帶的OpenGL例子。

    

二、檔案結構
如上圖,專案中共有三個類.
glwidget:opengl的渲染視窗,主要負責圖形的繪製,同時響應鍵盤滑鼠事件;
window:主視窗類,負責介面的佈局,一些訊號和槽的實現;
qtlogo:圖形類,負責logo的繪製。


三、glwidget類的分析

  首先來看glwidget的標頭檔案:
 
 #ifndef GLWIDGET_H
  #define GLWIDGET_H


  #include <QGLWidget>


  class QtLogo;


  //! [0]
  class GLWidget : public QGLWidget
  {
      Q_OBJECT


  public:
      GLWidget(QWidget *parent = 0);
      ~GLWidget();


      QSize minimumSizeHint() const;
      QSize sizeHint() const;
  //! [0]


  //! [1]
  public slots:
      void setXRotation(int angle);
      void setYRotation(int angle);
      void setZRotation(int angle);


  signals:
      void xRotationChanged(int angle);
      void yRotationChanged(int angle);
      void zRotationChanged(int angle);
  //! [1]


  //! [2]
  protected:
      void initializeGL();
      void paintGL();
      void resizeGL(int width, int height);
      void mousePressEvent(QMouseEvent *event);
      void mouseMoveEvent(QMouseEvent *event);
  //! [2]


  //! [3]
  private:
      QtLogo *logo;
      int xRot;
      int yRot;
      int zRot;
      QPoint lastPos;
      QColor qtGreen;
      QColor qtPurple;
  };
  //! [3]


  #endif


       首先是類的建構函式和解構函式,建構函式中可以對成員進行初始化,析夠函式
   可以在不再需要這個物件的時候將不再需要的資料刪除掉。
        接下來的
        QSize minimumSizeHint() const;
        QSize sizeHint() const;
        用來設定視窗的最小尺寸。


        接下來的三個signal和三個slot分別對應三個方向的旋轉:X,Y,Z。


        protected成員中有三個最主要的函式:initializeGL()負責初始化,paintGL()
  負責渲染視窗,resizeGL(int width, int height)在視窗大小變化時呼叫。後面兩個
  則是定義滑鼠點選和移動的事件。


        剩下的一些私有變數則是用來記錄旋轉角度,或是用來初始化logo的變數。


        接下來看一下它的實現:


        
#include <QtGui>
        #include <QtOpenGL>


        #include <math.h>


        #include "glwidget.h"
        #include "qtlogo.h"


        #ifndef GL_MULTISAMPLE
        #define GL_MULTISAMPLE  0x809D
        #endif


        //! [0]
        GLWidget::GLWidget(QWidget *parent)
            : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
        {
            logo = 0;
            xRot = 0;
            yRot = 0;
            zRot = 0;


            qtGreen = QColor::fromCmykF(0.40, 0.0, 1.0, 0.0);
            qtPurple = QColor::fromCmykF(0.39, 0.39, 0.0, 0.0);
        }
        //! [0]


        //! [1]
        GLWidget::~GLWidget()
        {
        }
        //! [1]


        //! [2]
        QSize GLWidget::minimumSizeHint() const
        {
            return QSize(50, 50);
        }
        //! [2]


        //! [3]
        QSize GLWidget::sizeHint() const
        //! [3] //! [4]
        {
            return QSize(400, 400);
        }
        //! [4]


        static void qNormalizeAngle(int &angle)
        {
            while (angle < 0)
                angle += 360 * 16;
            while (angle > 360 * 16)
                angle -= 360 * 16;
        }


        //! [5]
        void GLWidget::setXRotation(int angle)
        {
            qNormalizeAngle(angle);
            if (angle != xRot) {
                xRot = angle;
                emit xRotationChanged(angle);
                updateGL();
            }
        }
        //! [5]


        void GLWidget::setYRotation(int angle)
        {
            qNormalizeAngle(angle);
            if (angle != yRot) {
                yRot = angle;
                emit yRotationChanged(angle);
                updateGL();
            }
        }


        void GLWidget::setZRotation(int angle)
        {
            qNormalizeAngle(angle);
            if (angle != zRot) {
                zRot = angle;
                emit zRotationChanged(angle);
                updateGL();
            }
        }


        //! [6]
        void GLWidget::initializeGL()
        {
            qglClearColor(qtPurple.dark());


            logo = new QtLogo(this, 64);
            logo->setColor(qtGreen.dark());


            glEnable(GL_DEPTH_TEST);
            glEnable(GL_CULL_FACE);
            glShadeModel(GL_SMOOTH);
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
            glEnable(GL_MULTISAMPLE);
            static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
            glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
        }
        //! [6]


        //! [7]
        void GLWidget::paintGL()
        {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glLoadIdentity();
            glTranslatef(0.0, 0.0, -10.0);
            glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
            glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
            glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);
            logo->draw();
        }
        //! [7]


        //! [8]
        void GLWidget::resizeGL(int width, int height)
        {
            int side = qMin(width, height);
            glViewport((width - side) / 2, (height - side) / 2, side, side);


            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
        #ifdef QT_OPENGL_ES_1
            glOrthof(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
        #else
            glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
        #endif
            glMatrixMode(GL_MODELVIEW);
        }
        //! [8]


        //! [9]
        void GLWidget::mousePressEvent(QMouseEvent *event)
        {
            lastPos = event->pos();
        }
        //! [9]


        //! [10]
        void GLWidget::mouseMoveEvent(QMouseEvent *event)
        {
            int dx = event->x() - lastPos.x();
            int dy = event->y() - lastPos.y();


            if (event->buttons() & Qt::LeftButton) {
                setXRotation(xRot + 8 * dy);
                setYRotation(yRot + 8 * dx);
            } else if (event->buttons() & Qt::RightButton) {
                setXRotation(xRot + 8 * dy);
                setZRotation(zRot + 8 * dx);
            }
            lastPos = event->pos();
        }



         建構函式中首先對幾個私有變數進行了初始化,析夠函式中什麼都沒有做(自動回收?),
接下來的qNormalizeAngle函式,是用於糾正角度的,因為在旋轉了很多圈之後,角度會變得很大,需要對其進行糾正。
    接下來是對三個槽函式的定義,在呼叫之前都要對角度進行糾正,然後改變相應的角度的值,接著發出角度改變的訊號。
這個訊號最後是和window類中的控制滑動條的槽連結起來了,當logo發生旋轉的時候,就會把旋轉的角度告訴給window,然後
window的槽執行相應的語句,滑動條位置發生改變。
    相反,當window的滑動條數值發生改變的時候,也會發出相應的訊號給glwiget的三個槽函式,然後logo執行旋轉。


    initializeGL中對logo進行了初始化,還定義了燈光等的引數,比較好理解。


    paintGL中我們主要來看一下座標的變換:首先是把繪製點移到了(0,0,-10)的
位置,然後繞三個軸進行旋轉,做後將logo繪製出來(logo的繪製還是有點麻煩,有興趣的同學可以慢慢研究一下qtlogo類)。


    mouseMoveEvent則是對滑鼠拖動的響應,左鍵拖動和右鍵拖動分別繞不同的方向旋轉。
呼叫的是之前定義的set槽函式。
    所以這個類的槽函式有兩個作用:1.當作一般的函式;2.響應某個訊號。
三、參考
    QT幫助。