QOpenGLWidget 类

QOpenGLWidget 类是一个用于渲染 OpenGL 图形的窗口部件。 更多...

头部 #include <QOpenGLWidget>
CMakefind_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets)
target_link_libraries(mytarget PRIVATE Qt6::OpenGLWidgets)
qmakeQT += openglwidgets
继承 QWidget

公共类型

(since 6.5) 枚举TargetBuffer { LeftBuffer, RightBuffer }
枚举UpdateBehavior { NoPartialUpdate, PartialUpdate }

公共函数

QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
虚拟~QOpenGLWidget()
QOpenGLContext *context() const
(since 6.5) QOpenGLWidget::TargetBuffercurrentTargetBuffer() const
GLuintdefaultFramebufferObject() const
(since 6.5) GLuintdefaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const
voiddoneCurrent()
QSurfaceFormatformat() const
QImagegrabFramebuffer()
(since 6.5) QImagegrabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
boolisValid() const
voidmakeCurrent()
(since 6.5) voidmakeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)
voidsetFormat(const QSurfaceFormat &format)
voidsetTextureFormat(GLenum texFormat)
voidsetUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
GLenumtextureFormat() const
QOpenGLWidget::UpdateBehaviorupdateBehavior() const

信号

受保护函数

虚拟 voidinitializeGL()
虚拟 voidpaintGL()
虚拟 voidresizeGL(int w, int h)

重写受保护函数

虚拟布尔值事件(QEvent *e) 覆盖
虚拟整数度量(QPaintDevice::PaintDeviceMetric metric) const 覆盖
虚拟 QPaintEngine *绘制引擎() const 覆盖
虚拟 void绘制事件(QPaintEvent *e) 覆盖
虚拟 QPaintDevice *重定向(QPoint *p) const 覆盖
虚拟 void大小调整事件(QResizeEvent *e) 覆盖

详细描述

QOpenGLWidget 提供了将 OpenGL 图形集成到 Qt 应用中的功能。使用非常简单:让你的类继承自它,并像使用其他任何 QWidget 一样使用子类,只不过你可以选择使用 QPainter 或标准的 OpenGL 绘制命令。

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

  • paintGL() - 绘制 OpenGL 场景。每次小部件需要更新时都会调用。
  • resizeGL() - 设置 OpenGL 视口、投影等。每次小部件被调整大小(并在第一次显示时,因为所有新创建的小部件都会自动收到一个调整大小事件)时都会调用。
  • initializeGL() - 设置 OpenGL 资源和状态。在首次调用 resizeGL() 或 paintGL() 之前调用一次。

如果您需要从非 paintGL() 的地方触发重绘(一个典型的例子是在使用 定时器 进行场景动画时),您应调用小部件的 update() 函数以安排更新。

当调用 paintGL()、resizeGL() 或 initializeGL() 时,您的小部件的 OpenGL 渲染上下文将变为当前状态。如果您需要在其他地方调用标准的 OpenGL API 函数(例如,在您的组件构造函数中或在您的自定义绘制函数中),您必须首先调用 makeCurrent()。

所有绘图都在 OpenGL 可变窗口对象中发生。〈makeCurrent〈确保它在上下文中绑定。在 paintGL() 中的绘制代码中创建和绑定附加的可变窗口对象时,请注意这一点。永远不要重新绑定 ID 为 0 的可变窗口。相反,调用 defaultFramebufferObject() 来获取应绑定的 ID。

如果平台支持,QOpenGLWidget 允许使用不同的 OpenGL 版本和配置文件。只需通过 setFormat() 设置所需格式即可。但请注意,在同一窗口中有多个 QOpenGLWidget 实例时,它们都必须使用相同的格式,或者至少使用不会使上下文变为不可共享的格式。为了克服这个问题,最好使用 QSurfaceFormat::setDefaultFormat() 而不是 setFormat()。

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

绘画技巧

