[Interest] Rendering Qt/QML within native OpenGL or to texture

Stefan Fabian stefan.fabian.dev at hotmail.com
Thu Mar 7 18:33:38 CET 2019


I'm having another attempt at a problem I failed to solve about a year ago.
The rough sketch is:
I have an external application which is using Qt and renders some content using the OpenSource graphics engine OGRE. (I can't / don’t want to change the application's source. My current approach is a plugin) I want to overlay a QWidget or a QML Scene on top of the rendered 3D scene.
Now, there are different ways to achieve this.
1) My currently working one is to create a texture that is overlaid on top of the 3D scene by OGRE.
I simply have to lock the texture's memory, create a QImage on top of the memory and draw into the image.
This works for QWidgets, however, I can't display a QML scene that way (at least not as far as I know).
2) Whenever the scene has rendered, I switch to a QOpenGLContext, QOffscreenSurface, QOpenGLPaintDevice and QOpenGLFramebufferObject. Render the QWidget or QML scene into the FBO, retrieve the rendered content as a QImage, switch back to OGRE's OpenGL context and copy the image's content to the texture.
This works for both QWidgets and QML but the copy operation takes quite a performance penalty making it inviable (around an order of magnitude longer than the first approach).

Today I've found another way to use native OpenGL to draw on top of the scene in OGRE after the render queue ended but couldn't work out how to paint on it using Qt without getting stuck at the getting the content on screen without the QImage copy workaround in Approach 2.
Unfortunately, 3D graphics and OpenGL is pretty far from my fields of expertise which is why I'm hoping someone who is more competent than me regarding OpenGL and QOpenGL can help me figure this out.
I'll attach code parts that I deem important below, if you require a working example I can send you the code (it's not opensource yet but I'm planning on releasing it in the near future) but it requires a Linux distribution (only tested with Ubuntu) and a ROS installation.

To summarize: I'm trying to find a way to either render directly to the texture, use a quicker method to copy the content from the FBO to the texture than getting a QImage and memcpy or, alternatively, render directly on top of the scene using the access to native OpenGL I've found today (here the problem is that I don't really know how to continue using OGRE's context when rendering Qt/QML).

Best regards and thank you for taking your time to read until here, Stefan

Texture generation: 
Ogre::OverlayManager &overlay_manager = Ogre::OverlayManager::getSingleton();
ogre_overlay_ = overlay_manager.create( "overlay" );
material_ = Ogre::MaterialManager::getSingleton().create( "overlay_OverlayMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
overlay_panel_ = dynamic_cast<Ogre::OverlayContainer *>( overlay_manager.createOverlayElement( "Panel", "overlay_Panel" ));
overlay_panel_->setPosition( 0.0, 0.0 );
overlay_panel_->setDimensions( 1.0, 1.0 );
overlay_panel_->setMaterialName( "overlay_OverlayMaterial" );

ogre_overlay_->add2D( overlay_panel_ );

texture_ = Ogre::TextureManager::getSingleton().createManual(
material_->getTechnique( 0 )->getPass( 0 )->createTextureUnitState( texture_->getName());
material_->getTechnique( 0 )->getPass( 0 )->setSceneBlending( Ogre::SBT_TRANSPARENT_ALPHA );
material_->getTechnique( 0 )->getPass( 0 )->setSceneBlending( Ogre::SBF_ONE, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA );


Approach 1: 
buffer_ = texture_->getBuffer();
buffer_->lock( Ogre::HardwareBuffer::HBL_DISCARD );
auto width = (unsigned int) geometry_.width();
auto height = (unsigned int) geometry_.height();
const Ogre::PixelBox &pixel_box = buffer_->getCurrentLock();

auto data = static_cast<Ogre::uint8 *>(pixel_box.data);
int bytes_per_line = pixel_box.getWidth() * 4;
// Empties the texture by overwriting all ARGB data with 0.
Ogre::uint8 *offset = data;
for (int i = 0; i < height; ++i)
  memset(offset, 0, width * 4);
  offset += bytes_per_line;
paint_device_image_ = QImage(data, width, height, bytes_per_line, QImage::Format_ARGB32_Premultiplied);
// Rendering stuff
// ...

Approach 2: 
// Initialization
QSurfaceFormat format;
format.setDepthBufferSize( 16 );
format.setStencilBufferSize( 8 );

context_ = new QOpenGLContext;
context_->setFormat( format );

if ( !context_->create())
  LOG_ERROR( "OverlayManager: Fatal! Failed to create context!" );
surface_ = new QOffscreenSurface;
surface_->setFormat( format );


paint_device_ = new QOpenGLPaintDevice( geometry_.size());

if ( geometry_.width() == 0 || geometry_.height() == 0 ) {
QOpenGLFramebufferObjectFormat format;
format.setSamples( 16 );
format.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil );
fbo_ = new QOpenGLFramebufferObject( texture_->getWidth(), texture_->getHeight(), format );

// Rendering part
context_->functions()->glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// Rendering stuff
// ...
QImage image = fbo_->toImage().convertToFormat( QImage::Format_ARGB32 );

Ogre::HardwarePixelBufferSharedPtr buffer = texture_->getBuffer();
buffer->lock( Ogre::HardwareBuffer::HBL_DISCARD );
const Ogre::PixelBox &pixel_box = buffer->getCurrentLock();

auto data = static_cast<Ogre::uint8 *>(pixel_box.data);
memcpy( data, image.bits(), image.byteCount());


More information about the Interest mailing list