- Qt 6.11
- Qt Quick 3D
- User-Defined Render Passes in Qt Quick 3D
- Qt Quick 3D | Commercial or GPLv3

Programmable Materials, Effects, Geometry, and Texture data ◦ Balsam Asset Import Tool

# 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:

Level | Scope | Use Cases
--- | --- | ---
Effect | Post-processing | Applies effects after the scene is rendered (blur, color grading, etc.)
CustomMaterial | Per-material shaders | Custom vertex and fragment shaders for individual materials
RenderPass (User Render Passes) | Complete pipeline control | Deferred 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:

- ColorAttachment: Specifies a color render target
- DepthStencilAttachment: Specifies depth/stencil handling
- DepthTextureAttachment: Uses a texture for depth output
- RenderablesFilter: Filters objects by layer and type (opaque/transparent)
- PipelineStateOverride: Controls graphics pipeline state
- `SubRenderPass`: Executes another render pass within this pass
- AddDefine: Adds shader preprocessor defines

## 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:

Format | Description | Use Case
--- | --- | ---
RGBA8 | 8-bit per channel | Standard color output, lower memory usage
RGBA16F | 16-bit floating point | HDR rendering, intermediate buffers
RGBA32F | 32-bit floating point | High precision calculations
R8, R16, R16F, R32F | Single channel variants | Grayscale data, specialized buffers

Depth Formats:

Format | Description
--- | ---
Depth16 | 16-bit depth
Depth24 | 24-bit depth
Depth32 | 32-bit depth
Depth24Stencil8 | 24-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:

Macro | Type | Description
--- | --- | ---
`BASE_COLOR` | vec4 | Material base color (linear color space, after material processing).
`METALNESS` | float | Material metalness in the range 0.0 to 1.0.
`ROUGHNESS` | float | Material roughness in the range 0.0 to 1.0.
`WORLD_NORMAL` | vec3 | World-space surface normal (post normal-mapping).
`WORLD_TANGENT` | vec3 | World-space tangent vector.
`WORLD_BINORMAL` | vec3 | World-space binormal vector.
`DIFFUSE_LIGHT` | vec3 | Accumulated diffuse light contribution.
`SPECULAR_LIGHT` | vec3 | Accumulated specular light contribution.
`EMISSIVE_LIGHT` | vec3 | Material emissive contribution.
`F0` | vec3 | Fresnel reflectance at normal incidence.
`F90` | vec3 | Fresnel 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.

Library | Description
--- | ---
`tonemapping.glsllib` | Provides `qt_tonemap()` function for HDR to LDR conversion using the built-in tonemapping algorithm
`lightsData.glsllib` | Contains scene lighting information (light positions, colors, directions, etc.) that can be accessed in custom lighting calculations
`shadowMapping.glsllib` | Provides functions for sampling from the default shadow maps, enabling custom materials to receive shadows
`funcprocessPunctualLighting.glsllib` | Contains `qt_processPunctualLighting()` and other built-in lighting functions for standard PBR lighting calculations
`sampleProbe.glsllib` | Functions 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:

Format | Size per Pixel | Use Case
--- | --- | ---
RGBA8 | 4 bytes | Final output, simple color buffers
RGBA16F | 8 bytes | HDR content, intermediate buffers, normals
RGBA32F | 16 bytes | High precision calculations, positions
R16F | 2 bytes | Single-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

Aspect | Deferred Rendering | Forward Rendering
--- | --- | ---
Many lights | Efficient (O(lights + pixels)) | Expensive (O(lights * objects))
Memory bandwidth | High (G-buffer reads/writes) | Lower
Transparency | Requires separate pass | Natural support
MSAA | Not directly supported | Hardware MSAA works
Setup complexity | More complex | Simpler

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 (`scenePass` → `process1` → `process2` → 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:

Feature | QML Type | Description
--- | --- | ---
Main render pass definition | RenderPass | Defines a rendering pass with commands and material mode
Render target textures | RenderPassTexture | Texture used as render target (color or depth/stencil)
Texture output provider | RenderOutputProvider | Exposes render pass output as texture input
Layer constants | ContentLayer | Singleton providing layer filtering constants
Color output | ColorAttachment | Specifies a color render target
Depth/stencil (default) | DepthStencilAttachment | Uses implicit depth/stencil buffer
Depth/stencil (texture) | DepthTextureAttachment | Uses explicit depth texture
Object filtering | RenderablesFilter | Filters objects by layer and type
Pipeline state | PipelineStateOverride | Overrides graphics pipeline state
Nested pass execution | `SubRenderPass` | Executes another render pass
Shader defines | AddDefine | Adds shader preprocessor define
Per-target blend state | renderTargetBlend | Blend configuration for MRT
Display helper | SimpleQuadRenderer | Renders final output to View3D

### Related Types

Type | Description
--- | ---
CustomMaterial | Custom material with vertex/fragment shaders
Effect | Post-processing effects
View3D | 3D view with renderOverrides property
Model | 3D model with layers property

### Examples

- Qt Quick 3D - User Passes Example: Complete deferred rendering example

### See Also

- Programmable Materials, Effects, Geometry, and Texture data: Overview of Qt Quick 3D customization features
- Qt Quick 3D Architecture: Understanding the rendering pipeline

Programmable Materials, Effects, Geometry, and Texture data ◦ Balsam Asset Import Tool