如上所述,可以通过以下方式子类化QOpenGLWidget来绘制纯3D内容

  • 重写initializeGL()和resizeGL()函数以设置OpenGL状态并提供透视变换。
  • 重写paintGL()以绘制3D场景,只调用OpenGL函数。

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

  • 在paintGL()中,不使用OpenGL命令,而是为该小部件构造一个QPainter对象。
  • 使用QPainter的成员函数绘制原语。
  • 仍然可以发出OpenGL命令。但是,必须确保这些命令被painter的beginNativePainting()和endNativePainting()调用所包围。

当仅使用QPainter进行绘图时,也可以像对普通小部件那样执行绘图:通过重写paintEvent()。

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

OpenGL函数调用、头文件和QOpenGLFunctions

在调用OpenGL函数时,强烈建议避免直接调用函数。相反,建议使用QOpenGLFunctions(当制作可移植应用程序时)或其版本变体(例如,当针对现代桌面OpenGL时,例如QOpenGLFunctions_3_2_Core和类似),这样应用程序就能在所有Qt构建配置中正确运行,包括那些执行动态OpenGL实现加载的配置,这意味着应用程序并未直接链接到GL实现,因此直接函数调用是不可能的。

在paintGL()中,可以通过调用QOpenGLContext::currentContext()一直访问当前上下文。从这个上下文中,可以通过调用QOpenGLContext::functions()获取一个已初始化的、准备好使用的QOpenGLFunctions实例。作为在每次GL调用的前缀的替代方案,可以在initializeGL()中从QOpenGLFunctions继承并调用QOpenGLFunctions::initializeOpenGLFunctions()。

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

代码示例

要开始,最简单的QOpenGLWidget子类可能看起来像以下这样

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

protected:
    void initializeGL() override
    {
        // 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) override
    {
        // 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() override
    {
        // Draw the scene:
        QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
        f->glClear(GL_COLOR_BUFFER_BIT);
        ...
    }

};

或者,可以通过从QOpenGLFunctions派生来避免每个OpenGL调用的前缀

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

要获取与给定OpenGL版本或配置文件兼容的上下文,或者请求深度和模板缓冲区,请调用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的深度测试相关的操作可能会无法按预期工作。常用的深度和模板缓冲区大小请求分别为24和8。

对于OpenGL 3.0+上下文,当可移植性不是重点时,版本化的QOpenGLFunctions变体可以轻松访问给定版本中所有现代OpenGL函数。

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

如上所述,全局设置请求的格式更为简单且更健壮,以便它在应用程序生命周期的所有窗口和上下文中适用。下面是一个示例:

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();
}

多重采样

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

多重采样支持需要支持多采样渲染缓冲区和帧缓冲区重填。在OpenGL ES 2.0实现中,这些可能不存在。这意味着无法使用多重采样。在现代OpenGL版本和OpenGL ES 3.0及以上版本中,这通常不再成问题。

线程

例如,在辅助线程上执行离屏渲染以生成在GUI/main线程中的paintGL()中使用的纹理,支持通过在每个线程上创建共享此上下文的附加上下文来公开小部件的QOpenGLContext

在GUI/main线程之外直接向paintEvent()绘制到QOpenGLWidget的帧缓冲区是可能的,通过重写它是空的。必须通过QObject::moveToThread()更改上下文的线程亲和性。之后,makeCurrent()和doneCurrent()在辅助线程上可行。注意之后将上下文恢复到GUI/main线程。

由于没有真正的、屏幕上的原生表面,因此无法仅仅为QOpenGLWidget触发缓冲区交换。管理组成和缓冲区交换的责任在于小部件堆栈,在gui线程上。当线程完成更新帧缓冲区时,请在GUI/main线程上调用update()来安排组成。

必须格外小心,以避免在GUI/main线程执行组合期间使用帧缓冲区。当组合开始和结束时,将发出信号aboutToCompose()和frameSwapped()。这些信号在GUI/main线程上发出。这意味着通过使用直接的连接aboutToCompose()将阻止GUI/main线程,直到辅助线程完成其渲染。之后,辅助线程必须停止进一步的渲染,直到frameSwapped()信号发出。如果不接受这种做法,辅助线程必须实现双缓冲机制。这涉及到使用备用渲染目标进行绘图,该渲染目标由线程完全控制,例如,额外的帧缓冲区对象,并在合适的时间将其复制到QOpenGLWidget的帧缓冲区。

