On this page

User-Defined Render Passes in Qt Quick 3D

Qt Quick 3D provides a high-level API for 3D rendering that handles most rendering details automatically. However, for advanced use cases, applications may need complete control over the rendering pipeline. User-defined render passes enable this by allowing applications to disable the internal rendering pipeline and define their own custom passes.

User render passes enable advanced rendering techniques such as:

  • Deferred shading and lighting
  • Multi-pass rendering effects
  • Custom post-processing pipelines
  • Selective rendering with layer-based filtering
  • Screen-space effects (ambient occlusion, reflections, etc.)
  • Custom shadow mapping techniques
  • Debug visualization passes

Levels of Customization

Qt Quick 3D offers three complementary levels of rendering customization, each suited to different use cases:

LevelScopeUse Cases
EffectPost-processingApplies effects after the scene is rendered (blur, color grading, etc.)
CustomMaterialPer-material shadersCustom vertex and fragment shaders for individual materials
RenderPass (User Render Passes)Complete pipeline controlDeferred rendering, multiple passes, custom render targets

User render passes (RenderPass) provide the most control, allowing you to either supplement or completely replace the default rendering pipeline. This complements CustomMaterial and Effect: CustomMaterial customizes how individual objects are rendered, while user render passes control the overall rendering strategy and architecture.

Using User Render Passes

User render passes can be used in two ways:

Supplementing Internal Passes

You can add custom RenderPass objects alongside the default rendering pipeline without disabling internal passes. This is useful for rendering to textures that are then used by Effect or CustomMaterial, or for creating auxiliary render targets.

View3D {
    // Internal passes still run normally

    RenderPassTexture { id: customTexture; format: RenderPassTexture.RGBA16F }

    RenderPass {
        // Custom pass renders to texture
        commands: [
            ColorAttachment { target: customTexture },
            DepthStencilAttachment { }
        ]
    }

    // Use customTexture in an Effect or material
}

Replacing Internal Passes

For complete control over the rendering pipeline, disable Qt Quick 3D's internal rendering by setting the renderOverrides property:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    // Your custom render passes go here
}

When internal passes are disabled, Qt Quick 3D will not perform any default rendering. This means you must:

  • Define at least one RenderPass to render your scene
  • Provide the final output texture to display via SimpleQuadRenderer or similar mechanism
  • Handle all rendering aspects including depth buffers, transparency, etc.

Note: Disabling internal passes gives you complete control, but also complete responsibility. Features like automatic shadow rendering, transparency sorting, and environment reflections must be implemented in your custom passes if needed.

Core Concepts

User-defined render passes are built from several key components:

RenderPass

The RenderPass type is the main building block. It defines a single rendering operation with a set of commands that control what gets rendered and how. Each pass can:

  • Render to one or more color textures (up to 4 simultaneous render targets)
  • Write depth and stencil information
  • Filter which objects to render based on layers
  • Override graphics pipeline state (blending, culling, etc.)
  • Use original materials, augment them with custom shaders, or override them entirely

RenderPassTexture

The RenderPassTexture type defines textures that serve as render targets. These can be color textures in various formats (RGBA8, RGBA16F, RGBA32F, etc.) or depth/stencil textures. Render pass textures are used as outputs from one pass and can be used as texture inputs to subsequent passes.

RenderOutputProvider

The RenderOutputProvider type connects render passes by exposing the output textures from one pass as texture inputs that can be used by materials or other passes. This is essential for multi-pass rendering where later passes need to read the results of earlier passes.

ContentLayer

The ContentLayer singleton provides layer constants (Layer0 through Layer23) used for filtering which objects render in which pass. By assigning objects to specific layers and using RenderablesFilter in your passes, you can control precisely what gets rendered in each pass.

Render Commands

Each RenderPass contains a list of commands that configure its behavior:

Material Modes

Each RenderPass has a materialMode property that controls how materials are handled during rendering. The three modes offer different levels of material control:

OriginalMaterial Mode

This mode renders objects with their assigned materials unchanged. It's useful when you want to control the rendering pipeline structure (multiple passes, custom render targets) but keep the material behavior standard.

RenderPass {
    materialMode: RenderPass.OriginalMaterial
    commands: [
        ColorAttachment { target: myColorTexture },
        DepthStencilAttachment { }
    ]
}

AugmentMaterial Mode

This mode injects custom shader code into the existing material pipeline. It's particularly useful for deferred rendering where you need to output additional data (like normals, positions, etc.) to multiple render targets while preserving the material's base behavior.

RenderPass {
    materialMode: RenderPass.AugmentMaterial
    augmentShader: "my_augment.glsl"
    commands: [
        ColorAttachment { target: gbuffer0; name: "GBUFFER0" },
        ColorAttachment { target: gbuffer1; name: "GBUFFER1" },
        DepthStencilAttachment { }
    ]
}

The augment shader file contains a MAIN_FRAGMENT_AUGMENT() function:

void MAIN_FRAGMENT_AUGMENT()
{
    // Access material properties
    vec3 color = BASE_COLOR.rgb;
    float metal = METALNESS;
    float rough = ROUGHNESS;
    vec3 normal = normalize(WORLD_NORMAL);

    // Write to multiple render targets
    GBUFFER0 = vec4(color, metal);
    GBUFFER1 = vec4(normal * 0.5 + 0.5, rough);
}

See Augment Shaders for Multiple Render Targets for more details.

OverrideMaterial Mode

This mode replaces all object materials with a single material. It's useful for specialized passes like shadow mapping, depth prepass, or debug visualization.

RenderPass {
    materialMode: RenderPass.OverrideMaterial
    overrideMaterial: CustomMaterial {
        fragmentShader: "depth_only.frag"
        // All objects will use this material
    }
    commands: [
        DepthTextureAttachment { target: depthTexture }
    ]
}

Render Pass Commands

Commands are specified in the commands property and execute in the order they are defined.

Color Attachment

The ColorAttachment command specifies a color render target. The name property defines how the attachment is accessed in augment shaders.

ColorAttachment {
    target: myTexture      // RenderPassTexture to render to
    name: "GBUFFER0"      // Name for shader access (optional)
}

You can have up to 4 color attachments per pass (for multiple render targets).

Depth Attachments

There are two ways to handle depth:

DepthStencilAttachment uses an implicit depth/stencil buffer:

DepthStencilAttachment { }  // Creates depth/stencil buffer automatically

DepthTextureAttachment uses an explicit texture for depth output:

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

