Auf dieser Seite

Qt Quick 3D - Beispiel für Benutzerpässe

Veranschaulicht die Erstellung benutzerdefinierter Render-Pässe in „ Qt Quick 3D “.

Eine 3D-Szene, die mithilfe benutzerdefinierter Pässe gerendert wurde

}

Das Beispiel „User Passes“ veranschaulicht, wie man benutzerdefinierte Render-Passes in „ Qt Quick 3D “ erstellt. Es implementiert eine Render-Pipeline mit verzögerter Beleuchtung unter Verwendung mehrerer benutzerdefinierter Render-Passes und zeigt, wie die gesamte Render-Sequenz gehandhabt wird: opake Geometrie, Skybox, in die Szene eingebettete 2D-Elemente und transparente Geometrie.

Interne Render-Passes deaktivieren

Standardmäßig verwendet „ Qt Quick 3D “ eine Reihe interner Render-Passes, um die 3D-Szene zu rendern. Manchmal möchten Sie diese internen Render-Passes möglicherweise deaktivieren und Ihre eigene Rendering-Pipeline mithilfe benutzerdefinierter Render-Passes implementieren.

Um die internen Render-Pässe zu deaktivieren, setzen Sie die Eigenschaft „ renderOverrides “ des „ View3D “ auf „ View3D.DisableInternalPasses “.

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

Wenn Sie die internen Render-Pässe deaktivieren, müssen Sie das Ergebnis des Hauptfarb-Passes bereitstellen, damit „ View3D “ überhaupt etwas auf dem Bildschirm anzeigen kann.

Geometrie-Buffer-Pass

In diesem Beispiel ist der erste benutzerdefinierte Render-Pass ein Geometrie-Buffer-Pass (G-Buffer), der die Szenengeometrie in mehrere Render-Ziele rendert und dabei unterschiedliche Materialeigenschaften in jedem Ziel speichert. Das bereitgestellte Beispiel ist eine Teilmenge der vollständigen Materialeigenschaften, die von Qt Quick 3D -Materialien bereitgestellt werden, und konzentriert sich auf die Eigenschaften, die für eine grundlegende Implementierung der verzögerten Beleuchtung erforderlich sind.

Unser „ RenderPass “ ist in „ GBufferPass.qml “ definiert:

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"
}

Es definiert 3 Farb-Attachments und 1 Tiefen-Attachment. Der Pass benötigt 3 Texturen, die als „ RenderPassTexture “-Objekte innerhalb von „ RenderPass “ definiert sind. Diese 3 „RenderPassTextures“ dienen als Ziele für die Farb-Attachments des Passes, während das Tiefen-Attachment eine von außerhalb des Passes bereitgestellte Tiefentextur verwendet.

Der „ RenderPass “ selbst ist auf den Modus „ AugmentMaterial “ eingestellt, was bedeutet, dass er die Materialien der gerenderten Objekte durch zusätzlichen Shader-Code erweitert. Der Erweiterungs-Shader ist in der Datei „ gbuffer_augment.glsl “ enthalten, die die erforderlichen Materialeigenschaften an die verschiedenen Renderziele ausgibt.

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

Hier sehen Sie, wie die Grundfarbe, die Metallizität, die Weltnormale, die Rauheit und die Weltposition in den drei Farb-Attachments des G-Buffers gespeichert werden.

Um den G-Buffer-Pass tatsächlich in der Render-Pipeline zu nutzen, müssen wir in „ Main.qml “ eine Instanz davon erstellen und die erforderliche Tiefentextur bereitstellen:

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
}

Es werden drei „ RenderOutputProvider “-Instanzen erstellt, um Verweise auf die gerenderten G-Buffer-Texturen bereitzustellen, die im nachfolgenden Beleuchtungsdurchgang verwendet werden.

Die Eigenschaft „ layerMask “ des G-Buffer-Passes ist so eingestellt, dass nur Objekte gerendert werden, die sich auf ContentLayer.Layer0 und ContentLayer.Layer1 befinden. Auf diese Weise können wir steuern, welche Objekte im G-Buffer-Pass gerendert werden, indem wir ihre Eigenschaft „ layers “ entsprechend einstellen.

Hauptfarb-Pass und SubRenderPässe

Anstelle eines einzelnen flachen Render-Passes verwendet dieses Beispiel einen zusammengesetzten „ mainColorPass “, der die Hauptfarb- und Tiefentexturen enthält und das gesamte Rendering über eine Abfolge von „ SubRenderPass “-Unterobjekten koordiniert.

Jeder „ SubRenderPass “ nutzt dieselben Renderziele wie der übergeordnete Pass und wird der Reihe nach ausgeführt. Dadurch lässt sich auf einfache Weise eine mehrschichtige Rendering-Pipeline aufbauen, bei der jede Stufe das Ergebnis der vorherigen ergänzt.

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
                    }
                ]
            }
        }
    ]
}

