【问题标题】:Qt, openGL, widgets and offscreen renderingQt、openGL、小部件和离屏渲染
【发布时间】:2018-01-17 05:36:45
【问题描述】:

我在 RedHat Linux 上开发,cat /etc/redhat-release:

    Red Hat Enterprise Linux Workstation release 7.2 (Maipo)

我正在使用 Qt Creator 4.3.1:

    Based on Qt 5.9.1 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6), 64 bit)

我正在开发的项目使用 Qt 5.6.2 GCC 64bit,该项目是使用源自 QWidget 的图形对象开发的,其中包括实时视频流。

不幸的是,我们在播放视频时经历了撕裂,这在视频周围显示的其他小部件中也很明显,我相信这是因为视频没有使用 vsync。

我相信使用 openGL 会纠正这种情况,目的是使用 openGL 重写包括视频播放在内的小部件。我花了几天的时间试图找到完整且可行的解决方案,但到目前为止未能找到完整且可行的解决方案。

我一直在研究在我用来测试的小部件中使用 QOpenGLWidget:

    class clsElevStrip : public QOpenGLWidget, protected QOpenGLFunctions {
    Q_OBJECT

在构造函数中,我设置了离屏渲染的格式:

    //Create surface format for rendering offscreen
    mobjFormat.setDepthBufferSize(24);
    mobjFormat.setSamples(4);
    mobjFormat.setVersion(3, 0);
    mobjFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
    setFormat(mobjFormat);

在paintGL方法中:

    QOpenGLContext* pobjContext = context();
    QSurface* pobjSurface = pobjContext->surface();
    assert(pobjSurface != NULL);

    int intSB1 = pobjSurface->format().swapBehavior();
    qDebug() << (QString("paintGL:format: ")
               + QString::number(intSB1));
    pobjContext->makeCurrent(pobjSurface);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(-0.5, -0.5, 0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0);
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f( 0.0,  0.5, 0);
    glEnd();

    pobjContext->swapBuffers(pobjSurface);

主显示屏上什么都看不到,调试语句将格式显示为 2(双缓冲)。

如果我在构造函数中注释掉该行:

    setFormat(mobjFormat);

调试语句显示格式为 0 (DefaultSwapBehavior)。而且图形是可见的,我错过了什么?

【问题讨论】:

  • 为了省去你以后在谷歌上搜索的麻烦:你的目标是双缓冲渲染,而不是离屏渲染。双缓冲是,当您在后缓冲区中准备图像并在垂直回扫间隔中将后缓冲区交换到屏幕扫描输出前缓冲区时。 离屏渲染是,如果您要渲染到不会直接在显示设备上显示的缓冲区,即您打算将渲染的图像保存到文件而不显示或生成中间图像在显示之前进行后处理。
  • @datenwolf,谢谢,我之前使用离屏渲染来准备图形,然后将 blit 到可见显示(不使用 Qt 或 openGL),对于我们的要求,目的是消除撕裂视频和图形以提供流畅的外观。
  • 是的,这已经很清楚了。使用 OpenGL 及其交换间隔扩展 (khronos.org/opengl/wiki/Swap_Interval) 无疑是获得垂直同步正确显示更新的可行方法(但要小心合成 WM)。但是,如果您的目标是显示视频,那么您平台的专用视频演示 API 之一可能也是一个合适的选择。在 X11 上,它是 Xv 或 XVVA(VAAPI) 或 VDPAU 之一。它们都有 vsync 控制。
  • @datenwolf,谢谢,您能否举例说明您的意思,我在 RedHat 上使用 Qt 和 openGL,我不确定您所说的“在 X11 上会是什么意思” Xv 或 XVVA(VAAPI) 或 VDPAU 之一”?目前,当指定 DoubleBuffering 时,我没有任何可见视频。
  • 这些只是替代 API,您可以使用它们来代替 OpenGL。

标签: c++ qt opengl double-buffering


【解决方案1】:

解决您的问题的方法很简单:

只是不要把所有的 QOpenGLGLContext 玩弄。 paintGL 的全部意义在于,这个特定的函数是在一个包装器中调用的,该包装器已经为您完成了所有上下文杂耍。 **无需致电makeCurrentswapBuffers。 Qt 已经为您做到了!

来自 Qt 文档

void QOpenGLWidget::paintGL()

这个虚函数在小部件需要时被调用 绘。在子类中重新实现它。

没有必要调用 makeCurrent() 因为它有 调用此函数时已经完成。

在调用这个函数之前,上下文和帧缓冲区是 绑定,并且视口是通过调用 glViewport() 来设置的。 没有设置其他状态,也没有执行清除或绘制 由框架决定。

如果您将其作为您的 paintGL,它将显示一些内容,如果您有 兼容性配置文件 >=OpenGL-3.x 上下文,或者如果您使用的是您正在使用旧的固定函数管道,它适用于 OpenGL-3.x 核心配置文件上下文!

void glwidget::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(-0.5, -0.5, 0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0);
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f( 0.0,  0.5, 0);
    glEnd();
}

【讨论】:

  • 我认为对 context() 的调用会返回 paintGL 所属的 QOpenGLWidget 类的上下文?
  • 根据doc.qt.io/qt-5/qopenglwidget.html#context,调用context会返回一个指向widget中context的指针……不是新的context。
  • 我知道这是真的,因为paintGL 中的上下文与构造函数中设置的格式相同。
  • 我已经注释掉了 pobjContext->makeCurrent(pobjSurface);和 pobjContext->swapBuffers(pobjSurface);但仍然没有显示。所以现在我的paintGL 匹配你的,但仍然没有。
  • 我不知道为什么,但编辑构造函数并将格式设置更改为:mobjFormat.setSamples(16); mobjFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);现在它显示出来了。
猜你喜欢
  • 1970-01-01
  • 2018-06-11
  • 2011-04-20
  • 2010-09-17
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多