DepthTextureAttachment {
    target: depthTex  // Explicit depth texture
}

Use DepthTextureAttachment when you need to share the depth buffer between multiple passes or use it as a texture input in shaders.

Renderables Filter

The RenderablesFilter command controls which objects are rendered based on their layer assignment and renderable type.

RenderablesFilter {
    layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
    renderableTypes: RenderablesFilter.Opaque
}

The layerMask property uses bitwise OR to combine layers. The renderableTypes property can be:

  • RenderablesFilter.Opaque: Render only opaque objects
  • RenderablesFilter.Transparent: Render only transparent objects
  • RenderablesFilter.Opaque | RenderablesFilter.Transparent: Render both

Pipeline State Override

The PipelineStateOverride command provides fine-grained control over graphics pipeline state. It can override depth testing, blending, culling, polygon mode, and more.

PipelineStateOverride {
    depthTestEnabled: true
    depthWriteEnabled: false
    blendEnabled: true
    cullMode: PipelineStateOverride.Back
    polygonMode: PipelineStateOverride.Fill
}

See Pipeline State Control for more examples.

Sub Render Pass

The SubRenderPass command allows hierarchical composition of render passes by executing another render pass within the current pass.

RenderPass {
    id: parentPass
    commands: [
        ColorAttachment { target: colorTex },
        SubRenderPass { renderPass: childPass },
        // More commands after child pass
    ]
}

RenderPass {
    id: childPass
    // This pass executes within parentPass
}

Add Define

The AddDefine command adds shader preprocessor defines that affect shader compilation.

AddDefine {
    name: "USE_SPECIAL_MODE"
    value: 1
}

Simple Example: Single Pass Rendering

Here's a minimal example showing user-defined render passes:

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

View3D {
    anchors.fill: parent
    renderOverrides: View3D.DisableInternalPasses

    // Camera and lights
    PerspectiveCamera { z: 300 }
    DirectionalLight { }

    // Define render target texture
    RenderPassTexture {
        id: colorTarget
        format: RenderPassTexture.RGBA16F
    }

    // Define the render pass
    RenderPass {
        id: mainPass
        materialMode: RenderPass.OriginalMaterial
        clearColor: "skyblue"

        commands: [
            ColorAttachment { target: colorTarget },
            DepthStencilAttachment { }
        ]
    }

    // Display the result
    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: mainPass
                attachmentSelector: RenderOutputProvider.Attachment0
            }
        }
    }

    // Scene content
    Model {
        source: "#Sphere"
        materials: PrincipledMaterial {
            baseColor: "red"
            metalness: 0.0
            roughness: 0.3
        }
    }
}

This example:

  1. Disables internal passes
  2. Creates a render target texture (colorTarget)
  3. Defines a render pass that renders to that texture
  4. Uses RenderOutputProvider to expose the texture
  5. Displays the result with SimpleQuadRenderer
  6. Renders a sphere with standard material

Working with Layers

The ContentLayer singleton provides constants for organizing objects into layers, allowing fine-grained control over what renders in each pass.

Layer Constants

Qt Quick 3D provides 24 user-assignable layers:

  • ContentLayer.Layer0 through ContentLayer.Layer23: Individual layers
  • ContentLayer.LayerAll: All user layers combined
  • ContentLayer.LayerNone: No layers

Note: Layers 24-31 are reserved for internal use.

Assigning Objects to Layers

Assign objects to layers using the layers property:

Model {
    source: "#Cube"
    layers: ContentLayer.Layer0
    materials: PrincipledMaterial { baseColor: "red" }
}

Model {
    source: "#Sphere"
    layers: ContentLayer.Layer1 | ContentLayer.Layer2
    materials: PrincipledMaterial { baseColor: "blue" }
}

Objects can belong to multiple layers using bitwise OR.

Filtering by Layer

Use RenderablesFilter in your render passes to select which layers to render:

RenderPass {
    id: pass1
    commands: [
        ColorAttachment { target: texture1 },
        RenderablesFilter {
            layerMask: ContentLayer.Layer0
        }
    ]
    // Only renders objects on Layer0 (red cube)
}

RenderPass {
    id: pass2
    commands: [
        ColorAttachment { target: texture2 },
        RenderablesFilter {
            layerMask: ContentLayer.Layer1 | ContentLayer.Layer2
        }
    ]
    // Only renders objects on Layer1 or Layer2 (blue sphere)
}

Use Case: Selective Rendering

A practical example is rendering certain objects as wireframe overlays:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    RenderPassTexture { id: colorTex; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: depthTex; format: RenderPassTexture.Depth24Stencil8 }

    // Pass 1: Render solid objects
    RenderPass {
        id: solidPass
        commands: [
            ColorAttachment { target: colorTex },
            DepthTextureAttachment { target: depthTex },
            RenderablesFilter { layerMask: ContentLayer.Layer0 }
        ]
    }

    // Pass 2: Render wireframe overlay
    RenderPass {
        id: wireframePass
        commands: [
            ColorAttachment { target: colorTex },
            DepthTextureAttachment { target: depthTex },
            PipelineStateOverride {
                polygonMode: PipelineStateOverride.Line
                depthTestEnabled: true
                depthWriteEnabled: false
            },
            RenderablesFilter { layerMask: ContentLayer.Layer1 }
        ]
    }

    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: wireframePass
                attachmentSelector: RenderOutputProvider.Attachment0
            }
        }
    }

    Model {
        source: "#Sphere"
        layers: ContentLayer.Layer0  // Rendered solid
        materials: PrincipledMaterial { baseColor: "blue" }
    }

    Model {
        source: "#Sphere"
        layers: ContentLayer.Layer1  // Rendered as wireframe
        materials: PrincipledMaterial { baseColor: "yellow" }
    }
}

Texture Management

User render passes rely heavily on textures both as render targets and as inputs to subsequent passes or materials.

Render Pass Texture Formats

RenderPassTexture supports various formats for different use cases:

Color Formats:

FormatDescriptionUse Case
RGBA88-bit per channelStandard color output, lower memory usage
RGBA16F16-bit floating pointHDR rendering, intermediate buffers
RGBA32F32-bit floating pointHigh precision calculations
R8, R16, R16F, R32FSingle channel variantsGrayscale data, specialized buffers

Depth Formats:

FormatDescription
Depth1616-bit depth
Depth2424-bit depth
Depth3232-bit depth
Depth24Stencil824-bit depth + 8-bit stencil

Example texture definitions:

