Qt5 QOpenGLWidget类

来源:互联网 发布:2017网络流行词语大全 编辑:程序博客网 时间:2024/05/23 19:14

QOpenGLWidget 类

  • QOpenGLWidget 类
      • public函数
      • 信号
      • protected函数
      • Reimplemented Protected 函数
      • 附加成员
      • 详细描述
      • 绘制技巧Painting Techniques
      • OpenGL函数调用头文件和QOpenGL函数
      • 与QGLWidget的关系
      • 与QGLWidget的区别
      • 多重采样
      • 线程
      • 上下文共享
      • 资源初始化和清理

QOpenGLWidget类是用于渲染OpenGL图形的小部件.

#include <QOpenGLWidget>
pro文件: QT += widgets
开始于: Qt 5.4
继承于: QWidget

enum UpdateBehavior { NoPartialUpdate, PartialUpdate }

public函数

QOpenGLWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())~QOpenGLWidget()QOpenGLContext *context() constGLuint defaultFramebufferObject() constvoid doneCurrent()QSurfaceFormat format() constQImage grabFramebuffer()bool isValid() constvoid makeCurrent()void setFormat(const QSurfaceFormat &format)void setUpdateBehavior(UpdateBehavior updateBehavior)UpdateBehavior updateBehavior() const

211 个公有函数继承于 QWidget
31 个公有函数继承于 QObject
14 个公有函数继承于 QPaintDevice

信号

void aboutToCompose()void aboutToResize()void frameSwapped()void resized()

3 个信号函数继承于 QWidget
2 个信号函数继承于 QObject

protected函数

virtual void initializeGL()virtual void paintGL()virtual void resizeGL(int w, int h)

Reimplemented Protected 函数

virtual void paintEvent(QPaintEvent *e)virtual void resizeEvent(QResizeEvent *e)

35 个protected 函数继承于 QWidget
9 个protected 函数继承于 QObject
1 个protected 函数继承于 QPaintDevice

附加成员

58 个属性继承于QWidget
1 个属性继承于 QObject
19 个公有槽函数继承于 QWidget
1 个公有槽函数继承于 QObject
1 个公有变量继承于 QObject
5 个公有静态成员继承于 QWidget
10 个公有静态成员继承于 QObject
1 个protected槽函数继承于 QWidget
2 个protected槽函数继承于 QWidget
1 个protected类型继承于 QPaintDevice

详细描述

QOpenGLWidget类是用于渲染OpenGL图形的小部件。

QOpenGLWidget提供了显示集成在Qt应用程序中的OpenGL图形的功能。使用起来非常简单:将你的类继承于QOpenGLWidget,然后可以像使用任何QWidget一样使用这个子类。 并且,您可以选择使用QPainter或者标准OpenGL来进行图形渲染。

QOpenGLWidget提供了三个方便的virtual函数,您可以在子类中重新实现以执行典型的OpenGL任务:

paintGL() - 渲染OpenGL场景。每当需要更新窗口小部件时就会调用。
resizeGL() - 设置OpenGL视口,投影等。每当窗口小部件被调整大小时,都会调用(也是第一次显示时,因为所有新创建的窗口小部件都会自动获取调整大小的事件)。
initializeGL()  - 设置OpenGL资源和状态。在第一次调用resizeGL()paintGL()之前调用一次。

如果您需要在paintGL()之外的其他地方触发重绘(典型的例子是使用定时器来驱动场景)时,您应该调用窗口小部件的update()函数来计划更新。

当调用paintG(),resizeGL()或initializeGL()时,您的窗口小部件的OpenGL渲染上下文将变为最新状态。如果您需要从其他地方调用标准的OpenGL API函数(例如,在窗口小部件的构造函数或自己的绘图函数中),则必须首先调用makeCurrent()。

所有渲染发生在一个OpenGL帧缓冲区对象中。 makeCurrent()确保它在上下文中被绑定。请记住,当在paintGL()中的渲染代码中创建和绑定附加的帧缓冲对象时,不要使用ID 0重新绑定帧缓冲区。相反,调用defaultFramebufferObject()来获取应该绑定的ID。

QOpenGLWidget允许在平台支持时使用不同的OpenGL版本和配置文件。只需通过setFormat()设置所请求的格式。但请记住,在同一个窗口中无论在同一个窗口中有多少个QOpenGLWidget实例,要求它们都使用相同的格式,或至少不会使上下文成为不可共享的格式。为了克服这个问题,更喜欢使用QSurfaceFormat :: setDefaultFormat()而不是setFormat()。

注意:在需要OpenGL核心配置文件上下文(context)时,在某些平台(例如,macOS)上,在构建QApplication实例之前,调用QSurfaceFormat :: setDefaultFormat()上是必需的。这是为了确保上下文之间的资源共享保持功能,因为使用正确的版本和配置文件创建所有内部上下文。

