在 OpenGL 中使用 Qt3D 离屏渲染纹理

Use Qt3D offscreen-rendered texture in OpenGL

目标

我想为 Qt3D 实现一个实际的小部件,因为 QWidget::createWindowContainer 不适合我。


问题描述

我第一个让新的 class subclass QWidgetQSurface 的方法没有成功,因为 Qt3D 代码要么期望 QWindow 要么multiple places 中的 QOffscreenSurface,我不想重新编译整个 Qt3D 基础。

我的第二个想法是将 Qt3D 内容渲染到屏幕外表面,然后在 QOpenGLWidget 中的四边形上绘制纹理。当我使用 QRenderCapture framegraph 节点将渲染的图像保存到屏幕外纹理,然后将图像加载到 QOpenGLTexture 并在 QOpenGLWidgetpaintGL 函数中绘制时我可以看到渲染图像 - 即在 Qt3D 和 OpenGL 小部件中渲染工作正常。与直接从 Qt3D 渲染内容相比,这非常慢。

现在,当我在渲染 QOpenGLWidget 期间使用 QTexutre2D 返回的 GLuint 绑定纹理时,一切都保持黑色。

当然,如果 QOpenGLWidget 和 QT3D 的上下文是完全分开的,这是有道理的。但是通过检索 AbstractRenderer from the QRenderAspectPrivate 我能够获得 Qt3D 使用的上下文。在我的 main.cpp 我设置

QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

QOpenGLWidget 和 Qt3D 的上下文都引用相同的共享上下文 - 我通过使用 qDebug 打印两者来验证这一点,它们是同一个对象。

这不应该允许我使用 Qt3D 中的纹理吗?

或者关于如何实现此类小部件的任何其他建议?我只是认为这是最简单的方法。


实施细节/到目前为止我已经尝试过的

这就是我 QOpenGLWidget 中的 paintGL 函数的样子:

glClearColor(1.0, 1.0, 1.0, 1.0);
glDisable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

d->m_shaderProgram->bind();
{
    QMatrix4x4 m;
    m.ortho(0, 1, 1, 0, 1.0f, 3.0f);
    m.translate(0.0f, 0.0f, -2.0f);

    QOpenGLVertexArrayObject::Binder vaoBinder(&d->m_vao);

    d->m_shaderProgram->setUniformValue("matrix", m);
    glBindTexture(GL_TEXTURE_2D, d->m_colorTexture->handle().toUInt());
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
d->m_shaderProgram->release();

m_colorTexture 是附加到 QRenderTargetOutput/QRenderTargetQTexture2D,Qt3D 将场景渲染到屏幕外。

我有一个 QFrameAction 来触发 QOpenGLWidget 上的绘制更新:

connect(d->m_frameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qt3DWidget::paintGL);

我已经验证这确实调用了 paintGL 函数。所以每次我绘制 QOpenGLWidget 时,框架都应该准备好并出现在纹理中。

我也尝试用 QSharedGLTexture 替换 m_colorTexture。然后我用 QOpenGLWidget 的上下文创建了这个纹理,就像这样

m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
// w and h are width and height of the widget
m_texture->setSize(w, h);
// m_colorTexture is the QSharedGLTexture
m_colorTexture->setTextureId(m_texture->textureId());

QOpenGLWdigetresizeEvent 函数中,我在此纹理以及 Qt3D 的所有屏幕外资源上设置了适当的大小。这也只显示黑屏。在绑定纹理后直接放置 qDebug() << glGetError(); 只会每次显示 0,所以我假设没有任何错误。


可以找到代码here in my GitHub project

更新(2021 年 5 月 10 日,因为我再次偶然发现了我的答案):

我的 Qt3DWidget implementation 现在可以正常工作了,问题是当框架动作被触发时我不得不调用 update() 而不是 paintGL (呃,愚蠢的我,我实际上知道那个)。


虽然我没有找到我的问题的确切解决方案,但由于我成功创建了 Qt3D 小部件,所以我会 post 在这里给出答案。

可以找到代码here。这不是最干净的解决方案,因为我认为应该可以以某种方式使用共享纹理。相反,现在我在 Qt3D 上设置 QOpenGLWidget 的上下文,为此我必须使用 Qt3D 的私有 类。这意味着 Qt3D 直接绘制到由 OpenGL 小部件绑定的帧缓冲区上。不幸的是,现在小部件必须是渲染驱动程序并通过调用 processFrameQAspectEngine 上执行手动更新。理想情况下,我希望将所有处理循环留给 Qt3D,但至少小部件现在可以按原样工作。

编辑:

我在手动测试 here 中找到了 QSharedGLTexture 的示例。它以相反的方式工作,即 OpenGL 渲染纹理而 Qt3D 使用它,所以我认为应该可以反转方向。不幸的是,QSharedGLTexture 似乎有点不稳定,因为调整 OpenGL window 的大小有时会使应用程序崩溃。这就是为什么我现在会坚持我的解决方案。但是,如果有人有关于此问题的消息,请随时 post 回答!