RenderPassTexture {
    id: hdrColorBuffer
    format: RenderPassTexture.RGBA16F  // HDR color
}

RenderPassTexture {
    id: depthBuffer
    format: RenderPassTexture.Depth24Stencil8  // Depth + stencil
}

RenderPassTexture {
    id: normalBuffer
    format: RenderPassTexture.RGBA16F  // Store normals
}

Sharing Textures Between Passes

Multiple passes can share the same depth texture, allowing depth testing across passes:

RenderPassTexture {
    id: sharedDepth
    format: RenderPassTexture.Depth24Stencil8
}

RenderPass {
    id: geometryPass
    commands: [
        ColorAttachment { target: colorTex1 },
        DepthTextureAttachment { target: sharedDepth }
    ]
}

RenderPass {
    id: transparentPass
    renderTargetFlags: RenderPass.PreserveDepthStencilContents
    commands: [
        ColorAttachment { target: colorTex2 },
        DepthTextureAttachment { target: sharedDepth }
        // Uses depth from geometryPass for depth testing
    ]
}

Render Output Provider

The RenderOutputProvider exposes render pass outputs as Texture inputs:

// Define a render pass with color output
RenderPass {
    id: firstPass
    commands: [
        ColorAttachment { target: intermediateTexture }
    ]
}

// Expose its output
RenderOutputProvider {
    id: intermediateProvider
    textureSource: RenderOutputProvider.UserPassTexture
    renderPass: firstPass
    attachmentSelector: RenderOutputProvider.Attachment0
}

// Use in a material
CustomMaterial {
    property TextureInput inputTex: TextureInput {
        texture: Texture { textureProvider: intermediateProvider }
    }
    fragmentShader: "process.frag"
}

The attachmentSelector property specifies which color attachment to use when a pass has multiple render targets:

  • RenderOutputProvider.Attachment0: First color attachment
  • RenderOutputProvider.Attachment1: Second color attachment
  • RenderOutputProvider.Attachment2: Third color attachment
  • RenderOutputProvider.Attachment3: Fourth color attachment

Pass Chaining Example

Multiple passes can be chained together where each pass processes the output of the previous pass:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    // Intermediate textures
    RenderPassTexture { id: tex0; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: tex1; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: tex2; format: RenderPassTexture.RGBA16F }

    // Pass 1: Render scene
    RenderPass {
        id: scenePass
        commands: [
            ColorAttachment { target: tex0 },
            DepthStencilAttachment { }
        ]
    }

    // Pass 2: Process tex0 -> tex1
    RenderPass {
        id: process1
        materialMode: RenderPass.OriginalMaterial
        commands: [
            ColorAttachment { target: tex1 }
        ]
    }

    Model {
        layers: ContentLayer.Layer10
        geometry: PlaneGeometry { }
        materials: CustomMaterial {
            property TextureInput input: TextureInput {
                texture: Texture {
                    textureProvider: RenderOutputProvider {
                        textureSource: RenderOutputProvider.UserPassTexture
                        renderPass: scenePass
                    }
                }
            }
            fragmentShader: "effect1.frag"
        }
    }

    // Pass 3: Process tex1 -> tex2
    RenderPass {
        id: process2
        commands: [
            ColorAttachment { target: tex2 },
            RenderablesFilter { layerMask: ContentLayer.Layer11 }
        ]
    }

    Model {
        layers: ContentLayer.Layer11
        geometry: PlaneGeometry { }
        materials: CustomMaterial {
            property TextureInput input: TextureInput {
                texture: Texture {
                    textureProvider: RenderOutputProvider {
                        textureSource: RenderOutputProvider.UserPassTexture
                        renderPass: process1
                    }
                }
            }
            fragmentShader: "effect2.frag"
        }
    }

    // Display final result
    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: process2
            }
        }
    }
}

Pipeline State Control

The PipelineStateOverride command provides detailed control over graphics pipeline state, allowing customization of depth testing, blending, culling, and more.

Depth Testing and Writing

Control how depth values are tested and written:

PipelineStateOverride {
    depthTestEnabled: true
    depthWriteEnabled: true
    depthFunction: PipelineStateOverride.LessOrEqual
}

The depthFunction property can be:

  • Never, Less, Equal, LessOrEqual
  • Greater, NotEqual, GreaterOrEqual, Always

Blending

Enable and configure blending for transparency:

PipelineStateOverride {
    blendEnabled: true
    // Uses default blend mode (source alpha blending)
}

For multiple render targets, use per-target blend states. PipelineStateOverride exposes the per-attachment value-type properties targetBlend0 through targetBlend7 (of type renderTargetBlend) and the blend factor and operation enums are in the RenderTargetBlend namespace:

PipelineStateOverride {
    targetBlend0.enable: true
    targetBlend0.srcColor: RenderTargetBlend.SrcAlpha
    targetBlend0.dstColor: RenderTargetBlend.OneMinusSrcAlpha
    targetBlend0.opColor:  RenderTargetBlend.Add

    targetBlend1.enable: false  // No blending for attachment 1
}

Culling

Control face culling:

PipelineStateOverride {
    cullMode: PipelineStateOverride.Back   // Override material's cull mode to Back
}

Setting cullMode here overrides the value the material would otherwise specify for this pass. Options: None (no culling), Front (cull front faces), Back (cull back faces).

Wireframe Rendering

Render geometry as wireframes:

PipelineStateOverride {
    polygonMode: PipelineStateOverride.Line
    cullMode: PipelineStateOverride.None  // Show both sides
}

The polygonMode can be Fill (default) or Line (wireframe).

Scissor Rectangles

Limit rendering to a rectangular region:

PipelineStateOverride {
    usesScissor: true
    scissor: Qt.rect(100, 100, 400, 300)  // x, y, width, height
}

Viewport Control

Override the viewport:

PipelineStateOverride {
    viewport: Qt.rect(0, 0, 800, 600)
}

Complete Example: Wireframe Overlay

