Qt编程进阶(101):使用帧缓存对象生成叠加,避免重复绘制场景

Qt编程进阶(101):使用帧缓存对象生成叠加,避免重复绘制场景

首页休闲益智堆叠运行更新时间:2024-05-04

通常,我们需要在一个复杂的三维场景中绘制简单的注解。如果场景非常复杂,可能需要几秒钟来绘制它。为了避免重复绘制场景,每当注解改变吋,我们可以使用OpenGL内置支持的叠加。

pbuffers和framebuffer对象提供了更方便和更灵活的制作叠加的方法。基本的思路是在离屏表面绘制三维场景,并将其绑定纹理。纹理通过绘制矩形映射到屏幕,注解绘制在上面。当注解改变时,只需重绘矩形和注解。

为了阐述这一技术,我们将介绍如下图所示的Teapots应用程序。该应用程序由单个OpenGL窗口组成,该窗口显示了一组茶壶,用户可以在上面单击并拖动鼠标来绘制橡皮筋选择框。茶壶不以任何方式移动或改变,除非窗口大小发生变化。实现上依靠framebuffer对象来保存茶壶的场景。也以使用pbuffer来实现相同的效果,用QGLPixelBuffer代替QGLFramebufferObject。

Teapots类的定义

class Teapots : public QGLWidget { Q_OBJECT public: Teapots(QWidget *parent = 0); ~Teapots(); protected: void initializeGL(); void resizeGL(int width, int height); void paintGL(); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); private: void createGLTeapotObject(); void drawTeapot(GLfloat x, GLfloat y, GLfloat ambientR, GLfloat ambientG, GLfloat ambientB, GLfloat diffuseR, GLfloat diffuseG, GLfloat diffuseB, GLfloat specularR, GLfloat specularG, GLfloat specularB, GLfloat shininess); void drawTeapots(); QGLFramebufferObject *fbObject; GLuint glTeapotObject; QPoint rubberBandCorner1; QPoint rubberBandCorner2; bool rubberBandIsShown; };

Teapots类派生自QGLWidget,实现了OpenGL的高级处理函数initializeGL()、resizeGL()和paintGL()。同时,它还实现了mousePressEvent( )、mouseMoveEvent()和mouseReleaseEvent(),以便可以让用户绘制一个橡皮筋选择框。

私有函数负责创建茶壶对象以及茶壶的绘制。这些私有变量可以保存framebuffer对象、茶壶对象、橡皮筋选择框的各个角,以及橡皮筋选择框的可见属性。

Teapots类的实现

首先是构造函数:

Teapots::Teapots(QWidget *parent) : QGLWidget(parent) { rubberBandIsShown = false; makeCurrent(); fbObject = new QGLFramebufferObject(1024, 1024, QGLFramebufferObject::Depth); createGLTeapotObject(); }

Teapots构造函数初始化私有变量,创建framebuffer对象,并创建茶壶对象。我们将跳过createGLTeapotObject()函数,因为它很长而且并不包含与Qt相关的代码。

Teapots::~Teapots() { makeCurrent(); delete fbObject; glDeleteLists(glTeapotObject, 1); }

在析构函数中,释放与framebuffer对象和茶壶相关的资源。

void Teapots::initializeGL() { static const GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 }; static const GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; static const GLfloat position[] = { 0.0, 3.0, 3.0, 0.0 }; static const GLfloat lmodelAmbient[] = { 0.2, 0.2, 0.2, 1.0 }; static const GLfloat localView[] = { 0.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_POSITION, position); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodelAmbient); glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, localView); glFrontFace(GL_CW); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); }

initializeGL()函数用来设置光照模式,打开各种OpenGL特性。

void Teapots::resizeGL(int width, int height) { fbObject->bind(); glDisable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (width <= height) { glOrtho(0.0, 20.0, 0.0, 20.0 * GLfloat(height) / GLfloat(width),-10.0, 10.0); } else { glOrtho(0.0, 20.0 * GLfloat(width) / GLfloat(height), 0.0, 20.0,-10.0, 10.0); } glMatrixMode(GL_MODELVIEW); drawTeapots(); fbObject->release(); }

