8-5 用OpenGL绘图(Graphics with OpenGL)

  
8-5 用OpenGL绘图(Graphics with OpenGL)
OpenGL是绘制2D和3D模型的标准API。在OpenGL基础上,Qt可以使用QtOpenGL模块绘制3D图形。本节假设您已经熟悉OpenGL。如果对OpenGL不了解,可以浏览 http://www.opengl.org/
在Qt应用程序中使用OpenGL绘图非常简单:我们需要从QGLWidget继承自己的控件类,实现一些虚函数,连接到QtOpenGL和OpenGL库。因为QGLWidget从QWidget继承,我们以前学习的控件内容仍然适用。主要区别是我们使用OpenGL函数绘图而不是使用QPainter。
为了说明OpenGL的工作法方式,我们查看图8.17所示的四面体程序。这个程序显示了一个3D的四面体,每一个面都由不同的颜色显示。用户可以通过鼠标点击或者托拽进行旋转。双击一个面,会弹出QColorDialog,选择一个其他的颜色。
Figure 8.17. The Tetrahedron application
 

class Tetrahedron : public QGLWidget

{

    Q_OBJECT

public:

    Tetrahedron(QWidget *parent = 0);

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;

};

类Tetrahedron继承自QGLWidget,函数initializeGL(),resizeGL()和paintGL()是从QGLWidget继承的虚函数。鼠标事件的处理函数是从QWidget继承。

 

Tetrahedron::Tetrahedron(QWidget *parent)

    : QGLWidget(parent)

{

    setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));

    rotationX = -21.0;

    rotationY = -57.0;

    rotationZ = 0.0;

    faceColors[0] = Qt::red;

    faceColors[1] = Qt::green;

    faceColors[2] = Qt::blue;

    faceColors[3] = Qt::yellow;

}

在构造函数中,调用QGLWidget::setFormat()确定OpenGL的显示方式。然后初始化类的私有函数。

void Tetrahedron::initializeGL()

{

    qglClearColor(Qt::black);

    glShadeModel(GL_FLAT);

    glEnable(GL_DEPTH_TEST);

    glEnable(GL_CULL_FACE);

}

函数initializeGL()在paintGL()之前调用,且只调用一次,在这里可以设置OpenGL的显示内容,定义显示列表或者其他初始化操作。

其中qglClearColor()是QGLWidget的函数,其他函数都是OpenGL标准函数。如果全部遵循OpenGL库,可以调用RGBA格式的glClearColor()函数和颜色索引函数glClearIndex()。

void Tetrahedron::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);

}

函数resizeGL()在paintGL()之前开始调用,在任何时候只要控件大小改变,都会调用这个函数。在这个函数中可以设置OpenGL的视口,投影和其他与控件大小有关的设置。

void Tetrahedron::paintGL()

{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    draw();

}

 

函数paintGL()在控件需要重新绘制时调用,和QWidget控件的paintEvent()函数相似,不同的地方是绘制openGL控件时,使用OpenGL函数。实际的绘制由私有函数draw()实现。

void Tetrahedron::draw()

{

    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, 1.0, 0.0, 0.0);

    glRotatef(rotationY, 0.0, 1.0, 0.0);

    glRotatef(rotationZ, 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();

    }

}

在函数draw()中,我们参照x,y,z的坐标和faceColor中的颜色,绘制了这个四面体。除了glColor(),其他所有的函数都是调用OpenGL库。我们可以根据OpenGL模式使用glColor3d()或者glIndex()代替

void Tetrahedron::mousePressEvent(QMouseEvent *event)

{

    lastPos = event->pos();

}

void Tetrahedron::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();

}

 

函数mousePressEvent()和mouseMoveEvent()是对QWidget类的重写,使用户通过鼠标点击或者拖动实现旋转。点击鼠标左键则沿x轴和y轴方向旋转,点击右键沿x轴和z轴旋转。

在修改了rotationX变量,rotationY变量或者rotationZ变量后,调用updateGL()重新绘制控件。

void Tetrahedron::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();

        }

    }

}

函数mouseDoubleClickEvent()重写了QWidget的同名函数,允许用户双击控件设置四面体的一个面的颜色。私有函数faseAtPosition()得到鼠标双击位置所在四面体的那个面,如果双击了某一个面,调用QColorDialog::getColor()得到一个面的新的颜色。然后更新变量faceColors数组,调用updageGL()重新绘制控件。

int Tetrahedron::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];

}

 

函数faceAtPosition()返回控件上某一个位置所在的平面号,如果没有在平面上则返回-1。使用OpenGL实现代码有些复杂。实际上,我们用GL_SELECT模式绘制四面体,利用OpenGL的点获取功能,然后得到平面号。

下面是main.cpp的实现代码:

#include <QApplication>

#include <iostream>

#include "tetrahedron.h"

using namespace std;

int main(int argc, char *argv[])

{

    QApplication app(argc, argv);

    if (!QGLFormat::hasOpenGL()) {

        cerr << "This system has no OpenGL support" << endl;

        return 1;

    }

    Tetrahedron tetrahedron;

    tetrahedron.setWindowTitle(QObject::tr("Tetrahedron"));

    tetrahedron.resize(300, 300);

    tetrahedron.show();

    return app.exec();

}

 

如果用户的系统不支持OpenGL,在控制台上打印一条错误消息然后退出。

在.pro文件中,需要应用程序连接到QtOpenGL库:

QT             += opengl

 

到现在,这个四面体的程序就全部完成了。如果想了解更多的QtOpenGL,可以查看参考文档中QGLWidget,QGLFormat,QGLContext和QGLPixelBuffer。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>