This example renders a scene normally, then overlays a wireframe version:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    RenderPassTexture { id: color; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: depth; format: RenderPassTexture.Depth24Stencil8 }

    // Solid pass
    RenderPass {
        id: solidPass
        clearColor: "black"
        commands: [
            ColorAttachment { target: color },
            DepthTextureAttachment { target: depth },
            RenderablesFilter {
                layerMask: ContentLayer.Layer0
                renderableTypes: RenderablesFilter.Opaque
            }
        ]
    }

    // Wireframe overlay
    RenderPass {
        id: wirePass
        renderTargetFlags: RenderPass.PreserveColorContents |
                          RenderPass.PreserveDepthStencilContents
        commands: [
            ColorAttachment { target: color },
            DepthTextureAttachment { target: depth },
            PipelineStateOverride {
                polygonMode: PipelineStateOverride.Line
                depthTestEnabled: true
                depthWriteEnabled: false
                blendEnabled: true
            },
            RenderablesFilter { layerMask: ContentLayer.Layer0 }
        ]
    }

    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: wirePass
            }
        }
    }

    PerspectiveCamera { z: 300 }
    DirectionalLight { }

    Model {
        source: "#Sphere"
        layers: ContentLayer.Layer0
        materials: PrincipledMaterial {
            baseColor: "blue"
            metalness: 0.5
            roughness: 0.3
        }
    }
}

Augment Shaders for Multiple Render Targets

When using RenderPass.AugmentMaterial mode, you provide an augment shader that injects custom code into the material's fragment shader. This is particularly useful for deferred rendering where you need to output material data to multiple render targets (MRT).

The MAIN_FRAGMENT_AUGMENT Function

Your augment shader file must define a MAIN_FRAGMENT_AUGMENT() function:

void MAIN_FRAGMENT_AUGMENT()
{
    // Your custom shader code here
}

This function is called during the fragment shader after material calculations are complete, giving you access to material properties and the ability to write to custom outputs.

Available Built-in Variables

Inside MAIN_FRAGMENT_AUGMENT(), the engine substitutes a set of macros that expose material pipeline data. Only the macros listed below are part of the augment shader API:

MacroTypeDescription
BASE_COLORvec4Material base color (linear color space, after material processing).
METALNESSfloatMaterial metalness in the range 0.0 to 1.0.
ROUGHNESSfloatMaterial roughness in the range 0.0 to 1.0.
WORLD_NORMALvec3World-space surface normal (post normal-mapping).
WORLD_TANGENTvec3World-space tangent vector.
WORLD_BINORMALvec3World-space binormal vector.
DIFFUSE_LIGHTvec3Accumulated diffuse light contribution.
SPECULAR_LIGHTvec3Accumulated specular light contribution.
EMISSIVE_LIGHTvec3Material emissive contribution.
F0vec3Fresnel reflectance at normal incidence.
F90vec3Fresnel reflectance at grazing incidence.

Note: Some examples (including the deferred-rendering one below) read the world-space fragment position via qt_varWorldPos. This is the engine's underlying varying name, not an augment-shader macro, and falls into the same semi-public category as the .glsllib files described in Built-in Shader Facilities: it works in practice but is not guaranteed to remain stable across releases.

Writing to Named Outputs

Color attachments in your render pass can have names that correspond to output variables in your shader:

RenderPass {
    commands: [
        ColorAttachment { target: tex0; name: "GBUFFER0" },
        ColorAttachment { target: tex1; name: "GBUFFER1" },
        ColorAttachment { target: tex2; name: "GBUFFER2" }
    ]
}

In your augment shader:

void MAIN_FRAGMENT_AUGMENT()
{
    GBUFFER0 = vec4(...);  // Writes to first attachment
    GBUFFER1 = vec4(...);  // Writes to second attachment
    GBUFFER2 = vec4(...);  // Writes to third attachment
}

Qt Quick 3D supports up to 4 simultaneous color attachments (GBUFFER0 through GBUFFER3).

Complete Augment Shader Example

Here's a complete example for a deferred rendering G-buffer pass:

// gbuffer_augment.glsl
void MAIN_FRAGMENT_AUGMENT()
{
    // Get material properties
    vec3 baseColor = BASE_COLOR.rgb;
    float metalness = METALNESS;
    float roughness = ROUGHNESS;
    vec3 worldNormal = normalize(WORLD_NORMAL);
    vec3 worldPos = qt_varWorldPos;

    // GBuffer 0: Albedo (RGB) + Metalness (A)
    GBUFFER0 = vec4(baseColor, metalness);

    // GBuffer 1: World Normal (RGB) + Roughness (A)
    // Encode normal from [-1,1] to [0,1] for storage
    GBUFFER1 = vec4(worldNormal * 0.5 + 0.5, roughness);

    // GBuffer 2: World Position
    GBUFFER2 = vec4(worldPos, 1.0);
}

Used with a render pass (RenderPassTexture instances are declared as direct children of the enclosing View3D and referenced by id):

View3D {
    RenderPassTexture { id: gbuffer0; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer1; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer2; format: RenderPassTexture.RGBA16F }

    RenderPass {
        id: gbufferPass
        materialMode: RenderPass.AugmentMaterial
        augmentShader: "gbuffer_augment.glsl"
        commands: [
            ColorAttachment { target: gbuffer0; name: "GBUFFER0" },
            ColorAttachment { target: gbuffer1; name: "GBUFFER1" },
            ColorAttachment { target: gbuffer2; name: "GBUFFER2" },
            DepthStencilAttachment { }
        ]
    }
}

Preserving Material Behavior

The augment shader runs in addition to the normal material pipeline, not instead of it. This means:

  • The material's textures, properties, and calculations still occur
  • You're adding extra outputs, not replacing the primary color output
  • The original material's RGBA output is still written to the first color attachment unless overridden

If you only need to output custom data and don't need the standard material calculations, consider using RenderPass.OverrideMaterial instead.

When to Use Augment vs Override

Use AugmentMaterial when:

  • You need material properties (metalness, roughness, normals) for G-buffers
  • You want to leverage existing material features like texture mapping
  • You need multiple render targets with material data

Use OverrideMaterial when:

  • You need identical behavior for all objects (depth prepass, shadow maps)
  • You don't need per-material properties
  • You want maximum performance by bypassing material calculations

Built-in Shader Facilities

Augment shaders and custom materials used in render passes have access to Qt Quick 3D's built-in shader infrastructure through an include system. These shader library files (.glsllib) provide functionality for lighting, shadows, tonemapping, and more.

Include Syntax:

Use \#include directives in your shader code to import functionality:

#include "tonemapping.glsllib"
#include "lightsData.glsllib"
#include "shadowMapping.glsllib"
#include "funcprocessPunctualLighting.glsllib"

void MAIN()
{
    // Use included functions
    vec3 color = qt_tonemap(hdrColor);
}

Available Shader Libraries:

