Qt上的OpenGL 编程(11)Qt实例hellogl代码解析

Qt下的OpenGL 编程(11)Qt实例hellogl代码解析


一、提要


    还记得我们在第一篇教程中运行的例子吗?那是那个可以到处转的大Q,今天我们就来分析一下这个Qt自带的OpenGL例子。

    Qt上的OpenGL 编程(11)Qt实例hellogl代码解析


二、文件结构
如上图,项目*有三个类.
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帮助。