Sur cette page

Qt Quick 3D - Exemple de passes utilisateur

Illustre la création de passes de rendu personnalisées dans Qt Quick 3D.

Une scène 3D rendue à l'aide de passes personnalisées par l'utilisateur

}

L'exemple « Passages utilisateur » montre comment créer des passages de rendu personnalisés dans Qt Quick 3D. Il met en œuvre un pipeline de rendu à éclairage différé utilisant plusieurs passages de rendu définis par l'utilisateur et explique comment gérer la séquence de rendu complète : géométrie opaque, skybox, éléments 2D intégrés à la scène et géométrie transparente.

Désactivation des passes de rendu internes

Par défaut, Qt Quick 3D utilise un ensemble de passes de rendu internes pour rendre la scène 3D. Vous pouvez parfois souhaiter désactiver ces passes internes et implémenter votre propre pipeline de rendu à l'aide de passes de rendu définies par l'utilisateur.

Pour désactiver les passes de rendu internes, définissez la propriété ` renderOverrides ` de l’ View3D sur ` View3D.DisableInternalPasses`.

View3D {
    id: view3D
    anchors.fill: parent
    renderOverrides: View3D.DisableInternalPasses
    environment: ExtendedSceneEnvironment {
        lightProbe: Texture {
            textureData: ProceduralSkyTextureData {
            }
        }
        backgroundMode: SceneEnvironment.SkyBox
    }

Si vous désactivez les passes de rendu internes, vous devrez fournir le résultat de la passe de couleur principale à l’ View3D pour qu’il puisse afficher quoi que ce soit à l’écran.

Pass de tampon de géométrie

Dans cet exemple, le premier passage de rendu personnalisé est un passage de tampon de géométrie (G-buffer) qui rend la géométrie de la scène dans plusieurs cibles de rendu, en stockant différentes propriétés de matériau dans chaque cible. L’exemple fourni est un sous-ensemble des propriétés de matériau complètes fournies par les matériaux 3D d’ Qt Quick, en se concentrant sur les propriétés nécessaires à une implémentation de base de l’éclairage différé.

Notre RenderPass est défini dans GBufferPass.qml:

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

    property alias layerMask: filter.layerMask
    required property RenderPassTexture depthTexture

    RenderPassTexture {
        id: gbuffer0
        format: RenderPassTexture.RGBA16F
        // rgb: baseColor (linear), a: metalness
    }

    RenderPassTexture {
        id: gbuffer1
        format: RenderPassTexture.RGBA16F
        // rgb: normal, a: roughness
    }

    RenderPassTexture {
        id: gbuffer2
        format: RenderPassTexture.RGBA16F
        // rgb: emissive, a: ao/spare
    }

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

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

Il définit 3 attachements de couleur et 1 attachement de profondeur. Le passage nécessite 3 textures qui sont définies en tant qu’objets « RenderPassTexture » au sein de l’objet RenderPass. Ces 3 RenderPassTextures sont utilisées comme cibles pour les attachements de couleur du passage, tandis que l’attachement de profondeur utilise une texture de profondeur fournie en dehors du passage.

L’ RenderPass lui-même est configuré en mode « AugmentMaterial », ce qui signifie qu’il enrichira les matériaux des objets rendus à l’aide d’un code de shader supplémentaire. Le shader d’enrichissement est fourni dans le fichier « gbuffer_augment.glsl », qui transmet les propriétés de matériau requises aux différentes cibles de rendu.

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);
}

Vous voyez ici comment la couleur de base, la métallicité, la normale mondiale, la rugosité et la position mondiale sont stockées dans les trois attaches de couleur du G-buffer.

Pour utiliser concrètement le passage G-buffer dans le pipeline de rendu, nous devons en créer une instance dans ` Main.qml` et fournir la texture de profondeur requise :

RenderPassTexture {
    id: mainDepthStencilTexture
    format: RenderPassTexture.Depth24Stencil8
}
GBufferPass {
    id: gbufferPass
    layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
    depthTexture: mainDepthStencilTexture
}

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
}

Trois instances d’ RenderOutputProvider sont créées pour fournir des références aux textures du G-buffer rendues, qui seront utilisées dans la passe d’éclairage suivante.

La propriété « layerMask » de la passe G-buffer est configurée pour ne rendre que les objets se trouvant sur ContentLayer.Layer0 et ContentLayer.Layer1. Cela nous permet de contrôler quels objets sont rendus dans la passe G-buffer en définissant leur propriété « layers » en conséquence.

