Goal
I'd like to implement an actual widget for Qt3D since QWidget::createWindowContainer
just doesn't cut it for me.
Problem Description
My first approach of letting a new class subclass QWidget
and QSurface
was not successful since the Qt3D code either expects a QWindow
or a QOffscreenSurface
in multiple places and I don't want to recompile the whole Qt3D base.
My second idea was to render the Qt3D content to an offscreen surface and then draw the texture on a quad in a QOpenGLWidget
. When I use a QRenderCapture
framegraph node to save the image rendered to the offscreen texture and then load the image into a QOpenGLTexture
and draw it in the QOpenGLWidget
's paintGL
function I can see the rendered image - i.e. rendering in Qt3D and also in the OpenGL widget works properly. This is just extremely slow compared to rendering the content from Qt3D directly.
Now, when I use the GLuint
returned by the QTexutre2D
to bind the texture during rendering of the QOpenGLWidget
, everything stays black.
Of course this would make sense, if the contexts of the QOpenGLWidget
and QT3D were completely separate. But by retrieving the AbstractRenderer
from the QRenderAspectPrivate
I was able to obtain the context that Qt3D uses. In my main.cpp
I set
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
The context of the QOpenGLWidget
and of Qt3D both reference the same shared context - I verified this by printing both using qDebug
, they are the same object.
Shouldn't this allow me to use the texture from Qt3D?
Or any other suggestions on how to implement such a widget? I just thought this to be the easiest way.
Implementation Details / What I've tried so far
This is what the paintGL
function in my QOpenGLWidget
looks like:
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
is the QTexture2D
that is attached to the QRenderTargetOutput
/QRenderTarget
that Qt3D renders the scene offscreen to.
I have a QFrameAction
in place to trigger draw updates on the QOpenGLWidget
:
connect(d->m_frameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qt3DWidget::paintGL);
I have verified that this indeed calls the paintGL
function. So every time I draw the QOpenGLWidget
, the frame should be ready and present in the texture.
I've also tried to replace the m_colorTexture
with a QSharedGLTexture
. Then I created this texture with the context of the QOpenGLWidget
like this
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());
In the resizeEvent
function of the QOpenGLWdiget
I set the appropriate size on this texture and also on all offscreen resources of Qt3D. This also shows just a black screen. Placing qDebug() << glGetError();
directly after binding the texture simply shows 0
every time, so I assume that there aren't any errors.
The code can be found here in my GitHub project.
QSurface
(becauseQWindow
does andQt3DWindow
is a subclass). Now, I thought about sublcassingQWidget
andQSurface
and pass the surface to theQRenderSurfaceSelector
of Qt3D but I don't actually know how to implement it (e.g. a function that returnsQPlatformSurface *
) and Qt3D internally expects aQWindow
or aQOffscreenSurface
, i.e. I would have to adapt a lot of Qt3D code which makes in infeasible. – Florian Blume