Qt+OpenGL联合编程(1)
最近发现了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){ ...... }
这样果然在关闭程序的时候不会再抛出错误了。