Solved Qt3D: capturing FBO content from an offscreen surface
-
Hello,
I need to render a Qt3D scene offscreen onto an image. The reason is that I need to make a bunch of previews for different objects which will be displayed on UI. Using an OpenGL window there would be overkill.
For that purpose I've setup an offscreen surface and a frame graph with QTexture2D as a QRenderTargetOutput. Unfortunately I couldn't find any way to convert QTexture2D to QImage so decided to use QRenderCapture for that. However, the captured image is absolutely empty, not even the clear colour inside. Just an empty image with the size of the texture.
Here's the frame graph:TextureFrameGraph::TextureFrameGraph(Qt3DCore::QNode *parent): Qt3DRender::QFrameGraphNode(parent) { _surfaceSelector = new Qt3DRender::QRenderSurfaceSelector(this); _targetSelector = new Qt3DRender::QRenderTargetSelector(_surfaceSelector); _target = new Qt3DRender::QRenderTarget(_targetSelector); _targetOutput = new Qt3DRender::QRenderTargetOutput(_target); _viewport = new Qt3DRender::QViewport(_targetSelector); _cameraSelector = new Qt3DRender::QCameraSelector(_viewport); _clearBuffers = new Qt3DRender::QClearBuffers(_cameraSelector); _renderCapture = new Qt3DRender::QRenderCapture(_clearBuffers); _texture = new Qt3DRender::QTexture2D(); _texture->setSize(256, 256); _texture->setFormat(Qt3DRender::QTexture2D::RGBAFormat); _texture->setGenerateMipMaps(false); _texture->setMagnificationFilter(Qt3DRender::QTexture2D::Linear); _texture->setMinificationFilter(Qt3DRender::QTexture2D::Linear); _texture->setWrapMode(Qt3DRender::QTextureWrapMode()); _texture->setComparisonFunction(Qt3DRender::QTexture2D::CompareLessEqual); _texture->setComparisonMode(Qt3DRender::QTexture2D::CompareRefToTexture); _targetOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0); _targetOutput->setTexture(_texture); _target->addOutput(_targetOutput); _targetSelector->setTarget(_target); _viewport->setNormalizedRect(QRectF(0.0f, 0.0f, 1.0f, 1.0f)); _clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer); _clearBuffers->setClearColor(Qt::white); }
The engine aspect:
void TextureTarget::setupAspect() { _camera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection); _camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f); _camera->setUpVector(QVector3D(0, 1, 0)); _camera->setPosition(QVector3D(2, 2, 2)); _camera->setViewCenter(QVector3D()); _aspectEngine->registerAspect(_renderAspect); _frameGraph->setCamera(_camera); _frameGraph->setSurface(_surface); _renderSettings->setActiveFrameGraph(_frameGraph); _root->addComponent(_renderSettings); }
_surface is QOffscreenSurface targeted to OGL 3.3. After the aspect is created, the scene root is set the same way as in Qt3DWindow. The whole aspect is pretty much like in Qt3DWindow just with the offscreen surface and my own graph.
I'm not sure if I've setup the graph somehow incorrectly or I'm doing all that completely wrong. In the end the captured image isn't even of the white colour as it should be due to the clear colour.
Any help would be appreciated. -
Rendered the FBO texture on a plane inside a QWIndow and the texture contains everything I need in it. So it seems that QRenderCapture captures images from the surface, not the "closes" viewport as I hoped it would. I really wish Qt3D docs were at least somehow better. Got to find another way to do what's needed.
-
I'm not sure why your example didn't work, because your graph looks exactly like mine.
Anyway, in case someone is interested in a working example (like I desperately was), here's one on GitHub.