上下文共享

当将多个QOpenGLWidget作为同一顶层窗口的子窗口添加时,它们的上下文将相互共享。这不适用于属于不同窗口的QOpenGLWidget实例。

这意味着同一窗口中的所有QOpenGLWidget都可以访问彼此的可共享资源,如纹理,无需额外的"全局共享"上下文。

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

创建额外的QOpenGLContext实例,这些实例与QOpenGLWidget的上下文共享如纹理等资源也是可能的。只需在调用QOpenGLContext::create之前,将context()返回的指针传递给QOpenGLContext::setShareContext即可。由此产生的上下文也可以在另一个线程中使用,允许在线程中生成纹理和异步上传纹理。

请注意,当涉及到底层图形驱动程序时,QOpenGLWidget期望有标准符合的资源共享实现。例如,一些驱动(尤其是针对移动和嵌入式硬件的)在设置现有上下文与后来创建的其他上下文之间的共享时存在问题。一些其他驱动在尝试利用不同线程之间的共享资源时可能会表现出意外的行为。

资源初始化和清理

每当initializeGLpaintGL被调用时,QOpenGLWidget关联的OpenGL上下文都会保证是当前上下文。在调用initializeGL之前,不要尝试创建OpenGL资源。例如,尝试编译着色器、初始化顶点缓冲对象或上传纹理数据会将操作失败时放在子类构造函数中执行。这些操作必须推迟到initializeGL。Qt的一些OpenGL辅助类(如QOpenGLBufferQOpenGLVertexArrayObject)有匹配的延迟行为:它们可以在没有上下文的情况下实例化,但所有初始化都会延迟到create或类似调用。这意味着它们可以作为正常(非指针)成员变量在QOpenGLWidget子类中使用,但create或类似函数只能从initializeGL中调用。但是请注意,并非所有类都是这样设计的。如有疑问,将成员变量设置为指针,并在initializeGL和析构函数中分别动态创建和销毁实例。

释放资源也需要上下文是当前上下文。因此,执行此类清理的析构函数预期会在摧毁任何OpenGL资源或包装器之前调用makeCurrent()。避免通过deleteLaterQObject的父机制进行延迟删除。无法保证在问题实例真正被销毁时上下文 bude 当前上下文。

典型子类在资源初始化和销毁方面的外观通常如下所示

class MyGLWidget : public QOpenGLWidget
{
    ...

private:
    QOpenGLVertexArrayObject m_vao;
    QOpenGLBuffer m_vbo;
    QOpenGLShaderProgram *m_program;
    QOpenGLShader *m_shader;
    QOpenGLTexture *m_texture;
};

MyGLWidget::MyGLWidget()
    : m_program(0), m_shader(0), m_texture(0)
{
    // No OpenGL resource initialization is done here.
}

MyGLWidget::~MyGLWidget()
{
    // Make sure the context is current and then explicitly
    // destroy all underlying OpenGL resources.
    makeCurrent();

    delete m_texture;
    delete m_shader;
    delete m_program;

    m_vbo.destroy();
    m_vao.destroy();

    doneCurrent();
}

void MyGLWidget::initializeGL()
{
    m_vao.create();
    if (m_vao.isCreated())
        m_vao.bind();

    m_vbo.create();
    m_vbo.bind();
    m_vbo.allocate(...);

    m_texture = new QOpenGLTexture(QImage(...));

    m_shader = new QOpenGLShader(...);
    m_program = new QOpenGLShaderProgram(...);

    ...
}

这适用于大多数情况,但并非通用解决方案的理想选择。当小部件被重新父化,以至于最终在完全不同的顶级窗口中,需要更多的东西:通过连接到QOpenGLContextaboutToBeDestroyed()信号,可以在OpenGL上下文即将释放时执行清理。