Pass de couleur principale et sous-pass de rendu

Plutôt qu’un seul passage de rendu plat, cet exemple utilise un « composite mainColorPass » qui gère les textures de couleur principale et de profondeur, et orchestre l’ensemble du rendu via une séquence d’enfants de type « SubRenderPass ».

Chaque SubRenderPass partage les mêmes cibles de rendu que le pass externe et s’exécute dans l’ordre. Cela permet de construire facilement un pipeline de rendu en couches où chaque étape s’ajoute au résultat de la précédente.

RenderPass {
    id: mainColorPass
    clearColor: "black"
    // Preserve depth across SubRenderPasses so geometry depth is available
    // when rendering the skybox, transparent objects, and 2D items.
    renderTargetFlags: RenderPass.RenderTargetFlags.PreserveDepthStencilContents

    commands: [
        ColorAttachment {
            target: mainColorTexture
        },
        DepthTextureAttachment {
            target: mainDepthStencilTexture
        },
        RenderablesFilter {
            // Nothing renders directly in the outer pass; all rendering
            // is delegated to the SubRenderPasses below.
            renderableTypes: RenderablesFilter.None
        },

        // 1. Deferred lighting: shade opaque geometry stored in the G-buffer.
        SubRenderPass {
            renderPass: RenderPass {
                id: deferredLightingPass
                materialMode: RenderPass.OriginalMaterial
                commands: [
                    PipelineStateOverride {
                        // The full-screen quad must not write or test depth;
                        // geometry depth was already written by the G-buffer pass.
                        depthWriteEnabled: false
                        depthTestEnabled: false
                    },
                    RenderablesFilter { layerMask: ContentLayer.Layer13 }
                ]
            }
        },

        // 2. Skybox: render the environment behind all scene geometry.
        SubRenderPass {
            renderPass: RenderPass {
                id: skyboxPass
                passMode: RenderPass.SkyboxPass
                commands: [
                    PipelineStateOverride {
                        // The skybox is rendered "at infinity" so it must
                        // depth-test (to be hidden by geometry) but must not
                        // write depth.
                        depthTestEnabled: true
                        depthWriteEnabled: false
                    }
                ]
            }
        },

        // 3. 2D items: render any Qt Quick Items embedded in the 3D scene.
        SubRenderPass {
            renderPass: RenderPass {
                id: item2DPass
                passMode: RenderPass.Item2DPass
            }
        },

        // 4. Transparent objects: render blended geometry on top of everything else.
        SubRenderPass {
            renderPass: RenderPass {
                id: transparentItemPass
                materialMode: RenderPass.OriginalMaterial
                commands: [
                    RenderablesFilter {
                        renderableTypes: RenderablesFilter.Transparent
                        layerMask: ContentLayer.Layer0 | ContentLayer.Layer1
                    },
                    PipelineStateOverride {
                        // Enable alpha blending and depth testing so transparent
                        // objects sort correctly against opaque geometry.
                        blendEnabled: true
                        depthTestEnabled: true
                        targetBlend0.enable: true
                        targetBlend0.srcColor: RenderTargetBlend.SrcAlpha
                        targetBlend0.dstColor: RenderTargetBlend.OneMinusSrcAlpha
                        targetBlend0.srcAlpha: RenderTargetBlend.One
                        targetBlend0.dstAlpha: RenderTargetBlend.OneMinusSrcAlpha
                    }
                ]
            }
        }
    ]
}

Le pass externe définit renderableTypes: RenderablesFilter.None afin que rien ne soit rendu directement dans le pass parent — tout le rendu est délégué aux sous-pass. Le drapeau « PreserveDepthStencilContents » garantit que les valeurs de profondeur écrites par le pass G-buffer sont disponibles pour chaque sous-pass.

Sous-pass d’éclairage différé

Le premier sous-pass exécute le calcul de l’éclairage différé. Il effectue le rendu du modèle « deferredLightingQuad » en plein écran, qui échantillonne les textures du G-buffer et évalue l’éclairage pour chaque pixel.

Model {
    id: deferredLightingQuad
    layers: ContentLayer.Layer13
    castsShadows: false
    receivesShadows: false
    geometry: PlaneGeometry {
        // geometry doesn't matter, just need 4 verts
        plane: PlaneGeometry.XY
    }
    materials: [
        CustomMaterial {
            id: lightingPassMaterial
            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"
        }
    ]
}