resizeGL()函数在Teapot窗口部件改变大小时重绘茶壶场景。为了在framebuffer对象上绘制荼壶,在函数的开头调用QGLFramebufferObject::bind()。然后,设置一些OpenGL特性,以及投影和模式视图矩阵。结尾的drawTeapots()调用在framebuffer对象上绘制茶壶。最后,release()调用释放framebuffer对象,确保随后的OpenGL绘制操作不会发生在framebuffer对象上。

void Teapots::paintGL() { glDisable(GL_LIGHTING); glViewport(0, 0, width(), height()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fbObject->texture()); glColor3f(1.0, 1.0, 1.0); GLfloat s = width() / GLfloat(fbObject->size().width()); GLfloat t = height() / GLfloat(fbObject->size().height()); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0); glTexCoord2f(s, 0.0); glVertex2f(1.0, -1.0); glTexCoord2f(s, t); glVertex2f(1.0, 1.0); glTexCoord2f(0.0, t); glVertex2f(-1.0, 1.0); glEnd(); if (rubberBandIsShown) { glMatrixMode(GL_PROJECTION); glOrtho(0, width(), height(), 0, 0, 100); glMatrixMode(GL_MODELVIEW); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(4.0); glColor4f(1.0, 1.0, 1.0, 0.2); glRecti(rubberBandCorner1.x(), rubberBandCorner1.y(), rubberBandCorner2.x(), rubberBandCorner2.y()); glColor4f(1.0, 1.0, 0.0, 0.5); glLineStipple(3, 0xAAAA); glEnable(GL_LINE_STIPPLE); glBegin(GL_LINE_LOOP); glVertex2i(rubberBandCorner1.x(), rubberBandCorner1.y()); glVertex2i(rubberBandCorner2.x(), rubberBandCorner1.y()); glVertex2i(rubberBandCorner2.x(), rubberBandCorner2.y()); glVertex2i(rubberBandCorner1.x(), rubberBandCorner2.y()); glEnd(); glLineWidth(1.0); glDisable(GL_LINE_STIPPLE); glDisable(GL_BLEND); } }

在paintGL()函数中,首先重新设置投影和模式视图矩阵。然后,把framebuffer对象绑定到纹理上,绘制一个带该纹理的矩形覆盖整个窗口部件。如果橡皮筋选择框是可见的,我们可以把它绘制在矩形上。代码是标准的openGL。

void Teapots::mousePressEvent(QMouseEvent *event) { rubberBandCorner1 = event->pos(); rubberBandCorner2 = event->pos(); rubberBandIsShown = true; } void Teapots::mouseMoveEvent(QMouseEvent *event) { if (rubberBandIsShown) { rubberBandCorner2 = event->pos(); updateGL(); } } void Teapots::mouseReleaseEvent(QMouseEvent * /* event */) { if (rubberBandIsShown) { rubberBandIsShown = false; updateGL(); } }

鼠标事件处理器负责更新用来绘制橡皮筋线的rubberBandCorner1,rubberBandCorner2和rubberBandIsShown变量,并且调用updateGL()重绘场景。重绘场景的速度很快,因为paintGL()仅绘制一个纹理矩阵以及其上的橡皮筋线。只有当用户改变窗口大小时,场景才在resizeGL()中重新绘制。

主程序代码

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

int main(int argc, char *argv[]) { QApplication app(argc, argv); if (!QGLFormat::hasOpenGL()) { std::cerr << "This system has no OpenGL support" << std::endl; return 1; } if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) { std::cerr << "This system has no framebuffer object support" << std::endl; return 1; } Teapots teapots; teapots.setWindowTitle(QObject::tr("Teapots")); teapots.resize(400, 400); teapots.show(); return app.exec(); }

如果系统不支持openGL或者framebuffer对象,将会发送一条错误信息,并且返回该错误代码,结束运行。

Teapots例子告诉我们如何在离屏表面绑定纹理,以及如何使用命令在该表面上绘制图形。但是我们还可以在其他方面进行有益的尝试。例如,可以使QPainter代替OpenGL命令在QGLFramebufferObject或QGLPixelBuffer上绘制。这为在OpenGL场景中绘制变形字提供了一种方法。另一种常用的做法是使用framebuffer对象绘制场景,然后对其结果调用toImage()生成QImage。Qt自带的很多例子都展示了在实际应用中的多种做法,它们都是针对framebuffer对象和pbuffers的。

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

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

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

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