These shader libraries are located in the engine at src/runtimerender/res/effectlib/ and are considered semi-public: they are usable in user shaders but do not have the same binary compatibility guarantees as the rest of Qt's public API.

LibraryDescription
tonemapping.glsllibProvides qt_tonemap() function for HDR to LDR conversion using the built-in tonemapping algorithm
lightsData.glsllibContains scene lighting information (light positions, colors, directions, etc.) that can be accessed in custom lighting calculations
shadowMapping.glsllibProvides functions for sampling from the default shadow maps, enabling custom materials to receive shadows
funcprocessPunctualLighting.glsllibContains qt_processPunctualLighting() and other built-in lighting functions for standard PBR lighting calculations
sampleProbe.glsllibFunctions for sampling image-based lighting probes (qt_sampleDiffuse(), qt_sampleGlossyPrincipled())

Example: Custom Lighting with Built-in Functions

Here's an example fragment shader using the built-in lighting facilities:

#include "lightsData.glsllib"
#include "funcprocessPunctualLighting.glsllib"
#include "tonemapping.glsllib"
#include "sampleProbe.glsllib"

void MAIN()
{
    vec3 worldPos = ...; // From G-buffer or varying
    vec3 normal = ...;
    vec3 viewDir = normalize(CAMERA_POSITION - worldPos);
    vec3 baseColor = ...;
    float roughness = ...;
    float metalness = ...;

    vec3 F0 = mix(vec3(0.04), baseColor, metalness);

    vec3 diffuseAccum = vec3(0.0);
    vec3 specAccum = vec3(0.0);

    // Use built-in punctual lighting (directional, point, spot lights)
    qt_processPunctualLighting(diffuseAccum,
                               specAccum,
                               baseColor,
                               worldPos,
                               normal,
                               viewDir,
                               vec3(1.0), // specularAmount
                               vec3(1.0), // specularTint
                               roughness,
                               metalness,
                               F0,
                               vec3(1.0)); // F90

    // Add image-based lighting
    vec4 probeDiffuse = vec4(baseColor, 1.0) * qt_sampleDiffuse(normal);
    vec4 probeSpecular = qt_sampleGlossyPrincipled(normal, viewDir, F0, roughness);
    diffuseAccum += probeDiffuse.rgb;
    specAccum += probeSpecular.rgb;

    vec3 color = diffuseAccum + specAccum;

    // Apply tonemapping
    FRAGCOLOR = vec4(qt_tonemap(color), 1.0);
}

Note: These shader libraries are semi-public API. While they are stable and intended for use in custom shaders, Qt does not guarantee that their internal implementation or available functions will remain unchanged between releases. However, commonly used functions like qt_tonemap() and qt_processPunctualLighting() are unlikely to change significantly.

Advanced Example: Deferred Rendering

Deferred rendering is a technique where geometry information is rendered to multiple textures (called a G-buffer or geometry buffer) in a first pass, and lighting calculations are performed in a second pass using the stored geometry data. This approach is particularly efficient when you have many lights, as each pixel is shaded only once regardless of the number of lights.

Deferred Rendering Architecture

The deferred rendering pipeline consists of two main passes:

  1. Geometry Pass (G-Buffer Pass): Renders scene geometry to multiple render targets, storing material properties such as albedo, normals, roughness, metalness, and world position.
  2. Lighting Pass: Renders a full-screen quad that samples the G-buffers and performs lighting calculations for each pixel based on the stored geometry data.

G-Buffer Pass Implementation

First, define a G-buffer pass that outputs material data to multiple render targets:

GBufferPass.qml: The G-buffer render targets are exposed as required properties so the enclosing View3D supplies them and can read their outputs:

import QtQuick
import QtQuick3D

RenderPass {
    id: gbufferPass
    clearColor: Qt.rgba(0.0, 0.0, 0.0, 0.0)

    property alias layerMask: filter.layerMask

    // Provided by the View3D that uses this pass
    required property RenderPassTexture gbuffer0   // rgb: baseColor, a: metalness
    required property RenderPassTexture gbuffer1   // rgb: normal,    a: roughness
    required property RenderPassTexture gbuffer2   // rgb: world pos, a: spare
    required property RenderPassTexture depthTexture

    materialMode: RenderPass.AugmentMaterial
    augmentShader: "gbuffer_augment.glsl"

    commands: [
        ColorAttachment { target: gbufferPass.gbuffer0; name: "GBUFFER0" },
        ColorAttachment { target: gbufferPass.gbuffer1; name: "GBUFFER1" },
        ColorAttachment { target: gbufferPass.gbuffer2; name: "GBUFFER2" },
        DepthTextureAttachment { target: gbufferPass.depthTexture },
        RenderablesFilter {
            id: filter
            renderableTypes: RenderablesFilter.Opaque
        }
    ]
}

gbuffer_augment.glsl:

void MAIN_FRAGMENT_AUGMENT()
{
    vec3 baseColor   = BASE_COLOR.rgb;
    float metalness  = METALNESS;
    float roughness  = ROUGHNESS;
    vec3 worldNormal = normalize(WORLD_NORMAL);

    // GBuffer 0: albedo + metalness
    GBUFFER0 = vec4(baseColor, metalness);

    // GBuffer 1: normal (encoded to 0..1) + roughness
    GBUFFER1 = vec4(worldNormal * 0.5 + 0.5, roughness);

    // GBuffer 2: world position
    GBUFFER2 = vec4(qt_varWorldPos, 1.0);
}

The augment shader accesses material properties computed by the material pipeline and writes them to the three G-buffer attachments. Normals are encoded from [-1,1] to [0,1] range for storage.

Lighting Pass Implementation

The lighting pass renders a full-screen quad that samples the G-buffers and computes lighting:

// Lighting pass model (full-screen quad)
Model {
    id: deferredLightingQuad
    layers: ContentLayer.Layer13  // Dedicated layer for lighting quad

    geometry: PlaneGeometry {
        plane: PlaneGeometry.XY  // Quad in screen space
    }

    materials: CustomMaterial {
        // Texture inputs for G-buffers
        property TextureInput gbuffer0: TextureInput {
            enabled: true
            texture: Texture { textureProvider: gbuffer0Provider }
        }
        property TextureInput gbuffer1: TextureInput {
            enabled: true
            texture: Texture { textureProvider: gbuffer1Provider }
        }
        property TextureInput gbuffer2: TextureInput {
            enabled: true
            texture: Texture { textureProvider: gbuffer2Provider }
        }

        shadingMode: CustomMaterial.Unshaded
        fragmentShader: "lighting.frag"
        vertexShader: "lighting.vert"
    }
}

