QOpenGLWidget描述

QOpenGLWidget类是用于渲染OpenGL图形。

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

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

paintGL()—渲染OpenGL场景,需要更新Widget时就会调用。
resizeGL()—设置OpenGL视口,投影等。每当调整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函数, 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自动可用。

最简单的例子

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();
format.setStencilBufferSize();
format.setVersion(, );
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();
format.setStencilBufferSize();
format.setVersion(, );
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format); MyWidget widget;
widget.show(); return app.exec();
}

关于老版QGLWidget

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

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

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

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

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

QOpenGLWidget与QGLWidget的区别

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

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

QOpenGLWidget的更多相关文章

  1. Qt自定义阴影效果和QOpenGLWidget冲突导致控件不刷新

    Qt5.6.2版本存在这样一个问题(其它版本未测试),当main函数中设置了application.setAttribute(Qt::AA_NativeWindows)(用于使得每个子界面都可以获取w ...

  2. QOpenglWidget 与QGLWidget的选择

    1. QGLWidget 是Qt OpenGL模块,但是从其官方说明,推荐在Qt5.4 之后,使用QOpenglWidget版本,具体说明如下: Note: This class is part of ...

  3. 【Qt】QOpenGLWidget展示蒙版效果

    关键代码是派生QOpenGLWidget,覆写paintEvent函数 QPainter p; p.begin(this); p.drawImage(QPoint(, ), m_Img); QLine ...

  4. Visual Studio 2017 无法打开包括文件: “QOpenGLWidget”: No such file or directory

    编译项目时,发现报错:VS 无法打开包括文件: “QOpenGLWidget”: No such file or directory,在Qt对应的目录(E:\Qt\Qt5.12.2\5.12.2\ms ...

  5. 在qt的QOpenGLWidget开启opengl的抗锯齿

    在QOpenGLWidget的构造函数添加下面几句代码即可 QSurfaceFormat surfaceFormat; surfaceFormat.setSamples();//多重采样 setFor ...

  6. 2.通过QOpenGLWidget绘制三角形

    参考:1.opengl绘制三角形 1.QOpenGLWidget的早先版本 QGLWidget是遗留Qt OpenGL模块的一部分,和其他QGL类一样,应该在新的应用程序中避免使用.相反,从Qt 5. ...

  7. Cross-compiling Qt Embedded 5.5 for Raspberry Pi 2

    This tutorial shows how to cross-compile the Embedded build of Qt 5.5 for Raspberry Pi 2. The Embedd ...

  8. 【Qt for Android】OpenGL ES 绘制彩色立方体

    Qt 内置对OpenGL ES的支持.选用Qt进行OpenGL ES的开发是很方便的,很多辅助类都已经具备.从Qt 5.0開始添加了一个QWindow类,该类既能够使用OpenGL绘制3D图形,也能够 ...

  9. Qt与FFmpeg联合开发指南(二)——解码(2):封装和界面设计

    与解码相关的主要代码在上一篇博客中已经做了介绍,本篇我们会先讨论一下如何控制解码速度再提供一个我个人的封装思路.最后回归到界面设计环节重点看一下如何保证播放器界面在缩放和拖动的过程中保证视频画面的宽高 ...

随机推荐

  1. Java 基础篇之lambda

    Lambda 示例 public interface Eatable { void taste(); } public interface Flyable { void fly(String weat ...

  2. 整合AD RMS与EX 2010。

    1.点击开始菜单, 选择所有程 序,展开 Mi cros oft  Excha nge  Server  2010 ,打开Excha nge Ma na gement Cons ol e,选择收件人配 ...

  3. 使用Docker Maven 插件进行镜像的创建以及上传至私服

    1.在进行服务容器化部署的时候,需要将服务以及其运行的环境整个打包做成一个镜像,打包的过程有两种办法,第一种是首选通过maven打成jar包,然后再编写dockerfile,执行docker buil ...

  4. 南昌网络赛C.Angry FFF Party

    南昌网络赛C.Angry FFF Party Describe In ACM labs, there are only few members who have girlfriends. And th ...

  5. PHP会话(Session)实现用户登陆功能

    对比起 Cookie,Session 是存储在服务器端的会话,相对安全,并且不像 Cookie 那样有存储长度限制,本文简单介绍 Session 的使用. 由于 Session 是以文本文件形式存储在 ...

  6. spark算子篇-repartition and coalesce

    我们知道 RDD 是分区的,但有时候我们需要重新设置分区数量,增大还是减少需要结合实际场景,还有可以通过设置 RDD 分区数来指定生成的文件的数量 重新分区有两种方法:repartition and ...

  7. Java EE javax.servlet中的RequestDispatcher接口

    RequestDispatcher接口 public interface RequestDispatcher 一.介绍 定义一个对象,从客户端接收请求并将其发送到服务器上的任何资源(例如servlet ...

  8. 进阶Java编程(2)线程常用操作方法

    线程常用操作方法 多线程的主要操作方法都在Thread类中定义的. 1,线程的命名和取得 多线程的运行状态是不确定的,那么在程序的开发之中为了可以获取到一些需要使用到的线程就只能依靠线程的名字来进行操 ...

  9. Oracle数据库中的变量

    Oracle数据库中的变量 来源:https://blog.csdn.net/wahaa591/article/details/46772769 1.define(即host变量) define va ...

  10. 养成一个SQL好习惯

    要知道sql语句,我想我们有必要知道sqlserver查询分析器怎么执行我么sql语句的,我么很多人会看执行计划,或者用profile来监视和调优查询语句或者存储过程慢的原因,但是如果我们知道查询分析 ...