L’ deferredLightingQuad est placé sur « ContentLayer.Layer13 » (Cache de profondeur désactivé) afin qu’il soit invisible pour le passage G-buffer et rendu uniquement par ce sous-passage.

Un « PipelineStateOverride » désactive à la fois les écritures de profondeur et les tests de profondeur pour le quad. Le passage du G-buffer a déjà écrit la profondeur correcte pour la géométrie de la scène ; le quad plein écran ne doit donc ni modifier ni tester la profondeur.

Sous-pass Skybox

Le deuxième sous-pass effectue le rendu de la skybox de l’environnement de la scène à l’aide du mode de pass spécial « RenderPass.SkyboxPass ». « Qt Quick 3D» se charge de dessiner la géométrie de la skybox en utilisant les paramètres « lightProbe » et « backgroundMode » de l’ SceneEnvironment.

Un « PipelineStateOverride » active le test de profondeur afin que la skybox soit correctement masquée derrière la géométrie de la scène, tandis que les écritures de profondeur sont désactivées car la skybox se trouve « à l’infini » et ne doit jamais occulter la géométrie.

Sous-pass des objets transparents

Les objets transparents ne peuvent pas être stockés dans le G-buffer car ils nécessitent un mélange trié de l’arrière vers l’avant. Ils sont donc rendus en dernier à l’aide de leurs matériaux d’origine via un sous-pass dédié.

Le cône transparent de la scène est déclaré comme suit :

Model {
    id: cone
    layers: ContentLayer.Layer1
    source: "#Cone"
    y: 100
    materials: [
        PrincipledMaterial {
            baseColor: Qt.rgba(0.0, 1.0, 0.0, 0.5)
            alphaMode: PrincipledMaterial.Blend
            metalness: 0.0
            roughness: 0.5
        }
    ]
}

Le sous-pass transparent utilise une « RenderablesFilter » pour sélectionner uniquement les éléments de rendu transparents sur les couches correspondantes, et une « PipelineStateOverride » active le mélange alpha tout en conservant le test de profondeur actif afin que les objets transparents soient correctement triés par rapport à la géométrie opaque.

Les objets transparents doivent être rendus après toutes les passes opaques afin que le tampon de profondeur soit entièrement rempli avant que le mélange n'ait lieu.

Éléments 2D dans un sous-pass 3D

Qt Quick La 3D peut intégrer des éléments 2D standard d’ Qt Quick au sein de la scène 3D en utilisant Node comme conteneur. Pour inclure ces éléments dans un pipeline de rendu personnalisé, ajoutez un sous-pass avec passMode: RenderPass.Item2DPass.

Node {
    x: -200
    y: 100

    Item {
        anchors.centerIn: parent
        ColumnLayout {
            Button {
                text: "Click Me!"
            }
            Rectangle {
                color: "blue"
                implicitWidth: 50
                implicitHeight: 50

                NumberAnimation on rotation {
                    from: 0
                    to: 360
                    duration: 4000
                    loops: Animation.Infinite
                    running: true
                }
            }
        }
    }

    NumberAnimation on eulerRotation.y {
        from: 0
        to: 360
        duration: 6000
        loops: Animation.Infinite
        running: true
    }
}

Le mode « Item2DPass » indique à Qt Quick 3D de rendre tous les éléments Qt Quick qui sont des enfants de nœuds 3D dans la cible de rendu actuelle, en les composant avec le contenu 3D déjà présent.

Rendu à l’écran

Enfin, pour afficher à l’écran le résultat de nos passes de rendu personnalisées, nous devons nous assurer que la texture de couleur principale de l’ View3D est mise à jour avec le résultat de la passe de couleur principale.

SimpleQuadRenderer {
    texture: Texture {
        textureProvider: mainColorPassProvider
    }
}

RenderPassTexture {
    id: mainColorTexture
    format: RenderPassTexture.RGBA16F
}

RenderOutputProvider {
    id: mainColorPassProvider
    textureSource: RenderOutputProvider.UserPassTexture
    renderPass: mainColorPass
    attachmentSelector: RenderOutputProvider.Attachment0
}

L'SimpleQuadRenderer sert à copier la texture de couleur principale générée par l'mainColorPass vers le framebuffer de l'View3D. L'RenderOutputProvider expose la première attache de couleur de l'mainColorPass sous forme de texture que l'SimpleQuadRenderer peut échantillonner.

Exemple de projet sur code.qt.io

© 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.