// Lighting pass renders the quad to main output
RenderPass {
    id: deferredLightingPass
    materialMode: RenderPass.OriginalMaterial

    commands: [
        ColorAttachment { target: mainColorTexture },
        DepthStencilAttachment { },
        RenderablesFilter { layerMask: ContentLayer.Layer13 }
    ]
}

The lighting vertex shader (lighting.vert) creates a full-screen quad in normalized device coordinates, and the fragment shader (lighting.frag) samples the G-buffers and performs lighting calculations.

Complete Integration

Here's how to integrate both passes in a View3D:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    // Main output texture
    RenderPassTexture { id: mainColorTexture; format: RenderPassTexture.RGBA16F }

    // Shared depth texture
    RenderPassTexture { id: mainDepthStencilTexture; format: RenderPassTexture.Depth24Stencil8 }

    // G-buffer render targets
    RenderPassTexture { id: gbuffer0Tex; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer1Tex; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer2Tex; format: RenderPassTexture.RGBA16F }

    // G-buffer pass
    GBufferPass {
        id: gbufferPass
        layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
        gbuffer0: gbuffer0Tex
        gbuffer1: gbuffer1Tex
        gbuffer2: gbuffer2Tex
        depthTexture: mainDepthStencilTexture
    }

    // Expose G-buffer outputs
    RenderOutputProvider {
        id: gbuffer0Provider
        textureSource: RenderOutputProvider.UserPassTexture
        renderPass: gbufferPass
        attachmentSelector: RenderOutputProvider.Attachment0
    }

    RenderOutputProvider {
        id: gbuffer1Provider
        textureSource: RenderOutputProvider.UserPassTexture
        renderPass: gbufferPass
        attachmentSelector: RenderOutputProvider.Attachment1
    }

    RenderOutputProvider {
        id: gbuffer2Provider
        textureSource: RenderOutputProvider.UserPassTexture
        renderPass: gbufferPass
        attachmentSelector: RenderOutputProvider.Attachment2
    }

    // Lighting pass (defined above)

    // Display final result
    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: deferredLightingPass
                attachmentSelector: RenderOutputProvider.Attachment0
            }
        }
    }

    // Scene objects
    Model {
        layers: ContentLayer.Layer0
        source: "#Sphere"
        materials: PrincipledMaterial {
            baseColor: "red"
            metalness: 0.5
            roughness: 0.3
        }
    }

    // Camera and lights
    PerspectiveCamera { z: 300 }
    DirectionalLight { eulerRotation.x: -45 }
}

Rendering Flow

The rendering proceeds as follows:

  1. G-buffer Pass: Scene objects on Layer0 and Layer1 are rendered. For each object, the augment shader writes albedo, normals, roughness, metalness, and position to three color attachments. Depth is written to the shared depth texture.
  2. Lighting Pass: The full-screen quad on Layer13 is rendered. Its custom material samples the three G-buffer textures and the depth texture, reconstructs the scene information, and performs lighting calculations (directional lights, image-based lighting, etc.) to produce the final lit color.
  3. Display: The result of the lighting pass is displayed via SimpleQuadRenderer.

Advantages and Limitations

Advantages:

  • Efficient with many lights (each pixel lit once)
  • Lighting complexity independent of scene complexity
  • Easy to implement screen-space effects
  • Decouples geometry and lighting passes

Limitations:

  • Higher memory bandwidth due to G-buffer reads/writes
  • No hardware MSAA (requires alternative anti-aliasing)
  • Transparency requires separate forward pass
  • More complex pipeline to set up and maintain

For a complete working example, see Qt Quick 3D - User Passes Example.

Complete Rendering Pipeline Example

A realistic custom rendering pipeline often needs to combine multiple pass types: opaque geometry, skybox background, 2D overlays, and transparent objects. Here's a complete example showing how to organize these using SubRenderPass for hierarchical composition:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    environment: SceneEnvironment {
        backgroundMode: SceneEnvironment.SkyBox
        lightProbe: Texture {
            textureData: ProceduralSkyTextureData { }
        }
    }

    RenderPassTexture {
        id: mainColorTexture
        format: RenderPassTexture.RGBA16F
    }

    RenderPassTexture {
        id: mainDepthStencilTexture
        format: RenderPassTexture.Depth24Stencil8
    }

    // Main pass orchestrates the complete render path:
    // 1. Opaque geometry (deferred)
    // 2. Skybox background
    // 3. 2D UI overlays
    // 4. Transparent objects (forward)
    RenderPass {
        id: mainColorPass
        clearColor: "black"
        renderTargetFlags: RenderPass.PreserveDepthStencilContents

        commands: [
            ColorAttachment { target: mainColorTexture },
            DepthTextureAttachment { target: mainDepthStencilTexture },
            RenderablesFilter {
                renderableTypes: RenderablesFilter.None  // Parent doesn't render
            },

            // Sub-pass 1: Deferred lighting
            SubRenderPass {
                renderPass: RenderPass {
                    id: deferredLightingPass
                    materialMode: RenderPass.OriginalMaterial
                    commands: [
                        PipelineStateOverride {
                            depthWriteEnabled: false
                            depthTestEnabled: false
                        },
                        RenderablesFilter { layerMask: ContentLayer.Layer13 }
                    ]
                }
            },

            // Sub-pass 2: Skybox (behind everything)
            SubRenderPass {
                renderPass: RenderPass {
                    passMode: RenderPass.SkyboxPass
                    commands: [
                        PipelineStateOverride {
                            depthTestEnabled: true
                            depthWriteEnabled: false
                        }
                    ]
                }
            },

            // Sub-pass 3: 2D Qt Quick content
            SubRenderPass {
                renderPass: RenderPass {
                    passMode: RenderPass.Item2DPass
                }
            },

            // Sub-pass 4: Transparent objects (forward rendering)
            SubRenderPass {
                renderPass: RenderPass {
                    materialMode: RenderPass.OriginalMaterial
                    commands: [
                        RenderablesFilter {
                            renderableTypes: RenderablesFilter.Transparent
                            layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
                        },
                        PipelineStateOverride {
                            blendEnabled: true
                            depthTestEnabled: true
                            depthWriteEnabled: false
                        }
                    ]
                }
            }
        ]
    }

    // G-buffer pass (referenced by deferred lighting)
    GBufferPass {
        id: gbufferPass
        layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
        depthTexture: mainDepthStencilTexture
    }

    // Full-screen quad for deferred lighting
    Model {
        layers: ContentLayer.Layer13
        geometry: PlaneGeometry { plane: PlaneGeometry.XY }
        materials: CustomMaterial {
            property TextureInput gbuffer0: TextureInput {
                texture: Texture {
                    textureProvider: RenderOutputProvider {
                        textureSource: RenderOutputProvider.UserPassTexture
                        renderPass: gbufferPass
                        attachmentSelector: RenderOutputProvider.Attachment0
                    }
                }
            }
            // ... other G-buffer inputs
            shadingMode: CustomMaterial.Unshaded
            fragmentShader: "lighting.frag"
            vertexShader: "lighting.vert"
        }
    }

    // Display final result
    SimpleQuadRenderer {
        texture: Texture {
            textureProvider: RenderOutputProvider {
                textureSource: RenderOutputProvider.UserPassTexture
                renderPass: mainColorPass
            }
        }
    }

    // Opaque 3D content
    Model {
        layers: ContentLayer.Layer0
        source: "#Sphere"
        materials: PrincipledMaterial { baseColor: "red" }
    }

    // Transparent 3D content
    Model {
        layers: ContentLayer.Layer1
        source: "#Cone"
        materials: PrincipledMaterial {
            baseColor: Qt.rgba(0.0, 1.0, 0.0, 0.5)
            alphaMode: PrincipledMaterial.Blend
        }
    }

    // 2D content in 3D space
    Node {
        x: -200
        y: 100
        Item {
            Button { text: "Click Me!" }
            Rectangle {
                color: "blue"
                width: 50; height: 50
            }
        }
    }

    PerspectiveCamera { z: 300 }
    DirectionalLight { eulerRotation.x: -45 }
}