绘制技巧(Painting Techniques)

如上所述,子类QOpenGLWidget以以下方式呈现纯3D内容:

  • 重新实现initializeGL()和resizeGL()函数来设置OpenGL状态并提供透视变换。

  • 重新执行paintGL()来绘制3D场景,仅调用OpenGL函数。

也可以使用QPainter将2D图形绘制到QOpenGLWidget子类上:

  • 在paintGL()中,而不是发出OpenGL命令,构造一个QPainter对象以在widget上使用。
  • 使用QPainter的成员函数绘制原语。
  • 仍然可以直接发出OpenGL命令。但是,您必须确保这些都是通过调用画笔的beginNativePainting()和endNativePainting()来包含的。

当仅使用QPainter进行绘制时,也可以像普通小部件一样进行绘画:通过重新实现paintEvent()。

  • 重新实现paintEvent()函数。
  • 构造一个针对小部件的QPainter对象。或者将小部件传递给构造函数或QPainter :: begin()函数。
  • 使用QPainter的成员函数绘制原语。
  • 绘画完成,然后QPainter实例被销毁,或者,显式调用QPainter:: end()。

OpenGL函数调用,头文件和QOpenGL函数

在进行OpenGL函数调用时,强烈建议避免直接调用函数。 相反,更偏爱使用QOpenGLFunctions(当制作便携式应用程序)或版本化的变体(例如,当定位现代的仅桌面版OpenGL时,QOpenGLFunctions_3_2_Core和类似的)。 这样,应用程序将在所有Qt构建配置中正常工作,包括执行动态OpenGL实现加载的应用程序,这意味着应用程序不直接链接到GL实现,因此直接调用函数是不可行的。

在paintGL()中,当前上下文可以通过调用QOpenGLContext :: currentContext()来访问。从这个上下文中,通过调用QOpenGLContext :: functions()来检索已经初始化的、并可以使用的QOpenGLFunctions实例。每个GL调用前缀的替代方法是继承QOpenGLFunctions,并在initializeGL()中调用QOpenGLFunctions :: initializeOpenGLFunctions()。

对于OpenGL头文件,请注意,在大多数情况下,不需要直接包含任何头文件,如GL.h。 OpenGL相关的Qt头文件将包括于qopengl.h,它将依次包括系统的适当标题。这可能是OpenGL ES 3.x或2.0头(文件),可用的最高版本或系统提供的gl.h.另外,为OpenGL和OpenGL ES提供了扩展头(在某些系统上称为glext.h)的副本作为Qt的一部分。这些将在可行的情况下自动包含在平台上。这意味着来自ARB,EXT,OES扩展的常量和函数指针typedef自动可用。

与QGLWidget的关系

遗留的QtOpenGL模块(以QGL为前缀的类)提供了一个名为QGLWidget的小部件。QOpenGLWidget旨在成为现代的替代品。因此,特别是在新的应用程序中,一般的建议是使用QOpenGLWidget。

虽然API非常相似,但两者之间存在重大差异:QOpenGLWidget总是使用framebuffer对象来渲染屏幕外。另一方面,QGLWidget使用本机窗口和表面。后者在复杂用户界面中使用时会导致问题,因为根据平台,这些本机子窗口小部件可能会有各种限制,例如关于堆栈顺序。 QOpenGLWidget通过不创建单独的本机窗口来避免这种情况。

由于受到framebuffer对象的支持,当更新行为设置为PartialUpdateBlit或PartialUpdateBlend时,QOpenGLWidget的行为非常类似于QOpenGLWindow。这意味着内容在paintGL()调用之间保留,以便增量渲染是可能的。使用QGLWidget(和自然的QOpenGLWindow具有默认的更新行为)通常不是这种情况,因为交换缓冲区会留下具有未定义内容的后台缓冲区。

注意:大多数应用程序不需要增量渲染,因为它们会在每个绘图调用的视图中呈现所有内容。在这种情况下,重要的是在paintGL()中尽可能早地调用glClear()。这有助于使用基于瓦片的架构的移动GPU识别瓦片缓冲区,而不需要重新加载帧缓冲区的先前内容。省略明确的通话可能会导致这种系统的性能显着下降。

注意:避免在QOpenGLWidget上调用winId()。此功能可触发本机窗口的创建,从而降低性能并可能导致故障。

与QGLWidget的区别

除了由framebuffer对象支持的主要概念差异外,QOpenGLWidget和较旧的QGLWidget之间存在一些较小的内部差异:

  • 调用paintGL()时的OpenGL状态。 QOpenGLWidget通过glViewport()设置视口。 它不执行任何清算。

  • 通过QPainter开始绘画时清除。 与常规小部件不同,QGLWidget默认为autoFillBackground的值为true。 然后每次使用QPainter :: begin()时,执行清除调色板的背景颜色。 QOpenGLWidget不会这样:autoFillBackground默认为false,就像任何其他小部件一样。 唯一的例外是用作其他小部件(如QGraphicsView)的视口。 在这种情况下,autoFillBackground将自动设置为true,以确保与基于QGLWidget的视口兼容。

