QOpenGLWidget类讲解

Detailed Description

QOpenGLWidget类是用于渲染OpenGL图形。

除了可以选择使用QPainter和标准的OpenGL渲染图形,QOpenGLWidget类提供了在Qt应用程序中显示OpenGL图形的功能。它使用起来非常简单:新建类继承于QOpenGLWidget,使用方法就像继承于QWidget类子类一样。

QOpenGLWidget类提供了三个方便的虚函数,可以在新建的子类中重新实现以完成OpenGL的任务:

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

如果需要从paintGL()以外的地方触发重绘(一个典型的例子是使用定时器为场景设置动画),应该调用widget的update()函数来进行更新。

当调用paintGL(),resizeGL()或initializeGL()时,Widget的OpenGL渲染环境需要设为当前。如果需要从其他位置调用标准OpenGL API函数(例如,Widget的构造函数或自己的绘图函数中),则必须首先调用makeCurrent()。

所有渲染都发生在OpenGL帧缓冲对象中,makeCurrent()确保它在渲染环境中,在paintGL()中的渲染代码中创建和绑定其他帧缓冲对象时,不要使用ID 0重新绑定帧缓冲区,而是调用defaultFramebufferObject()来获取应该绑定的ID。

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

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

OpenGL Function Calls, Headers and QOpenGLFunctions

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

在paintGL()中,当前场景(context)始终可以通过调用QOpenGLContext :: currentContext()来访问。从这个context中,可以通过调用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的Qt的一部分,提供了扩展头的副本(在某些系统上称为glext.h)。这些将在可行的情况下自动包含在平台上。这意味着来自ARB,EXT,OES扩展的常量和函数指针typedef自动可用。

Code Examples

最简单的例子

 class MyGLWidget : public QOpenGLWidget
  {
  public:
      MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }

  protected:
      void initializeGL()
      {
          // Set up the rendering context, load shaders and other resources, etc.:
          QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
          f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
          ...
      }

      void resizeGL(int w, int h)
      {
          // Update projection matrix and other size related settings:
          m_projection.setToIdentity();
          m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
          ...
      }

      void paintGL()
      {
          // Draw the scene:
          QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
          f->glClear(GL_COLOR_BUFFER_BIT);
          ...
      }

  };

或通过使用QOpenGLFunction来代替OpenGL函数

 class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
  {
      ...
      void initializeGL()
      {
          initializeOpenGLFunctions();
          glClearColor(...);
          ...
      }
      ...
  };

要获得与给定OpenGL版本或配置文件兼容的context,或者要求深度和模板缓冲区,调用setFormat():

  QOpenGLWidget *widget = new QOpenGLWidget(parent);
  QSurfaceFormat format;
  format.setDepthBufferSize(24);
  format.setStencilBufferSize(8);
  format.setVersion(3, 2);
  format.setProfile(QSurfaceFormat::CoreProfile);
  widget->setFormat(format); // must be called before the widget or its parent window gets shown

使用OpenGL 3.0+ context时,当可移植性不重要时,版本化的QOpenGLFunctions变体可以轻松访问给定版本中可用的所有现代的OpenGL函数:

      void paintGL()
      {
          QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
          ...
          f->glDrawArraysInstanced(...);
          ...
      }

如上所述,全局设置所要求的格式以使其在应用程序的生命周期内应用于所有窗口和context更简单且更鲁棒。 以下是此示例:

  int main(int argc, char **argv)
  {
      QApplication app(argc, argv);

      QSurfaceFormat format;
      format.setDepthBufferSize(24);
      format.setStencilBufferSize(8);
      format.setVersion(3, 2);
      format.setProfile(QSurfaceFormat::CoreProfile);
      QSurfaceFormat::setDefaultFormat(format);

      MyWidget widget;
      widget.show();

      return app.exec();
  }

Relation to QGLWidget

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

虽然API非常相似,但两者之间存在重要差异:QOpenGLWidget始终使用帧缓冲对象在屏幕外渲染。另一方面,QGLWidget使用原生窗口和曲面。后者在复杂的用户界面中使用它时会引起问题,因为根据平台,这种本机子窗口小部件可能具有各种限制,例如关于堆叠命令。QOpenGLWidget通过不创建单独的本机窗口来避免这种情况。

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

注意:大多数应用程序不需要增量渲染,因为它们将在每次绘制调用时呈现视图中的所有内容。在这种情况下,在paintGL()中尽早调用glClear()非常重要。这有助于使用基于图块的体系结构的移动GPU识别出图块缓冲区不需要使用帧缓冲区的先前内容重新加载。省略明确的呼叫可能导致此类系统的性能显着下降。

注意:避免在QOpenGLWidget上调用winId()。此功能触发创建本机窗口,从而降低性能并可能出现毛刺。

Differences to QGLWidget

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

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

没有更多推荐了,返回首页