注意:对于在其生命周期内多次更改其关联顶级窗口的部件,如下代码片段所示的综合清理方法至关重要。每当部件或其父部件被重新设置父项,导致顶级窗口变为不同时,部件的关联上下文将被销毁并创建一个新的上下文。然后,将对initializeGL() 进行调用,其中必须重新初始化所有 OpenGL 资源。因此,执行适当清理的唯一选项是连接到上下文的 aboutToBeDestroyed() 信号。请注意,当该信号被发出时,所涉及的上下文可能不是当前上下文。因此,在连接的槽中调用 makeCurrent() 是一种 good practice。另外,需要从派生类的析构函数中执行相同的清理步骤,因为当部件被销毁时,连接到信号的槽或 lambda 可能没有调用。

MyGLWidget::~MyGLWidget()
{
    cleanup();
}

void MyGLWidget::initializeGL()
{
    ...
    connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}

void MyGLWidget::cleanup()
{
    makeCurrent();
    delete m_texture;
    m_texture = 0;
    ...
    doneCurrent();
    disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}

注意:当设置了 Qt::AA_ShareOpenGLContexts 时,部件的上下文永远不会变化,即使在 Reparenting 期间也不会变化,因为部件的关联纹理也将可以从新的顶级上下文中访问。因此,在此标志设置的情况下,不一定需要处理上下文的 aboutToBeDestroyed() 信号。

由于上下文共享,适当的清理特别重要。尽管每个 QOpenGLWidget 的关联上下文都会与 QOpenGLWidget 一起销毁,但该上下文中的可共享资源,如纹理,将保持有效,直到包含 QOpenGLWidget 的顶级窗口被销毁。此外,如 Qt::AA_ShareOpenGLContexts 和一些 Qt 模块可能触发的上下文共享的更广泛范围,可能导致相关资源在整个应用程序生命周期内保持活跃。因此,始终为 QOpenGLWidget 中使用的所有资源和资源包装执行显式清理也是最安全且最稳健的做法。

限制和其他考虑因素

将其他部件放在下方并使 QOpenGLWidget 透明不会导致预期的结果:下面的部件不会可见。这是因为实际中,QOpenGLWidget 会先绘制所有其他常规、非 OpenGL 部件,因此不可能实现透视类型的解决方案。其他类型的布局,如部件位于 QOpenGLWidget 顶部,将按预期工作。

如果绝对必要,可以通过设置 QOpenGLWidget 的 Qt::WA_AlwaysStackOnTop 属性来克服此限制。但是请注意,这会破坏堆叠顺序,例如,将无法在 QOpenGLWidget 上方放置其他部件,因此它应该只在需要具有可见下方部件的半透明 QOpenGLWidget 的场景中使用。

请注意,在没有其他部件的情况下,且意图是具有半透明窗口时,这不适用。在这种情况下,设置顶级窗口的 Qt::WA_TranslucentBackground 是传统方法就足够了。请注意,如果只在 QOpenGLWidget 中希望具有透明区域,则在启用 Qt::WA_TranslucentBackground 后,需要将 Qt::WA_NoSystemBackground 转换回 false。此外,根据系统情况,可能还需要通过 setFormat() 请求 QOpenGLWidget 上下文的 alpha 通道。

QOpenGLWidget支持多种更新行为,就像QOpenGLWindow一样。在保留模式下,之前的paintGL()调用渲染的内容将在下一个调用中可用,从而允许增量渲染。在非保留模式下,内容会丢失,并且期望paintGL()实现重新绘制视图中的所有内容。

在Qt 5.5之前,QOpenGLWidget的默认行为是在paintGL()调用之间保留渲染内容。从Qt 5.5开始,默认行为是非保留模式,因为这提供了更好的性能,并且大多数应用程序没有保留之前内容的需求。这也类似于基于OpenGL的QWindow的语义,并且与QOpenGLWindow的默认行为相匹配,即在每一帧中使颜色和辅助缓冲区无效。要恢复保留行为,请调用setUpdateBehavior()并使用PartialUpdate

