Qt编程进阶(99):使用OpenGL绘制三维图形

Qt编程进阶(99):使用OpenGL绘制三维图形

首页休闲益智绘制像素3D更新时间:2024-08-03

一、Qt中的OpenGL支持

OpenGL是绘制三维图形的标准API。Qt应用程序可以使用QtOpenGL模块绘制三维图形,该模块依赖于系统的OpenGL库。Qt OpenGL模块提供QGLWidget类,可以通过对它的子类化,并使用OpenGL命令开发出自己的窗口部件。对于许多三维应用程序来说,这就足够了。

从Qt 4开始,可以在QGLWidget上使用QPainter,尽管它还只是一个普通的窗口部件。在QGLWidget上使用QPainter的好处是可以使用OpenGL高效地绘图,例如进行坐标变换以及像素映射绘制。使用QPainter的另一个好处是可以使用其二维的高级API,并结合OpenGL调用来绘制三维图形。

使用QGLWidget可以在场景中使用OpenGL作为后端绘制三维场景。为了在硬件加速的离屏表面上绘制,可以使用pbuffer和framebuffer对象进行扩展,它们分别在QGLPixelBuffer和QGLFramebufferObject类中。

在应用程序中绘制OpenGL是很简单的:派生QGLWidget,实现几个虚函数,连接QtOpenGL和OpenGL库。由于QGLWidget派生自QWidget,许多已知的技术仍然适用。最大的区别是要使用标准的OpenGL函数替代QPainter来实现绘制。

为了使应用程序正确连接QtOpenGL模块和系统的OpenGL库,需要在.pro文件中添加这两项:

QT = opengl LIBS = -lopengl32 -lglu32

本文介绍一个简单的应用程序,该程序使用这一技术绘制一个三角锥,用户可以使用鼠标与其交互。

二、Tetrahedron类

1.Tetrahedron类的定义

为了介绍QGLWidget的用法,我们编写一个Tetrahedron应用程序。该应用程序可以显示一个三维的三角锥(tetrahedron),或者也可以称为四面体,它的每个面都具有不同的颜色。用户可以通过单击并拖动鼠标来旋转它。用户也可以通过双击某个面,并从弹出的QColorDialog中选择一种颜色来设置这个面的颜色。

class Tetrahedron : public QGLWidget { Q_OBJECT public: Tetrahedron(QWidget *parent = nullptr); ~Tetrahedron(); protected: void initializeGL(); void resizeGL(int w, int h); 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中实现。

2.OpenGL设置

首先是构造函数:

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的绘图描述表,定义显示列表,以及执行其他的初始化。

所有的代码都是标准的OpenGL,除了对QGLWidget的qglClearColor()函数的调用。如果想坚持使用标准的OpenGL,则可以在RGBA模式下调用glClearColor(),而在颜色索引模式下调用glClearlndex()。

void Tetrahedron::resizeGL(int w, int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); GLfloat x=GLfloat(w)/h; glFrustum(-x, x,-1.0, 1.0,4.0,15.0); glMatrixMode(GL_MODELVIEW); }

应在第一次调用paintGL()之前,但在initializeGL()之后调用resizeGL()函数。在窗口部件改变大小时也将调用resizeGL()函数。在这里可以设值OpenGL视口、投影以及其他窗口部件尺寸相关的设置。

void Tetrahedron::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(); }

在窗口部件需要重绘时调用paintGL()函数。它与QWidget::PaintEvent()类似,使用OpenGL函数代替QPainter函数。实际的绘制由私有函数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轴的旋转以及保存在数组中的颜色。每个调用都是标准的OpenGL,除了qglColor()函数。可以根据颜色模式使用OpenGL函数glColor3d()或者glIndex()。

3.事件处理

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中实现,用户可以双击三角锥的面设置其颜色。调用私有函数faceAtPosition()确定光标下的面。如果用户双击了某个面,就调用QColorDialog::getColor()为该面获取一种新的颜色。然后,使用新颜色更新faceColor数组,并且调用updateGL()重绘场景。

int Tetrahedron::faceAtPosition(const QPoint &pos) { const int MaxSize = 512; GLuint buffer[MaxSize]; GLint viewport[4]; makeCurrent(); 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的选择功能,从OpenGL的命中记录中读取面编号(它的“名字”)。代码是标准的代码,除了起初的QGLWidget::makeCurrent()调用,它可以确保我们正确地使用OpenGL描述表。[QGLWidget在调用initializeGL()、resizeGL()或paintGL()之前会自动调用它,在Tetrahedron实现的其他地方则不需要调用它。]

三、tetrahedron的调用

以下是该应用程序的main()函数:

int main(int argc, char *argv[]) { QApplication a(argc, argv); if(!QGLFormat::hasOpenGL()){ std::cerr<<"This system has no OpenGL support"<<std::endl; return 1; } Tetrahedron tetrahedron; tetrahedron.setWindowTitle("Tetrahedron"); tetrahedron.resize(300,300); tetrahedron.show(); return a.exec(); }

如果用户的系统不支持OpenGL,则会在控制台打印错误信息并立即返回。

这就完成了Tetrahedron应用程序。

——————————————————

对于本文实例完整代码有需要的朋友,可关注并在评论区留言!

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved