Qt 3D Render Porting to RHI

As a reminder, in Qt 6, Qt 3D will default to using it's RHI rendering backend.

Using the older OpenGL backend from the Qt 5 series remains possible. This can be enabled by setting environment variable QT3D_RENDERER to opengl. This is required in case you don't want to port your application to support RHI or in case you need features which are currently limited or not available on the RHI backend.

Currently, the known RHI limitations are:

  • No way to explicitly Blit (you have to blit manually by rendering a quad into a framebuffer)
  • MemoryBarrier cannot be set explicitly
  • Not all Texture Formats are available
  • Draw Indirect is currently not supported
  • Geometry Shaders are currently not supported.
  • Different RHI backends might support different feature set.

Please also take care not to confuse the Qt 3D OpenGL render backend with Qt 3D's RHI render backend running on top of OpenGL.

RHI is an abstraction over different graphics API. This means that on a given platform, several RHI could use several backends.

To force RHI to use a given backend, the QSG_RHI_BACKEND environment variable should be set to one of opengl, vulkan, metal, directx.

Add RHI Compatible Techniques

To add RHI support to a Qt 3D Material / Effect, a new Technique targeting RHI will be required. As of this writing, the only valid RHI version is 1.0.

 Material {
     Effect {
         techniques: [
             Technique {
                 id: gl3Technique
                 graphicsApiFilter {
                     api: GraphicsApiFilter.OpenGL
                     profile: GraphicsApiFilter.CoreProfile
                     majorVersion: 3
                     minorVersion: 1
                 }
                 renderPasses: RenderPass {
                     id: gl3Pass
                     shaderProgram: ShaderProgram {
                         ...
                     }
                 }
             },
             Technique {
                 id: rhiTechnique
                 graphicsApiFilter {
                     api: GraphicsApiFilter.RHI
                     profile: GraphicsApiFilter.NoProfile
                     majorVersion: 1
                     minorVersion: 0
                 }
                 renderPasses: RenderPass {
                     id: rhiPass
                     shaderProgram: ShaderProgram {
                         ...
                     }
                 }
             }
         ]
     }
 }
 QMaterial *material = new QMaterial();
 QEffect *effect = new QEffect();

 // Set the effect on the material
 material->setEffect(effect);

 {
     QTechnique *gl3Technique = new QTechnique();
     QRenderPass *gl3Pass = new QRenderPass();
     QShaderProgram *glShader = new QShaderProgram();

     // Set the shader on the render pass
     gl3Pass->setShaderProgram(glShader);

     // Add the pass to the technique
     gl3Technique->addRenderPass(gl3Pass);

     // Set the targeted GL version for the technique
     gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
     gl3Technique->graphicsApiFilter()->setMajorVersion(3);
     gl3Technique->graphicsApiFilter()->setMinorVersion(1);
     gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);

     // Add the technique to the effect
     effect->addTechnique(gl3Technique);
 }

 {
     QTechnique *rhiTechnique = new QTechnique();
     QRenderPass *rhiPass = new QRenderPass();
     QShaderProgram *rhiShader = new QShaderProgram();

     // Set the shader on the render pass
     rhiPass->setShaderProgram(glShader);

     // Add the pass to the technique
     rhiTechnique->addRenderPass(rhiPass);

     // Set the targeted RHI version for the technique
     rhiTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
     rhiTechnique->graphicsApiFilter()->setMajorVersion(1);
     rhiTechnique->graphicsApiFilter()->setMinorVersion(0);
     rhiTechnique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);

     // Add the technique to the effect
     effect->addTechnique(rhiTechnique);
 }

Creating a RHI compatible shader

Regardless on which backend RHI will be running on top of, the shaders will be written in GLSL 450.

Changes are minimal compared to earlier GLSL versions, the main noticeable differences are in the way uniforms are declared. Please also note that in and out variables need to have their locations defined and that they should be consistent between shader stages.

 #version 450 core

 layout(location = 0) in vec3 vertexPosition;
 layout(location = 0) out vec3 worldPosition;

 layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
   mat4 viewMatrix;
   mat4 projectionMatrix;
   mat4 uncorrectedProjectionMatrix;
   mat4 clipCorrectionMatrix;
   mat4 viewProjectionMatrix;
   mat4 inverseViewMatrix;
   mat4 inverseProjectionMatrix;
   mat4 inverseViewProjectionMatrix;
   mat4 viewportMatrix;
   mat4 inverseViewportMatrix;
   vec4 textureTransformMatrix;
   vec3 eyePosition;
   float aspectRatio;
   float gamma;
   float exposure;
   float time;
   float yUpInNDC;
   float yUpInFBO;
 };

 layout(std140, binding = 1) uniform qt3d_command_uniforms {
   mat4 modelMatrix;
   mat4 inverseModelMatrix;
   mat4 modelViewMatrix;
   mat3 modelNormalMatrix;
   mat4 inverseModelViewMatrix;
   mat4 modelViewProjection;
   mat4 inverseModelViewProjectionMatrix;
 };

 void main()
 {
     ...
 }

For more details about shader changes, please checkout Qt3DRender::QShaderProgram

Qt 3D Extras

Materials in Qt 3D Extras have been ported to RHI.