注意:在动态将QOpenGLWidget添加到窗口层次结构中时,例如通过将新的QOpenGLWidget作为子窗口添加到一个已经显示在屏幕上的顶层窗口中,如果QOpenGLWidget是该窗口中该类型的第一个实例,其关联的本地窗口可能会被隐式销毁和重新创建。这是因为窗口类型已从RasterSurface变为OpenGLSurface,这具有平台特定的含义。在Qt 6.4中引入了这种新行为。

一旦QOpenGLWidget被添加到窗口层次结构中,顶层窗口的内容将通过OpenGL-based rendering刷新。除了QOpenGLWidget之外的控件将继续使用软件化的画家绘制其内容,但最终的合成是通过3D API完成的。

注意:由于与其他基于QWidget的内容的合成方式,显示QOpenGLWidget需要在关联的顶层窗口的backing store中具有alpha通道。如果没有alpha通道,QOpenGLWidget渲染的内容将不可见。这在Linux/X11的远程显示设置(如使用Xvnc)中尤其相关,当使用的颜色深度低于24时。例如,16位的颜色深度通常映射到使用QImage::Format_RGB16(RGB565)格式的backing store图像,没有为alpha通道留下空间。因此,如果遇到QOpenGLWidget的内容与其他窗口中的控件正确合成的困难,请确保服务器(如vncserver)使用24位或32位深度,而不是16位。

替代方案

将QOpenGLWidget添加到窗口中将为整个窗口开启基于OpenGL的合成。在某些特殊情况下,这可能不是理想的,而希望使用类似QGLWidget的旧样式行为,带有单独的、本地的子窗口。理解此方法局限性的桌面应用程序(例如,在重叠、透明度、滚动视图和MDI区域时)可以使用QOpenGLWindowQWidget::createWindowContainer()。这是QGLWidget的现代替代品,由于没有额外的合成步骤,速度比QOpenGLWidget快。强烈建议将此方法的用途限制在没有其他选择的情况下。请注意,此选项对于大多数嵌入式和移动平台来说不合适,并且已知在某些桌面平台(如macOS)上存在一些问题。始终推荐使用稳定的跨平台解决方案,即QOpenGLWidget。

立体渲染

从6.5版本开始,QOpenGLWidget支持立体渲染。要启用它,请在创建窗口之前全局设置QSurfaceFormat::StereoBuffers标志,使用QSurfaceFormat::SetDefaultFormat()。

注意:由于内部处理标志的方式,使用 setFormat() 不一定能正常工作。

这将会触发在每个帧中调用 paintGL() 两次,一次对应每个 QOpenGLWidget::TargetBuffer。在 paintGL() 中调用 currentTargetBuffer() 来查询当前正在绘制到哪一个。

注意:为了更精细地控制左右色彩缓冲区,考虑使用 QOpenGLWindow + QWidget::createWindowContainer()。

注意:此类3D渲染具有一定的硬件要求,例如显卡需要配置成支持立体显示。

OpenGL是硅 Graphics,Inc. 在美国及其他国家的注册商标。

另请参阅:QOpenGLFunctionsQOpenGLWindowQt::AA_ShareOpenGLContextsUpdateBehavior

成员类型文档

[since 6.5] 枚举 QOpenGLWidget::TargetBuffer

指定在启用立体渲染时要使用的缓冲区,它通过设置 QSurfaceFormat::StereoBuffers 来切换。

注意:左缓冲区始终是默认值,并且在立体渲染未启用或图形驱动程序不支持立体渲染时用作后备值。

常量
QOpenGLWidget::LeftBuffer0
QOpenGLWidget::RightBuffer1

此枚举是从 Qt 6.5 开始引入的。

枚举 QOpenGLWidget::UpdateBehavior

此枚举描述了 QOpenGLWidget 的更新语义。