Key Concepts

SubRenderPass for Hierarchical Organization:

The main pass uses SubRenderPass commands to execute child passes in sequence. The parent pass itself doesn't render anything (RenderablesFilter.None), it just orchestrates the child passes. This provides a clear structure and allows depth buffer sharing across all sub-passes.

Pass Modes:

  • RenderPass.UserPass (default): Custom rendering with your geometry and materials
  • RenderPass.SkyboxPass: Renders the environment skybox from SceneEnvironment
  • RenderPass.Item2DPass: Renders 2D Qt Quick content embedded in the 3D scene

Render Order:

  1. Opaque geometry rendered to G-buffer
  2. Deferred lighting applied to full-screen quad
  3. Skybox rendered behind geometry (depth test enabled, depth write disabled)
  4. 2D UI overlays rendered on top
  5. Transparent objects rendered last with blending

Depth Buffer Sharing:

The mainDepthStencilTexture is shared across passes using PreserveDepthStencilContents. This ensures skybox renders behind geometry and transparent objects test against opaque geometry correctly.

Item2D Content:

Qt Quick 2D items (Button, Rectangle, Text, etc.) placed in Node objects are rendered by RenderPass.Item2DPass. These items are positioned in 3D space but rendered as 2D overlays that can receive mouse/touch input.

Performance Considerations

User-defined render passes provide maximum control but require careful consideration of performance implications.

Texture Format Choices

Choose texture formats based on your needs:

FormatSize per PixelUse Case
RGBA84 bytesFinal output, simple color buffers
RGBA16F8 bytesHDR content, intermediate buffers, normals
RGBA32F16 bytesHigh precision calculations, positions
R16F2 bytesSingle-channel HDR (depth, AO, etc.)

For a 1920x1080 framebuffer:

  • RGBA8: ~8 MB
  • RGBA16F: ~16 MB
  • RGBA32F: ~32 MB
  • Three RGBA16F G-buffers: ~48 MB

Recommendation: Use RGBA16F for intermediate buffers and RGBA8 or R8 where sufficient.

Deferred vs Forward Rendering

AspectDeferred RenderingForward Rendering
Many lightsEfficient (O(lights + pixels))Expensive (O(lights * objects))
Memory bandwidthHigh (G-buffer reads/writes)Lower
TransparencyRequires separate passNatural support
MSAANot directly supportedHardware MSAA works
Setup complexityMore complexSimpler

Use deferred rendering when:

  • You have many lights (10+)
  • Screen-space effects are important
  • Scene is mostly opaque

Use forward rendering when:

  • Few lights (<5)
  • Lots of transparency
  • Memory bandwidth is constrained
  • Simpler pipeline is preferable

Pass Ordering Optimization

Render passes execute in the order they are encountered. Optimize by:

  • Rendering opaque geometry before transparent geometry
  • Using depth prepass when beneficial (reduces overdraw in complex scenes)
  • Minimizing render target switches
  • Reusing depth buffers across passes when possible

Render Target Flags

The renderTargetFlags property controls memory behavior:

RenderPass {
    // First pass: write new content
    renderTargetFlags: 0  // Default: clear render targets
}

RenderPass {
    // Second pass: add to existing content
    renderTargetFlags: RenderPass.PreserveColorContents |
                      RenderPass.PreserveDepthStencilContents
}

RenderPass {
    // Depth not needed after this pass
    renderTargetFlags: RenderPass.DoNotStoreDepthStencilContents
}

PreserveColorContents/PreserveDepthStencilContents: Keep existing data (e.g., for multi-pass rendering to same target).

DoNotStoreDepthStencilContents: Hint that depth/stencil can be discarded (on some hardware this can improve performance by avoiding memory writes).

When to Use User Passes

Consider using user render passes when:

  • Implementing deferred rendering
  • Creating complex multi-pass effects
  • Needing explicit control over render order
  • Implementing custom rendering techniques
  • Building screen-space effects that need geometry data

Avoid user passes when:

  • Default rendering meets your needs
  • You only need post-processing (use Effect instead)
  • You only need custom material shaders (use CustomMaterial instead)
  • Performance is critical and extra passes would be wasteful

Common Patterns and Use Cases

User render passes enable many advanced rendering techniques. Here are some common patterns:

Deferred Shading/Lighting

Store geometry attributes in G-buffers and perform lighting in screen space. Covered in detail in Advanced Example: Deferred Rendering.

Edge Detection and Outlining

Render scene normally, then apply edge detection in a second pass:

// Pass 1: Render scene with normals/depth
RenderPass {
    id: scenePass
    commands: [
        ColorAttachment { target: colorTex },
        DepthTextureAttachment { target: depthTex }
    ]
}