多重采样

要启用多重采样,请在传递给setFormat()的QSurfaceFormat上设置请求的样本数。 在不支持它的系统上,请求可能会被忽略。

多重采样支持需要支持多采样的renderbuffer和帧缓冲区blits。 在OpenGL ES 2.0实现中,这些将不会存在。 这意味着多次采样将不可用。 使用现代OpenGL版本和OpenGL ES 3.0,这通常不是一个问题。

线程

通过暴露窗口小部件的QOpenGLContext,可以在每个线程上创建与之共享的其他上下文,从而支持在工作线程上执行屏幕外渲染,例如生成在paintGL()中的GUI /主线程中使用的结构(textures)。

直接绘制到GUI /主线程外的QOpenGLWidget的framebuffer可以通过重新执行paintEvent()来执行任何操作。上下文的线程关系必须通过QObject :: moveToThread()进行更改。之后,makeCurrent()和doneCurrent()在工作线程上可用。稍后将上下文移回GUI /主线程。

与QGLWidget不同,触发仅用于QOpenGLWidget的缓冲区交换是不可能的,因为它没有真正的屏幕上的本机表面。相反,由widget组件管理gui线程上的组​​合和缓冲区交换。当线程完成更新帧缓冲区时,调用GUI /主线程上的update()来调度组合。

当GUI /主线程执行合成时,必须特别注意避免使用帧缓冲区。当组合开始和结束时,将发出aboutToCompose()和frameSwapped()的信号。它们在GUI /主线程上发出。这意味着通过使用直接连接aboutToCompose()可以阻止GUI /主线程,直到工作线程完成其渲染。之后,工作线程必须不再进行渲染,直到发送frameSwapped()信号为止。如果这是不可接受的,工作线程必须实现双缓冲机制。这涉及使用由线程完全控制的替代渲染目标进行绘制,例如。一个额外的帧缓冲区对象,并在适当的时间对QOpenGLWidget的帧缓冲区进行blitting。

上下文共享

当多个QOpenGLWidgets作为子代添加到同一顶级小部件时,它们的上下文将彼此共享。这不适用于属于不同窗口的QOpenGLWidget实例。

这意味着同一窗口中的所有QOpenGLWidget可以访问彼此的共享资源,如纹理(textures),并且不需要额外的“全局共享”上下文,就像QGLWidget一样。

要设置属于不同窗口的QOpenGLWidget实例之间的共享,请在实例化QApplication之前设置Qt :: AA_ShareOpenGLContexts应用程序属性。这将触发所有QOpenGLWidget实例之间的共享,无需任何进一步的步骤。

创建额外的QOpenGLContext实例,可以与QOpenGLWidget的上下文共享资源(如纹理textures)。在调用QOpenGLContext :: create()之前,将从context()返回的指针传递给QOpenGLContext :: setShareContext()。所得到的上下文也可以在不同的线程上使用,允许线程生成纹理(textures)和异步纹理(textures)上传。

请注意,当涉及到底层图形驱动程序时,QOpenGLWidget需要符合标准的资源共享实现。例如,一些驱动程序,特别是用于移动和嵌入式硬件的驱动程序,在设置现有上下文与稍后创建的其他内容之间的共享方面存在问题。当尝试在不同线程之间使用共享资源时,其他一些驱动程序可能会出现意外的方式。

资源初始化和清理

当调用initializeGL()和paintGL()时,要保证QOpenGLWidget的相关OpenGL上下文是当前的。在调用initializeGL()之前,不要尝试创建OpenGL资源。例如,在子类的构造函数中完成时,尝试编译着色器,初始化顶点缓冲区对象或上传纹理数据将失败。这些操作必须推迟到initializeGL()。 Qt的一些OpenGL助手类,如QOpenGLBuffer或QOpenGLVertexArrayObject,具有相似的延迟行为:它们可以在没有上下文的情况下进行实例化,但所有初始化都将推迟到create()或类似调用。这意味着它们可以用作QOpenGLWidget子类中的普通(非指针)成员变量,但create()或类似函数只能从initializeGL()调用。请注意,并不是所有的类都是这样设计的。当有疑问时,将成员变量作为指针,分别在initializeGL()和析构函数中动态创建和破坏实例。

释放资源也需要上下文为当前。因此,执行这种清理的析构函数预计会调用makeCurrent(),然后继续去破坏任何OpenGL资源或包装器。避免通过deleteLater()或QObject的父母机制进行延迟删除。当事实真的被破坏时,不能保证正确的上下文是现在的。

原创粉丝点击