Der äußere Pass setzt ` renderableTypes: RenderablesFilter.None`, sodass im übergeordneten Pass nichts direkt gerendert wird – das gesamte Rendering wird an die Unterpässe delegiert. Das Flag „ PreserveDepthStencilContents “ stellt sicher, dass die vom G-Buffer-Pass geschriebenen Tiefenwerte jedem Unterpass zur Verfügung stehen.

Subpass für verzögerte Beleuchtung

Der erste Unterpass führt die Berechnung der verzögerten Beleuchtung durch. Er rendert das Vollbild- deferredLightingQuad -Modell, das die G-Buffer-Texturen abtastet und die Beleuchtung für jedes Pixel berechnet.

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"
        }
    ]
}

Der „ deferredLightingQuad “ wird auf „ ContentLayer.Layer13 “ gesetzt, sodass er für den G-Buffer-Pass unsichtbar ist und nur von diesem Sub-Pass gerendert wird.

Ein „ PipelineStateOverride “ deaktiviert sowohl Tiefenschreibvorgänge als auch Tiefenprüfungen für das Quad. Der G-Buffer-Durchlauf hat bereits die korrekte Tiefe für die Szenengeometrie geschrieben, daher darf das Vollbild-Quad die Tiefe weder verändern noch prüfen.

Skybox-Subpass

Der zweite Sub-Pass rendert die Skybox der Szenenumgebung unter Verwendung des speziellen „ RenderPass.SkyboxPass “-Pass-Modus. „ Qt Quick 3D “ übernimmt das Zeichnen der Skybox-Geometrie unter Verwendung der Einstellungen „ lightProbe “ und „ backgroundMode “ aus dem „ SceneEnvironment “.

Ein „ PipelineStateOverride “ aktiviert die Tiefenprüfung, sodass die Skybox korrekt hinter der Szenengeometrie verborgen wird, während Tiefenschreibvorgänge deaktiviert sind, da sich die Skybox „im Unendlichen“ befindet und niemals Geometrie verdecken darf.

Subpass für transparente Objekte

Transparente Objekte können nicht im G-Buffer gespeichert werden, da sie eine von hinten nach vorne sortierte Überblendung erfordern. Stattdessen werden sie zuletzt mit ihren ursprünglichen Materialien über einen speziellen Sub-Pass gerendert.

Der transparente Kegel in der Szene wird wie folgt deklariert:

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
        }
    ]
}

Der Subpass für transparente Objekte verwendet einen „ RenderablesFilter “, um nur transparente Renderelemente auf den entsprechenden Ebenen auszuwählen, und ein „ PipelineStateOverride “ ermöglicht das Alpha-Blending, während der Tiefentest aktiv bleibt, damit transparente Objekte korrekt gegenüber undurchsichtiger Geometrie sortiert werden.

Transparente Objekte müssen nach allen undurchsichtigen Durchläufen gerendert werden, damit der Tiefenpuffer vollständig gefüllt ist, bevor die Überblendung erfolgt.

2D-Elemente im 3D-Subpass

Qt Quick 3D können Standard-2D-Elemente des „ Qt Quick “ mithilfe von „ Node “ als Container in die 3D-Szene einbetten. Um diese Elemente in eine benutzerdefinierte Render-Pipeline einzubinden, fügen Sie einen Sub-Pass mit „ passMode “ hinzu: „ 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
    }
}

Der Modus „ Item2DPass “ weist „ Qt Quick 3D “ an, alle „ Qt Quick “-Elemente, die untergeordnete Elemente von 3D-Knoten sind, in das aktuelle Renderziel zu rendern und mit den bereits vorhandenen 3D-Inhalten zu kombinieren.

Rendering auf den Bildschirm

Um schließlich das Ergebnis unserer benutzerdefinierten Render-Pässe auf dem Bildschirm anzuzeigen, müssen wir sicherstellen, dass die Hauptfarbtextur des „ View3D “ mit dem Ergebnis des Hauptfarb-Passes aktualisiert wird.

SimpleQuadRenderer {
    texture: Texture {
        textureProvider: mainColorPassProvider
    }
}

RenderPassTexture {
    id: mainColorTexture
    format: RenderPassTexture.RGBA16F
}

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

Die „ SimpleQuadRenderer “ dient dazu, die von der „ mainColorPass “ erzeugte Hauptfarbtextur in den Framebuffer der „ View3D “ zu blitten. Die „ RenderOutputProvider “ stellt den ersten Farb-Attachment von „ mainColorPass “ als Textur bereit, die von der „ SimpleQuadRenderer “ ausgelesen werden kann.

Beispielprojekt auf 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.