Qt开发笔迹:QGLWidget、QOpenGLWidget详解及区别


QGLWidget

OpenGL图形的小部件。

       QGLWidget提供了显示集成到Qt应用程序中的OpenGL图形的功能。它很容易使用。继承它并使用子类,就像其他任何QWidget一样,额外的可以选择使用QPainter和标准OpenGL渲染命令。

       注意:这个类是传统QtOpenGL模块的一部分,与其他QGL类一样,应该在新的应用程序中避免使用。相反,从Qt5.4开始,Qt推荐使用QOpenGLWidget和QOpenGL类。

       QGLWidget提供了三个方便的虚拟函数,可以在子类中重写这些函数来执行典型的OpenGL任务:

  • paintGL()渲染OpenGL场景。每当需要更新小部件时调用。
  • resizeGL ()设置OpenGL视区、投影等。每当小部件调整了大小时都会调用该视区(并且当它第一次显示时也会调用,因为所有新创建的小部件都会自动获得一个调整大小的事件)。
  • initializeGL()设置OpenGL呈现上下文,定义显示列表等。在第一次调用resizeGL ()或paintGL ()之前调用一次。

基类实现。如果要渲染动画,则可能根本不需要处理绘制事件,因为渲染线程正在进行定期更新。然后,只需重新实现QGLWidget::paintEvent()就可以了。

       在进行线程渲染时,一般规则是:请注意,不同线程中的绑定和释放上下文必须由用户同步。GL呈现上下文在任何时候都只能在一个线程中是最新的。如果您试图在QGLwidget上打开一个QPainer,而该Widget的呈现上下文在另一个线程中是最新的,那么它将失败。

       除此之外,还支持在单独的线程中使用原始GL调用进行渲染。

 

QOpenGLWidget

概述

       QOpenGLWidget类是用于呈现OpenGL图形的小部件。

       QOpenGLWidget提供显示集成到Qt应用程序中的OpenGL图形的功能。使用起来非常简单:让类继承它,并像其他QWidget一样使用子类,额外可以选择使用QPainer和标准的OpenGL渲染命令。

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

  • paintGL()渲染OpenGL场景。每当需要更新小部件时调用。
  • resizeGL ()设置OpenGL视区、投影等。每当小部件调整了大小时都会调用该视区(并且当它第一次显示时也会调用,因为所有新创建的小部件都会自动获得一个调整大小的事件)。
  • initializeGL()设置OpenGL呈现上下文,定义显示列表等。在第一次调用resizeGL ()或paintGL ()之前调用一次。

更新绘制

       如果需要从paintGL()以外的地方触发重新绘制(典型的例子是使用计时器来动画场景),您应该调用小部件的update()函数来安排更新。

       当调用paintGL()、resizeGL()或initializeGL()时,小部件的OpenGL呈现上下文成为当前上下文。如果需要从其他地方调用标准的OpenGL API函数(例如,在小部件的构造函数或自己的paint函数中),则必须首先调用makeCurrent()。

       所有渲染都发生在OpenGL帧缓冲区对象中。makeCurrent()确保它在上下文中绑定。在paintGL()中的呈现代码中创建和绑定其他framebuffer对象时,请记住这一点。永远不要重新绑定ID为0的帧缓冲区。相反,调用defaultFrameBufferObject()获取应该绑定的ID。

       当平台支持时,QOpenGLWidget允许使用不同的OpenGL版本和配置文件。只需通过setFormat()设置请求的格式。但是请记住,在同一窗口中拥有多个QOpenGLWidget实例需要它们都使用相同的格式,或者至少使用不使上下文不可共享的格式。要解决此问题,最好使用QSurfaceFormat::setDefaultFormat()而不是setFormat()。

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

绘制技术

子类QOpenGLWidget以以下方式呈现纯3D内容:

  • 重新实现QGLWidget::initializeGL()QGLWidget::resizeGL(),以设置OpenGL状态并提供从始至终的转换。
  • 重新实现QGLWidget::paintGL()以绘制3D场景,只调用OpenGL函数在小部件上绘制。

若要在QGLWidget子类上绘制二维图形,需要重新实现QGLWidget:: paintEvent()并执行以下操作:

  • 在paintGL()中,不要发出OpenGL命令,而是构造一个用于小部件的QPainter对象。
  • 使用QPainter的成员函数绘制基元。
  • 仍然可以发出直接的OpenGL命令。但是,必须确保通过调用画师的beginNativePainting()endNativePainting()将这些内容括起来。

仅使用QPainter执行绘图时,也可以像对普通小部件执行一样执行绘图:通过重新实现paintEvent()

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

调用OpenGL头文件和函数

       在进行OpenGL函数调用时,强烈建议避免直接调用函数。相反,在面向现代、仅桌面的OpenGL时,更喜欢使用QOpenglFunctions(在制作可移植应用程序时)或版本化的变体(例如,QopenglFunctions_3_2_Core和类似的变体)。这样,应用程序将在所有qt构建配置中正常工作,包括执行动态OpenGL实现加载的配置,这意味着应用程序没有直接链接到GL实现,因此直接函数调用是不可行的。

       在paintGL()中,始终可以通过调用QOpenGLContext::currentContext()访问当前上下文。通过调用QOpenGLContex::Functions(),可以从此上下文中检索已初始化、准备好使用的QOpenGLFunctions实例。为每个GL调用添加前缀的另一种方法是从QOpenGLFunctions继承并在InitializeGL()中调用QOpenGLFunctions::nitializeOpenGLFunctions()。

       至于OpenGL头文件,请注意,在大多数情况下,不需要直接包含任何头文件,如gl.h。OpenGL相关的qt报头将包括qopengl.h,而qopengl.h又将包含适合系统的报头。这可能是OpenGL ES 3.x或2.0头文件,可用的最高版本,或者是系统提供的gl.h。此外,还为OpenGL和OpenGL ES提供了扩展头文件(在某些系统上称为glext.h)的副本,作为qt的一部分。在可行的情况下,这些将自动包含在平台上。这意味着来自arb、ext、oes扩展的常量和函数指针typedef将自动可用。

代码示例

      最简单的QOpenGLWidget子类可以如下所示:

      image

相关