常量描述
QOpenGLWidget::NoPartialUpdate0QOpenGLWidget 在绘制到屏幕后将会丢弃颜色缓冲区和辅助缓冲区的内容。这可以通过调用QOpenGLContext::swapBuffers(以默认启用的 opengl 和 QWindow 作为参数)来期待相同的行为。在某些硬件架构中,当使用帧缓冲对象作为渲染目标时,NoPartialUpdate 可能具有一些性能优势。帧缓冲对象在帧之间无效化(如果支持 glInvalidateFramebuffer),或者作为后备使用 glDiscardFramebufferEXT(如果支持)或调用 glClear。
QOpenGLWidget::PartialUpdate1帧缓冲对象的颜色缓冲区和辅助缓冲区在帧之间不会无效化。

另请参阅:updateBehavior() 和 setUpdateBehavior()。

成员函数文档

[explicit] QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())

构建一个具有指定标志 fparent 的子窗口的控件。

[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()

销毁 QOpenGLWidget 实例,并释放其资源。

在析构函数中,将 QOpenGLWidget 的上下文设置为当前状态,以确保安全地销毁可能需要释放属于此小部件提供的上下文中 OpenGL 资源的任何子对象。

警告:如果你将OpenGL资源封装的对象(如 QOpenGLBufferQOpenGLShaderProgram 等)作为 OpenGLWidget 子类的成员,可能需要在那个子类的析构函数中也添加对 makeCurrent() 的调用。由于 C++ 对象析构的规则,这些对象将在调用此函数之前被销毁(但在此之后,子类的析构函数已经执行),因此在此函数中使 OpenGL 上下文成为当前上下文的时间太晚了,无法安全地处置。

另请参阅:makeCurrent

[信号] void QOpenGLWidget::aboutToCompose()

当小部件的最顶层窗口即将开始组合其 QOpenGLWidget 子窗口和其他窗口的纹理时,将发出此信号。

[信号] void QOpenGLWidget::aboutToResize()

当小部件的大小改变,因此帧缓冲对象将被重新创建时,将发出此信号。

QOpenGLContext *QOpenGLWidget::context() const

返回值:由这个小部件使用的 QOpenGLContext 或目前尚未初始化时的 0

注意:小部件使用的上下文和帧缓冲对象在通过 setParent() 重新父化小部件时会改变。

另请参阅:QOpenGLContext::setShareContext() 和 defaultFramebufferObject

[自 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const

返回当前活动目标缓冲区。默认情况下,这是左侧缓冲区;当启用 QSurfaceFormat::StereoBuffers 时,右侧缓冲区才会使用。当启用立体渲染时,可以在 paintGL() 中查询当前正在使用哪个缓冲区。paintGL() 将被调用两次,一次用于每个目标。

此函数是在 Qt 6.5 中引入的。

另请参阅:paintGL

GLuint QOpenGLWidget::defaultFramebufferObject() const

返回帧缓冲对象句柄或目前尚未初始化时的 0

注意:帧缓冲对象属于 context() 返回的上下文,并且可能无法从其他上下文中访问。

注意:小部件使用的上下文和帧缓冲对象在通过 setParent() 重新父化小部件时会改变。此外,在每次调整大小后,帧缓冲对象也会改变。

另请参阅:context

[自 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const

返回指定目标缓冲区的帧缓冲对象句柄或目前尚未初始化时的 0

仅当 QSurfaceFormat::StereoBuffers 已启用且硬件支持时调用此重载才有意义。如果不支持,此方法将返回默认缓冲区。

注意: 帧缓冲对象属于由 context() 返回的上下文,可能无法从其他上下文中访问。当通过 setParent() 函数重设定 widget 的父级时,上下文和 widget 使用的帧缓冲对象会发生变化。此外,每次调整大小后,帧缓冲对象也会发生变化。

此函数是在 Qt 6.5 中引入的。

另请参阅:context

void QOpenGLWidget::doneCurrent()

释放上下文。

在大多数情况下,不需要调用此函数,因为 widget 会确保在调用 paintGL() 时上下文被正确绑定和释放。

[重写虚受保护] bool QOpenGLWidget::event(QEvent *e)

重新实现:QWidget::event(QEvent *event)。

QSurfaceFormat QOpenGLWidget::format() const

返回所用 widget 和其顶层窗口的上下文和表面格式。

widget 和其顶层都已创建、调整大小并显示后,此函数将返回上下文实际格式。这可能与请求的格式不同,如果平台无法满足请求,则可能得到比请求更大的颜色缓冲区大小。

当 widget 的窗口及其相关 OpenGL 资源尚未初始化时,返回值是通过 setFormat() 设置的格式。

另请参阅 setFormat() 和 context

[信号] void QOpenGLWidget::frameSwapped()

在此信号发出后,widget 的顶层窗口已完成合成,并从其可能阻止的 QOpenGLContext::swapBuffers() 调用返回。

QImage QOpenGLWidget::grabFramebuffer()

渲染并返回帧缓冲区的32位RGB图像。

注意:这可能会是一项昂贵的操作,因为它依赖于 glReadPixels() 来读取像素。这可能会很慢,并阻止 GPU 管道。

[自 6.5] QImage QOpenGLWidget::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)

渲染并返回指定目标缓冲区的帧缓冲区的32位RGB图像。此重载仅在启用了 QSurfaceFormat::StereoBuffers 时才有意义。如果立体渲染被禁用或硬件不支持,则检索右侧目标缓冲区的帧缓冲区将返回默认图像。

注意:这可能会是一项昂贵的操作,因为它依赖于 glReadPixels() 来读取像素。这可能会很慢,并阻止 GPU 管道。

此函数是在 Qt 6.5 中引入的。

[虚受保护] void QOpenGLWidget::initializeGL()

此虚拟函数在第一次调用 paintGL() 或 resizeGL() 之前被调用一次。在子类中重写它。

此函数应设置任何所需 OpenGL 资源。

调用makeCurrent()()没有必要,因为在调用此函数时,这一操作已经完成。但是请注意,在此阶段帧缓冲区尚未可用,因此请避免从此处发出绘图调用。将此类调用推迟到paintGL()()。

另请参阅paintGL()和resizeGL()。

bool QOpenGLWidget::isValid() const

如果控件和OpenGL资源,如上下文,已成功初始化,则返回 true。请注意,在控件显示之前,返回值始终为false。

void QOpenGLWidget::makeCurrent()

为在此控件中进行OpenGL内容渲染做准备,通过使相应的上下文变为当前上下文,并在此上下文中绑定帧缓冲区对象。

在大多数情况下,无需调用此函数,因为它会在调用paintGL()()之前自动调用。

另请参阅context(),paintGL(),和doneCurrent()。

[自6.5以来] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)

为在此控件中对OpenGL内容进行渲染做准备,通过使传入的缓冲区的上下文变为当前上下文,并在此上下文中绑定帧缓冲区对象。

注意:仅在启用立体渲染时调用此函数才具有意义。当禁用右缓冲区时,请求右缓冲区将不会发生任何事情。

在大多数情况下,无需调用此函数,因为它会在调用paintGL()()之前自动调用。

此函数是在 Qt 6.5 中引入的。

另请参阅context(),paintGL(),和doneCurrent()。

[重写虚拟受保护] int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const

重实现了:QWidget::metric(QPaintDevice::PaintDeviceMetric m) const

[重写虚拟受保护] QPaintEngine *QOpenGLWidget::paintEngine() const

重实现了:QWidget::paintEngine() const

[重写虚拟受保护] void QOpenGLWidget::paintEvent(QPaintEvent *e)

重实现了:QWidget::paintEvent(QPaintEvent *event)。

处理绘制事件。

调用QWidget::update()会导致发送绘制事件e,从而调用此函数。(请注意,这是异步的,并在从update()返回后发生。)然后,在此函数调用虚拟paintGL()以更新QOpenGLWidget的帧缓冲区内容之后,控件的最顶层窗口将与帧缓冲区的纹理组合起来。

[虚拟受保护] void QOpenGLWidget::paintGL()

此虚拟函数在需要绘制控件时被调用。请在子类中重写它。

无需调用makeCurrent(),因为当调用此函数时,这已经完成。

在调用此函数之前,已设置上下文和帧缓冲区,并通过调用glViewport()设置视口。框架不会设置其他状态,也不会清除或绘制。

默认实现执行glClear()。子类不应该调用基类实现,而应自行执行清除。

注意:为了确保可移植性,不要期望在initializeGL()中设置的状态持久。相反,应在paintGL()中设置所有必要的状态,例如,通过调用glEnable()。这是因为某些平台(如使用WebGL的WebAssembly),在某些情况下对OpenGL上下文可能有限制,这可能导致除了与QOpenGLWidget一起使用之外还用于其他目的。

当启用QSurfaceFormat::StereoBuffers时,此函数将被调用两次——每个缓冲区调用一次。通过调用currentTargetBuffer()查询已绑定哪个缓冲区。

注意:即使硬件不支持立体渲染,也会绘制到每个目标的帧缓冲区。只有左缓冲区实际上会在窗口中可见。

另请参阅:initializeGL()、resizeGL() 和 currentTargetBuffer

[override virtual protected] QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const

[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)

重新实现:QWidget::resizeEvent(QResizeEvent *event)。

处理传递给 e 事件参数的尺寸事件。调用虚拟函数 resizeGL

注意:避免在派生类中重写此函数。如果这不可行,确保也调用了 QOpenGLWidget 的实现。否则,基础帧缓冲区对象和相关资源将不会正确地调整大小,从而导致错误的渲染。

[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)

每次小部件调整大小时,都会调用此虚拟函数。在子类中实现它。新的大小传递到 wh

不需要调用makeCurrent(),因为调用此函数时已经完成。此外,还会绑定帧缓冲区。

另请参阅:initializeGL() 和 paintGL

[信号] void QOpenGLWidget::resized()

在由于调整小部件尺寸而重新创建帧缓冲区对象后,发出此信号。

void QOpenGLWidget::setFormat(const QSurfaceFormat &format)

设置请求的表面格式

如果未通过此函数显式设置格式,则将使用QSurfaceFormat::defaultFormat() 返回的格式。这意味着在存在多个OpenGL小部件的情况下,可以对每个小部件的此函数调用进行替换,在创建第一个小部件之前进行一次QSurfaceFormat::setDefaultFormat() 调用。

注意:通过此函数请求alpha缓冲区不会导致期望的结果,如果目的是使下面的其他小部件可见。相反,使用Qt::WA_AlwaysStackOnTop启用与其他小部件可见的半透明QOpenGLWidget实例。但是要注意,这会破坏堆叠顺序,因此将不再可能有小部件位于QOpenGLWidget之上。

另请参阅:format(),Qt::WA_AlwaysStackOnTop,和QSurfaceFormat::setDefaultFormat

void QOpenGLWidget::setTextureFormat(GLenum texFormat)

设置texFormat的自定义内部纹理格式。

当与sRGB帧缓冲区一起工作时,需要指定类似GL_SRGB8_ALPHA8的格式。这可以通过调用此函数来实现。

注意:如果小部件已经显示并因此已执行初始化,则此函数调用将没有效果。

注意:此函数通常必须与设置颜色空间为QSurfaceFormat::sRGBColorSpaceQSurfaceFormat::setDefaultFormat()调用一起使用。

另请参阅:textureFormat

void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)

将此小部件的更新行为设置为updateBehavior

另请参阅:updateBehavior

GLenum QOpenGLWidget::textureFormat() const

如果小部件已经初始化,则返回活动内部纹理格式;如果设置了请求的格式但小部件尚未可见,则返回请求的格式;如果没有调用setTextureFormat()且小部件尚未可见,则返回nullptr

另请参阅:setTextureFormat

QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const

返回小部件的更新行为。

另请参阅:setUpdateBehavior

© 2024 Qt公司有限。包含在此处的文档贡献的版权归其各自的所有者。此处提供的文档是根据自由软件基金会出版的GNU自由文档许可第1.3版许可的。Qt和其相应的标志是芬兰的Qt公司及其在全球的商标。所有其他商标是各自所有者的财产。