OpenGL图象 |
发布: 2008-08-12 23:49 |
OpenGL是一种渲染2D和3D图象的标准API。Qt程序可以使用QtOpenGL模块绘制3D图象,这依赖于系统的OpenGL库。这一节假设你已经熟悉了OpenGL。如果OpenGL对你来说是新的,一个开始学习的好地方是http://www.opengl.org/。 在Qt程序中使用OpenGL绘图是非常直接的:我们必须继承QGLWidget,重新实现几个虚函数,并把程序与QtOpenGL和OpenGL库链接。因为QGLWidget继承自QWidget,我们已经知道的大多数依然可用。主要的不同是我们使用标准的OpenGL函数执行绘图而不是用QPainter。 为了展示这是如何工作的,我们预览一下图8.17中展示的四面体程序的代码。该程序展示了一个3D的四面体,或者叫四边骰子,它的每一面都使用不同的颜色绘制。用户可以通过按下鼠标并拖动来旋转该四面体。用户也可以通过双击它并从弹出的QcolorDialog选择一种颜色设置其中一面的颜色。 图8.17 四面体程序 [code type="cpp-qt"] 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; }; [/code] 该Tetrahedron类继承自QGLWidget。initializeGL(), resizeGL(), 和 paintGL()函数都是重新实现了QGLWidget中的。鼠标事件处理器像通过一样是重新实现了QWidget中的虚函数。 [code type="cpp-qt"] 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; } [/code] 在构造函数中,我们调用QGLWidget::setFormat()来指定OpenGL显示上下文,并初始化该类的私有变量。 [code type="cpp-qt"] void Tetrahedron::initializeGL() { qglClearColor(Qt::black); glShadeModel(GL_FLAT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } [/code] initializeGL()函数仅在paintGL()调用前调用了一次。这是我们可以设置渲染上下文,定义显示列表,和执行其他初始化的地方。 所有代码都是标准的OpenGL,除了QGLWidget的qglClearColor()函数之外。如果我们希望坚持使用OpenGL,我们可以在RGBA模式下调用glClearColor(),在颜色索引模式调用glClearIndex()。 [code type="cpp-qt"] 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); } [/code] resizeGL()函数在paintGL()首次调用前,在initializeGL()被调用之后被调用。它还在物件在任何调整尺寸的时候被调用。这是我们可以设置OpenGL视口、投影以及任何依赖于物件尺寸的其他设置的地方。 [code type="cpp-qt"] void Tetrahedron::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(); } [/code] paintGL()函数在任何物件需要重绘的时候调用。这与QWidget::paintEvent()很相似,但与QPainter函数相反,我们使用OpenGL函数。实现的绘图是在私有函数draw()中执行的。 [code type="cpp-qt"] 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(); } } [/code] 在draw()中,我们绘制该四面体,利用x, y, 和 z旋转和存储在faceColors数组中的颜色。所有都是标准OpenGL,除了qglColor()调用之外。我们本应该使用glColor3d() 或者 glIndex()中的一个,具体用哪个依赖于当前的模式。 [code type="cpp-qt"] 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(); } [/code] mousePressEvent() 和 mouseMoveEvent()函数也是重新实现自QWidget以允许用户通过点击和拖动旋转该视图。鼠标左键允许用户沿x和y轴旋转,鼠标右键允许用户沿x和z轴旋转。 在修改了rotationX、rotationY 或者rotationZ变量后,我们调用updateGL()重绘该场景。 [code type="cpp-qt"] 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(); } } } [/code] mouseDoubleClickEvent()重新实现自QWidget以允许用户通过双击它设置四面体各面的颜色。我们调用私有函数faceAtPosition()来决定哪一面,如果有的话,位于鼠标的下面。如果一个面被双击,我们调用QColorDialog::getColor()为该面获取一个新的颜色。然后我们用新的颜色值更新faceColors数组,再调用updateGL()来重绘该场景。 [code type="cpp-qt"] 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]; } [/code] faceAtPosition()函数物件上特定点所在面的序号,或者如果该点没有面则返回-1。决定这个值的代码在OpenGL中有点小复杂。本质上来说,我们所做的只是在GL_SELECT模式下渲染该场景以充分利用OpenGL的挑选功能,然后就可从OpenGL的命中记录中获取面的序号(它的“名字”)。 下面是main.cpp: [code type="cpp-qt"] #include #include #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(); } [/code] 如果用户的系统不支持OpenGL,我们向控制台打印一个错误消息并立即返回。 要把该程序与QtOpenGL模块和系统的OpenGL库链接,.pro文件中需要下面的一条: QT += opengl 该四面体程序就完成了。为了更多关于QtOpenGL模块的信息,请看手册中QGLWidget, QGLFormat, QGLContext, QGLColormap, 和 QGLPixelBuffer的文档。 |
原文: http://qtchina.tk/?q=node/242 |
Powered by zexport
|