最近發現了Qt在界面設計方面的優良性能,遂決定開始學習,奈何自己出身計算機圖形學專業,如能將它們結合起來自是最好啦。
參考Nokia Developer網站上關於《如何在Qt中使用OpenGl》的文章,記錄下自己的實際使用過程。http://www.developer.nokia.com/Community/Wiki/%E5%A6%82%E4%BD%95%E5%9C%A8Qt%E4%B8%AD%E4%BD%BF%E7%94%A8OpenGL,
同時也參考了tornadomeet的《OpenGL_Qt學習筆記之_01(創建一個OpenGL窗口)》http://www.cnblogs.com/tornadomeet/archive/2012/08/22/2651574.html
OpenGL(Open Graphics Library)我就不多說了,它定義了一個跨編程語言、跨平台的編程接口的規格,用於生成二維、三維圖像。Qt主要通過QGLWidget這個類來實現,將opengl的函數和Qt的界面結合。所以一般開發opengl,同時要用到Qt的界面時,這些類都可以從QGLWidget繼承過來。
開發環境:windows 7+Qt 4.7.3+QtCreater 2.5.0
1.選擇工程類型時,選擇Qt Gui Application
2.進入Class Information界面時,此時基類可以選擇QMainWindow或者QWidget,因為后面還是要進行修改,所以這里差別不大。但是沒有試過QDialog,不知道情況怎么樣。
之后的實驗來自Nokia Developer
OpenGLQt.pro
為了使用QtOpenGL Module, 我們必須設.pro文件
QT += core gui\ opengl \\這里新加了一個opengl選項 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = BBSS_System_QT TEMPLATE = app SOURCES += main.cpp\ openglqt.cpp HEADERS += openglqt.h FORMS += openglqt.ui
openglqt.h
ps: OpenGLQt類繼承於QGLWidget,主要是重寫了3個函數,initializeGL();paintGL();resizeGL();這3個函數都是QGLWidget內部的虛函數。
#ifndef OPENGLQT_H #define OPENGLQT_H #include <QWidget> #include <QtOpenGL> namespace Ui { class OpenGLQt; } class OpenGLQt : public QGLWidget { Q_OBJECT public: explicit OpenGLQt(QWidget *parent = 0); ~OpenGLQt(); private: Ui::OpenGLQt *ui; protected: void initializeGL(); void resizeGL(int width, int height); void paintGL(); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); private: void draw(); int faceAtPosition(const QPoint &pos); GLfloat rotationX; GLfloat rotationY; GLfloat rotationZ; QColor faceColors[4]; QPoint lastPos; void Spin(int xAngle, int yAngle, int zAngle); private slots: void Rotate(); }; #endif // OPENGLQT_H
openglqt.cpp
#include "openglqt.h" #include "ui_openglqt.h" OpenGLQt::OpenGLQt(QWidget *parent) : QGLWidget(parent), ui(new Ui::OpenGLQt) { //QGLWidget::setFormat() to specify the OpenGL display context setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer)); rotationX = -21.0; rotationY = -57.0; rotationZ = 0.0; // Note: Square faces are set with static color and are cannot be changed during runtime // set the color of the faces of Tetrahedron faceColors[0] = Qt::red; faceColors[1] = Qt::green; faceColors[2] = Qt::blue; faceColors[3] = Qt::yellow; initializeGL(); resizeGL(51,51); paintGL(); //timer to spin the object QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(Rotate())); timer->start(20); } OpenGLQt::~OpenGLQt() { delete ui; } void OpenGLQt::initializeGL() { qglClearColor(Qt::black); glShadeModel(GL_FLAT); //設置陰影平滑模式 glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } void OpenGLQt::resizeGL(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); GLfloat x = GLfloat(width) / height; glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0); glMatrixMode(GL_MODELVIEW); } void OpenGLQt::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(); } void OpenGLQt::draw() { //change the value if don't want to spin automatically and want to check the mouse event // glRotatef(rotationX/16.0f, 1.0, 0.0, 0.0); to glRotatef(rotationX, 1.0, 0.0, 0.0); // glRotatef(rotationY/16.0f, 0.0, 1.0, 0.0); to glRotatef(rotationY, 0.0, 1.0, 0.0); //glRotatef(rotationZ/16.0f, 0.0, 0.0, 1.0); to glRotatef(rotationZ, 0.0, 0.0, 1.0); // Draw Tetrahedron static const GLfloat P1[3] = { 0.0, -1.0, +2.0 }; static const GLfloat P2[3] = { +1.73205081, -1.0, -1.0 }; static const GLfloat P3[3] = { -1.73205081, -1.0, -1.0 }; static const GLfloat P4[3] = { 0.0, +2.0, 0.0 }; static const GLfloat * const coords[4][3] = { { P1, P2, P3 }, { P1, P3, P4 }, { P1, P4, P2 }, { P2, P4, P3 } }; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); glRotatef(rotationX/16.0f, 1.0, 0.0, 0.0); glRotatef(rotationY/16.0f, 0.0, 1.0, 0.0); glRotatef(rotationZ/16.0f, 0.0, 0.0, 1.0); for (int i = 0; i < 4; ++i) { glLoadName(i); glBegin(GL_TRIANGLES); qglColor(faceColors[i]); for (int j = 0; j < 3; ++j) { glVertex3f(coords[i][j][0], coords[i][j][1], coords[i][j][2]); } glEnd(); } } void OpenGLQt::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); } void OpenGLQt::mouseMoveEvent(QMouseEvent *event) { GLfloat dx = GLfloat(event->x() - lastPos.x()) / width(); GLfloat dy = GLfloat(event->y() - lastPos.y()) / height(); if (event->buttons() & Qt::LeftButton) { rotationX += 180 * dy; rotationY += 180 * dx; updateGL(); } else if (event->buttons() & Qt::RightButton) { rotationX += 180 * dy; rotationZ += 180 * dx; updateGL(); } lastPos = event->pos(); } void OpenGLQt::mouseDoubleClickEvent(QMouseEvent *event) { int face = faceAtPosition(event->pos()); if (face != -1) { QColor color = QColorDialog::getColor(faceColors[face], this); if (color.isValid()) { faceColors[face] = color; updateGL(); } } } int OpenGLQt::faceAtPosition(const QPoint &pos) { const int MaxSize = 512; GLuint buffer[MaxSize]; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glSelectBuffer(MaxSize, buffer); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(GLdouble(pos.x()), GLdouble(viewport[3] - pos.y()), 5.0, 5.0, viewport); GLfloat x = GLfloat(width()) / height(); glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0); draw(); glMatrixMode(GL_PROJECTION); glPopMatrix(); if (!glRenderMode(GL_RENDER)) return -1; return buffer[3]; } void OpenGLQt::Rotate() { //QMessageBox::about(this,"Hello","world"); this->Spin(+2 * 16, +2 * 16, -1 * 16); } void OpenGLQt::Spin(int xAngle, int yAngle, int zAngle) { rotationX += xAngle; rotationY += yAngle; rotationZ += zAngle; updateGL(); }
- 在構造函數中調用setFormat 設置OPENGL的顯示方式.
- 函數initializeGL()在paintGL()之前調用,且只調用一次,在這里可以設置OpenGL的顯示內容,定義顯示列表或者其他初始化操作。其中qglClearColor()是QGLWidget的函數,其他函數都是OpenGL標准函數。如果全部遵循OpenGL庫,可以調用RGBA格式的glClearColor()函數和顏色索引函數glClearIndex()。
- PaintGL() 函數將在任何需要重繪的時候調用,真正是在draw 函數中執行繪圖操作
- 在draw函數中我們繪制四面體
- Spin() 函數用於旋轉四面體到特定的角度
main.cpp
#include <QApplication> #include "openglqt.h" #include <iostream> #include <QtOpenGL> using namespace std; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (!QGLFormat::hasOpenGL()) { cerr << "This system has no OpenGL support" << endl; return 1; } OpenGLQt openglqt; openglqt.setWindowTitle(QObject::tr("OpenGL Qt")); openglqt.resize(300, 300); openglqt.show(); return a.exec(); }
運行結果
編譯和運行能夠正常,可是當關閉的時候就出現錯誤提示:"
ASSERT: "group->context() == q_ptr" in file .\qgl.cpp, line 1657
上網搜了一下這個問題,在http://blog.sina.com.cn/s/blog_404dab3b0101f2e5.html中找到了解決方案:
原來是現在的版本不能在構造函數里設置:
setFormat(QGLFormat(QGL::DoubleBuffer|QGL::DepthBuffer))
只要這樣設置就會在關閉程序的時候出錯。所以只有在構造函數的初始化列表里傳遞參數設置,比如:
OpenGLQt::OpenGLQt(QWidget *parent) : QGLWidget(QGLFormat(QGL::DoubleBuffer|QGL::DepthBuffer),parent), ui(new Ui::OpenGLQt){ ...... }
這樣果然在關閉程序的時候不會再拋出錯誤了。