// Pass 2: Edge detection using depth discontinuities
RenderPass {
    id: edgePass
    commands: [
        ColorAttachment { target: outlineTex },
        RenderablesFilter { layerMask: ContentLayer.Layer10 }
    ]
}

Model {
    layers: ContentLayer.Layer10
    geometry: PlaneGeometry { }
    materials: CustomMaterial {
        property TextureInput depthInput: TextureInput {
            // depthProvider is the id of a RenderOutputProvider that exposes
            // depthTex as a sampleable texture (see "Render Output Provider"
            // earlier in this page).
            texture: Texture { textureProvider: depthProvider }
        }
        fragmentShader: "edge_detect.frag"
        // Shader samples depth, computes gradients, draws edges
    }
}

Custom Post-Processing Chains

Chain multiple post-processing effects together by feeding each pass's output into the next via RenderOutputProvider. See Pass Chaining Example for a worked example of a multi-stage chain (scenePassprocess1process2 → display).

Selective Wireframe Overlay

Render some objects solid and others as wireframe overlays. See the example in Working with Layers.

Debug Visualization

Create debug passes that visualize normals, depth, or other data:

// Normal visualization pass
RenderPass {
    materialMode: RenderPass.OverrideMaterial
    overrideMaterial: CustomMaterial {
        fragmentShader: "debug_normals.frag"
        // FRAGCOLOR = vec4(normalize(NORMAL) * 0.5 + 0.5, 1.0);
    }
    commands: [
        ColorAttachment { target: debugTex }
    ]
}

Custom Shadow Mapping

Implement custom shadow mapping with explicit control:

// Shadow map pass (render from light's perspective)
RenderPass {
    id: shadowPass
    materialMode: RenderPass.OverrideMaterial
    overrideMaterial: CustomMaterial {
        fragmentShader: "depth_only.frag"
    }
    commands: [
        DepthTextureAttachment { target: shadowMapTex }
    ]
}

// Main pass using shadow map
RenderPass {
    id: mainPass
    commands: [
        ColorAttachment { target: colorTex },
        DepthStencilAttachment { }
    ]
}

// Materials in main pass sample shadowMapTex for shadow testing

Important Considerations

When working with user render passes, keep these important points in mind:

Shadow Handling

User render passes do not automatically include Qt Quick 3D's internal shadow rendering. If you disable internal passes and need shadows, you must:

  • Implement your own shadow mapping passes
  • Render the scene from light's perspective to a depth texture
  • Sample the shadow map in your lighting calculations

Alternatively, for simpler cases, you may not need shadows or can use pre-baked shadow maps.

HDR and Tonemapping

When using floating-point render targets (RGBA16F, RGBA32F), your rendering is in linear HDR space. You must apply tonemapping in your final display pass to convert to displayable LDR:

CustomMaterial {
    fragmentShader: "tonemap.frag"
}
// In tonemap.frag
vec3 tonemap(vec3 hdr) {
    // Simple Reinhard tonemapping
    return hdr / (hdr + vec3(1.0));
}

void MAIN() {
    vec3 hdrColor = texture(hdrInput, UV0).rgb;
    FRAGCOLOR = vec4(tonemap(hdrColor), 1.0);
}

Qt Quick 3D provides qt_tonemap() function in shaders that can be used for this purpose.

Depth Texture Generation

When you need depth as a texture (for effects like depth-of-field), use DepthTextureAttachment instead of DepthStencilAttachment:

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

RenderPass {
    commands: [
        ColorAttachment { target: colorTex },
        DepthTextureAttachment { target: depthTex }
        // depthTex can now be sampled in shaders
    ]
}

Clear Values and Render Target Management

Each pass can specify clear values:

RenderPass {
    clearColor: Qt.rgba(0.0, 0.0, 0.0, 0.0)
    depthClearValue: 1.0
    stencilClearValue: 0
}

When a pass doesn't preserve contents (default), render targets are cleared to these values before rendering. When PreserveColorContents or PreserveDepthStencilContents is set, existing content is kept and clear values are ignored.

Layer Organization Best Practices

Organize your layers logically:

  • Layer0-2: Main scene objects
  • Layer3-4: Transparent objects (separate pass)
  • Layer10+: Full-screen quads for post-processing
  • Layer15+: UI/debug overlays

Document your layer usage in comments and be consistent across your application.

Shader Compatibility

Augment shaders and custom materials used in render passes must be compatible with Qt Quick 3D's shader infrastructure:

  • Use GLSL-compatible syntax
  • Include appropriate \#include directives for Qt Quick 3D functions
  • Be aware of available built-in variables
  • Test on all target platforms (OpenGL, Vulkan, Metal, D3D)

Multiple Render Targets Limitations

When using multiple render targets (MRT):

  • Maximum of 4 color attachments
  • All attachments must have the same dimensions
  • Blend state can be specified per-attachment via PipelineStateOverride
  • Not all hardware supports MRT (check device capabilities)

Transparency

Transparent objects are challenging with deferred rendering. Common approaches:

  • Forward pass for transparent: Render opaque geometry deferred, transparent geometry in a separate forward pass
  • Separate transparent G-buffer: Render transparent geometry to a separate set of G-buffers with blending enabled
  • Weighted blended order-independent transparency: Use specialized OIT techniques

For most cases, a separate forward pass for transparent objects is simplest.

API Reference

The following table provides a complete reference of all types related to user render passes:

FeatureQML TypeDescription
Main render pass definitionRenderPassDefines a rendering pass with commands and material mode
Render target texturesRenderPassTextureTexture used as render target (color or depth/stencil)
Texture output providerRenderOutputProviderExposes render pass output as texture input
Layer constantsContentLayerSingleton providing layer filtering constants
Color outputColorAttachmentSpecifies a color render target
Depth/stencil (default)DepthStencilAttachmentUses implicit depth/stencil buffer
Depth/stencil (texture)DepthTextureAttachmentUses explicit depth texture
Object filteringRenderablesFilterFilters objects by layer and type
Pipeline statePipelineStateOverrideOverrides graphics pipeline state
Nested pass executionSubRenderPassExecutes another render pass
Shader definesAddDefineAdds shader preprocessor define
Per-target blend staterenderTargetBlendBlend configuration for MRT
Display helperSimpleQuadRendererRenders final output to View3D
TypeDescription
CustomMaterialCustom material with vertex/fragment shaders
EffectPost-processing effects
View3D3D view with renderOverrides property
Model3D model with layers property

Examples

See Also

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.