Auf dieser Seite

Benutzerdefinierte Render-Pässe in Qt Quick 3D

Qt Quick 3D bietet eine hochrangige API für das 3D-Rendering, die die meisten Rendering-Details automatisch übernimmt. Für fortgeschrittene Anwendungsfälle benötigen Anwendungen jedoch möglicherweise die vollständige Kontrolle über die Rendering-Pipeline. Benutzerdefinierte Render-Passes ermöglichen dies, indem sie es Anwendungen erlauben, die interne Rendering-Pipeline zu deaktivieren und eigene benutzerdefinierte Passes zu definieren.

Benutzerdefinierte Render-Pässe ermöglichen fortgeschrittene Rendering-Techniken wie:

  • Deferred Shading und Beleuchtung
  • Multi-Pass-Rendering-Effekte
  • Benutzerdefinierte Post-Processing-Pipelines
  • Selektives Rendering mit ebene-basierter Filterung
  • Screen-Space-Effekte (Ambient Occlusion, Reflexionen usw.)
  • Benutzerdefinierte Shadow-Mapping-Techniken
  • Visualisierungsdurchläufe zur Fehlerbehebung

Anpassungsstufen

Qt Quick 3D bietet drei sich ergänzende Stufen der Rendering-Anpassung, die jeweils für unterschiedliche Anwendungsfälle geeignet sind:

EbeneGültigkeitsbereichAnwendungsfälle
EffectNachbearbeitungWendet Effekte nach dem Rendern der Szene an (Unschärfe, Farbkorrektur usw.)
CustomMaterialMaterialspezifische ShaderBenutzerdefinierte Vertex- und Fragment-Shader für einzelne Materialien
RenderPass (Benutzerdefinierte Render-Durchläufe)Vollständige Kontrolle über die PipelineDeferred Rendering, mehrere Durchläufe, benutzerdefinierte Render-Ziele

Benutzerdefinierte Render-Passes (RenderPass) bieten die größte Kontrolle und ermöglichen es Ihnen, die Standard-Rendering-Pipeline entweder zu ergänzen oder vollständig zu ersetzen. Dies ergänzt „ CustomMaterial “ und „ Effect “: „ CustomMaterial “ passt die Darstellung einzelner Objekte an, während benutzerdefinierte Render-Passes die gesamte Rendering-Strategie und -Architektur steuern.

Verwendung von benutzerdefinierten Render-Pässen

Benutzerdefinierte Render-Pässe können auf zwei Arten verwendet werden:

Ergänzung interner Render-Passes

Sie können benutzerdefinierte „ RenderPass “-Objekte neben der Standard-Rendering-Pipeline hinzufügen, ohne interne Render-Passes zu deaktivieren. Dies ist nützlich für das Rendern in Texturen, die anschließend von „ Effect “ oder „ CustomMaterial “ verwendet werden, oder für die Erstellung von zusätzlichen Render-Zielen.

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
}

Ersetzen interner Durchläufe

Um die vollständige Kontrolle über die Rendering-Pipeline zu erlangen, deaktivieren Sie das interne Rendering von „ Qt Quick 3D “, indem Sie die Eigenschaft „ renderOverrides “ festlegen:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    // Your custom render passes go here
}

Wenn interne Durchläufe deaktiviert sind, führt „ Qt Quick 3D “ kein Standard-Rendering durch. Das bedeutet, dass Sie:

  • Mindestens einen „ RenderPass “ definieren, um Ihre Szene zu rendern
  • die endgültige Ausgabetextur bereitstellen, die über „ SimpleQuadRenderer “ oder einen ähnlichen Mechanismus angezeigt werden soll
  • alle Aspekte des Renderings, einschließlich Tiefenpuffer, Transparenz usw., selbst übernehmen

Hinweis: Das Deaktivieren interner Durchläufe gibt Ihnen vollständige Kontrolle, aber auch die volle Verantwortung. Funktionen wie automatisches Schatten-Rendering, Transparenzsortierung und Umgebungsreflexionen müssen bei Bedarf in Ihren benutzerdefinierten Durchläufen implementiert werden.

Kernkonzepte

Benutzerdefinierte Render-Pässe bestehen aus mehreren Schlüsselkomponenten:

RenderPass

Der Typ „ RenderPass “ ist der Hauptbaustein. Er definiert einen einzelnen Rendering-Vorgang mit einer Reihe von Befehlen, die steuern, was wie gerendert wird. Jeder Pass kann:

  • Rendering auf eine oder mehrere Farbtexturen (bis zu 4 gleichzeitige Renderziele)
  • Tiefen- und Stencil-Informationen schreiben
  • basierend auf Ebenen filtern, welche Objekte gerendert werden sollen
  • den Status der Grafikpipeline überschreiben (Überblenden, Culling usw.)
  • Originalmaterialien verwenden, diese durch benutzerdefinierte Shader ergänzen oder vollständig überschreiben

RenderPassTexture

Der Typ „ RenderPassTexture “ definiert Texturen, die als Renderziele dienen. Dabei kann es sich um Farbtexturen in verschiedenen Formaten (RGBA8, RGBA16F, RGBA32F usw.) oder um Tiefen-/Stencil-Texturen handeln. Render-Pass-Texturen werden als Ausgabedaten eines Durchgangs verwendet und können als Textureingaben für nachfolgende Durchgänge dienen.

RenderOutputProvider

Der Typ „ RenderOutputProvider “ verbindet Render-Pässe, indem er die Ausgabetexturen eines Passes als Textureingaben bereitstellt, die von Materialien oder anderen Pässen genutzt werden können. Dies ist für das Multi-Pass-Rendering unerlässlich, bei dem spätere Pässe die Ergebnisse früherer Pässe lesen müssen.

ContentLayer

Das Singleton „ ContentLayer “ stellt Layer-Konstanten (Layer0 bis Layer23) bereit, mit denen gefiltert wird, welche Objekte in welchem Pass gerendert werden. Indem Sie Objekte bestimmten Layern zuweisen und in Ihren Pässen „ RenderablesFilter “ verwenden, können Sie präzise steuern, was in jedem Pass gerendert wird.

Render-Befehle

Jedes „ RenderPass “ enthält eine Liste von Befehlen, die sein Verhalten konfigurieren:

Materialmodi

Jedes „ RenderPass “ verfügt über die Eigenschaft „ materialMode “, die steuert, wie Materialien beim Rendern behandelt werden. Die drei Modi bieten unterschiedliche Ebenen der Materialsteuerung:

Modus „OriginalMaterial“

In diesem Modus werden Objekte mit ihren zugewiesenen Materialien unverändert gerendert. Dies ist nützlich, wenn Sie die Struktur der Rendering-Pipeline (mehrere Durchläufe, benutzerdefinierte Render-Ziele) steuern möchten, das Materialverhalten jedoch standardmäßig beibehalten wollen.

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

Modus „AugmentMaterial“

Dieser Modus fügt benutzerdefinierten Shader-Code in die bestehende Material-Pipeline ein. Er ist besonders nützlich für das Deferred Rendering, bei dem zusätzliche Daten (wie Normalen, Positionen usw.) an mehrere Render-Ziele ausgegeben werden müssen, während das Basisverhalten des Materials erhalten bleibt.

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

Die Augment-Shader-Datei enthält eine Funktion „ MAIN_FRAGMENT_AUGMENT() “:

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

Weitere Details finden Sie unter „Augment-Shader für mehrere Render-Targets “.

„OverrideMaterial“-Modus

Dieser Modus ersetzt alle Objektmaterialien durch ein einziges Material. Er eignet sich für spezielle Durchläufe wie Shadow Mapping, Depth Prepass oder Debug-Visualisierung.

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

Render-Pass-Befehle

Befehle werden in der Eigenschaft „ commands “ angegeben und in der Reihenfolge ihrer Definition ausgeführt.

Farb-Attachment

Der Befehl „ ColorAttachment “ legt ein Farb-Renderziel fest. Die Eigenschaft „ name “ definiert, wie in Augment-Shadern auf die Zuordnung zugegriffen wird.

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

Pro Durchgang sind bis zu 4 Farb-Attachments möglich (für mehrere Renderziele).

Tiefen-Attachments

Es gibt zwei Möglichkeiten, mit der Tiefe umzugehen:

DepthStencilAttachment Verwendet einen impliziten Tiefen-/Stencil-Puffer:

DepthStencilAttachment { }  // Creates depth/stencil buffer automatically

DepthTextureAttachment Verwendet eine explizite Textur für die Tiefenausgabe:

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

DepthTextureAttachment {
    target: depthTex  // Explicit depth texture
}

Verwenden Sie „ DepthTextureAttachment “, wenn Sie den Tiefenpuffer zwischen mehreren Durchläufen gemeinsam nutzen oder ihn als Textureingabe in Shadern verwenden möchten.

Renderables-Filter

Der Befehl „ RenderablesFilter “ steuert anhand der Layer-Zuweisung und des Renderable-Typs, welche Objekte gerendert werden.

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

Die Eigenschaft „ layerMask “ verwendet bitweises ODER, um Ebenen zu kombinieren. Die Eigenschaft „ renderableTypes “ kann folgende Werte annehmen:

  • RenderablesFilter.Opaque: Nur undurchsichtige Objekte rendern
  • RenderablesFilter.Transparent: Nur transparente Objekte rendern
  • RenderablesFilter.Opaque | „ RenderablesFilter.Transparent“: Beide rendern

Überschreiben des Pipeline-Zustands

Der Befehl „ PipelineStateOverride “ ermöglicht eine detaillierte Steuerung des Grafik-Pipeline-Zustands. Er kann Tiefenprüfung, Überblendung, Culling, den Polygonmodus und vieles mehr überschreiben.

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

Weitere Beispiele finden Sie unter „Steuerung des Pipeline-Zustands “.

Untergeordneter Render-Durchlauf

Der Befehl „ SubRenderPass “ ermöglicht die hierarchische Zusammensetzung von Render-Pässen, indem ein weiterer Render-Pass innerhalb des aktuellen Passes ausgeführt wird.

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

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

„Define“ hinzufügen

Der Befehl „ AddDefine “ fügt Shader-Präprozessor-Definitionen hinzu, die sich auf die Shader-Kompilierung auswirken.

AddDefine {
    name: "USE_SPECIAL_MODE"
    value: 1
}

Einfaches Beispiel: Single-Pass-Rendering

Hier ist ein Minimalbeispiel, das benutzerdefinierte Render-Pässe veranschaulicht:

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

Dieses Beispiel:

  1. Deaktiviert interne Durchläufe
  2. Erstellt eine Render-Target-Textur (colorTarget)
  3. Definiert einen Render-Pass, der auf diese Textur rendert
  4. Verwendet „ RenderOutputProvider “, um die Textur sichtbar zu machen
  5. Zeigt das Ergebnis an mit SimpleQuadRenderer
  6. Rendert eine Kugel mit Standardmaterial

Arbeiten mit Ebenen

Das Singleton „ ContentLayer “ stellt Konstanten zur Verfügung, mit denen Objekte in Ebenen organisiert werden können, was eine fein abgestimmte Steuerung darüber ermöglicht, was in jedem Durchgang gerendert wird.

Ebenenkonstanten

Qt Quick 3D bietet 24 vom Benutzer zuweisbare Ebenen:

  • ContentLayer.Layer0 über „ ContentLayer.Layer23 “: Einzelne Ebenen
  • ContentLayer.LayerAll: Alle Benutzer-Ebenen zusammen
  • ContentLayer.LayerNone: Keine Ebenen

Hinweis: Die Ebenen 24–31 sind für den internen Gebrauch reserviert.

Objekte Ebenen zuweisen

Weisen Sie Objekte mithilfe der Eigenschaft „ layers “ Ebenen zu:

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

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

Objekte können mithilfe des bitweisen ODER-Operators mehreren Ebenen zugeordnet werden.

Filtern nach Ebene

Verwenden Sie „ RenderablesFilter “ in Ihren Render-Durchläufen, um auszuwählen, welche Ebenen gerendert werden sollen:

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

Anwendungsfall: Selektives Rendern

Ein praktisches Beispiel ist das Rendern bestimmter Objekte als Drahtgitter-Überlagerungen:

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

Texturverwaltung

Benutzerdefinierte Render-Pässe sind in hohem Maße auf Texturen angewiesen, sowohl als Render-Ziele als auch als Eingaben für nachfolgende Pässe oder Materialien.

Texturformate für Render-Pässe

RenderPassTexture unterstützt verschiedene Formate für unterschiedliche Anwendungsfälle:

Farbformate:

FormatBeschreibungAnwendungsfall
RGBA88 Bit pro KanalStandard-Farbausgabe, geringerer Speicherbedarf
RGBA16F16-Bit-GleitkommaHDR-Rendering, Zwischenpuffer
RGBA32F32-Bit-GleitkommaHochpräzise Berechnungen
R8, R16, R16F, R32FEinkanal-VariantenGraustufendaten, spezielle Puffer

Tiefenformate:

FormatBeschreibung
Farbtiefe 1616-Bit-Farbtiefe
Tiefe2424-Bit-Farbtiefe
Tiefe 3232-Bit-Farbtiefe
Tiefe24Stencil824-Bit-Farbtiefe + 8-Bit-Stencil

Beispiele für Texturdefinitionen:

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

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

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

Gemeinsame Nutzung von Texturen zwischen Durchgängen

Mehrere Durchläufe können dieselbe Tiefentextur gemeinsam nutzen, was Tiefenprüfungen über mehrere Durchläufe hinweg ermöglicht:

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-Ausgabeprovider

Der „ RenderOutputProvider “ stellt die Ausgänge der Render-Pässe als Eingänge für „ Texture “ zur Verfügung:

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

Die Eigenschaft „ attachmentSelector “ legt fest, welcher Farb-Attachment verwendet werden soll, wenn ein Pass mehrere Render-Ziele hat:

  • RenderOutputProvider.Attachment0: Erster Farb-Attachment
  • RenderOutputProvider.Attachment1: Zweites Farb-Attachment
  • RenderOutputProvider.Attachment2: Dritter Farb-Attachment
  • RenderOutputProvider.Attachment3: Vierte Farbzuordnung

Beispiel für die Verkettung von Pässen

Mehrere Durchläufe können miteinander verkettet werden, wobei jeder Durchlauf die Ausgabe des vorherigen Durchlaufs verarbeitet:

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

Steuerung des Pipeline-Zustands

Der Befehl „ PipelineStateOverride “ ermöglicht eine detaillierte Steuerung des Grafik-Pipeline-Zustands und erlaubt die Anpassung von Tiefenprüfung, Überblendung, Culling und mehr.

Tiefenprüfung und -schreiben

Steuern Sie, wie Tiefenwerte geprüft und geschrieben werden:

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

Die Eigenschaft „ depthFunction “ kann folgende Werte annehmen:

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

Überblendung

Aktivieren und konfigurieren Sie das Blending für Transparenz:

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

Bei mehreren Renderzielen sollten Sie zielspezifische Mischzustände verwenden. „ PipelineStateOverride “ stellt die anhangsspezifischen Eigenschaften vom Typ „value“ bereit: „ targetBlend0 “ bis „ targetBlend7 “ (vom Typ „ renderTargetBlend “), und die Enums für Mischfaktor und Operation befinden sich im Namespace „ RenderTargetBlend “:

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

Steuerung des Face-Cullings:

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

Die Einstellung „ cullMode “ hier überschreibt den Wert, den das Material andernfalls für diesen Durchgang festlegen würde. Optionen: None (kein Culling), Front (Vorderseiten ausblenden), Back (Rückseiten ausblenden).

Drahtgitter-Rendering

Geometrie als Drahtgitter darstellen:

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

Die Option „ polygonMode “ kann auf „ Fill “ (Standard) oder „ Line “ (Drahtgitter) gesetzt werden.

Scher-Rechtecke

Rendering auf einen rechteckigen Bereich beschränken:

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

Ansichtsfenster-Steuerung

Den Ansichtsbereich überschreiben:

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

Vollständiges Beispiel: Drahtgitter-Überlagerung

In diesem Beispiel wird eine Szene zunächst normal gerendert und anschließend mit einer Drahtgitter-Version überlagert:

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-Shader für mehrere Render-Ziele

Im Modus „ RenderPass.AugmentMaterial “ stellen Sie einen Augment-Shader bereit, der benutzerdefinierten Code in den Fragment-Shader des Materials einfügt. Dies ist besonders nützlich beim Deferred Rendering, bei dem Materialdaten an mehrere Render-Ziele (MRT) ausgegeben werden müssen.

Die Funktion `MAIN_FRAGMENT_AUGMENT`

Ihre Augment-Shader-Datei muss eine „ MAIN_FRAGMENT_AUGMENT() “-Funktion definieren:

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

Diese Funktion wird während des Fragment-Shaders aufgerufen, nachdem die Materialberechnungen abgeschlossen sind. Dadurch erhalten Sie Zugriff auf Materialeigenschaften und können in benutzerdefinierte Ausgänge schreiben.

Verfügbare integrierte Variablen

Innerhalb von ` MAIN_FRAGMENT_AUGMENT()` ersetzt die Engine eine Reihe von Makros, die Daten der Material-Pipeline bereitstellen. Nur die unten aufgeführten Makros sind Teil der Augment-Shader-API:

MakroTypBeschreibung
BASE_COLORvec4Material-Grundfarbe (linearer Farbraum, nach der Materialverarbeitung).
METALNESSfloatMetallizität des Materials im Bereich von 0,0 bis 1,0.
ROUGHNESSfloatMaterialrauheit im Bereich von 0,0 bis 1,0.
WORLD_NORMALvec3Oberflächennormale im Weltraum (nach dem Normal-Mapping).
WORLD_TANGENTvec3Tangentialvektor im Weltraum.
WORLD_BINORMALvec3Binormalvektor im Weltraumkoordinatensystem.
DIFFUSE_LIGHTvec3Kumulierter Beitrag des diffusen Lichts.
SPECULAR_LIGHTvec3Kumulierter Beitrag des Spiegelungslichts.
EMISSIVE_LIGHTvec3Emissionsbeitrag des Materials.
F0vec3Fresnel-Reflexionsgrad bei senkrechtem Einfall.
F90vec3Fresnel-Reflexionsgrad bei streifendem Einfall.

Hinweis: Einige Beispiele (einschließlich des unten aufgeführten Beispiels zum Deferred Rendering) lesen die Fragmentposition im Weltraum über ` qt_varWorldPos`. Dies ist der zugrunde liegende variierende Name der Engine, kein Augment-Shader-Makro, und fällt in dieselbe halböffentliche Kategorie wie die in „Built-in Shader Facilities“ beschriebenen „ .glsllib “-Dateien: Es funktioniert in der Praxis, es kann jedoch nicht garantiert werden, dass es über verschiedene Releases hinweg stabil bleibt.

Schreiben in benannte Ausgänge

Farb-Attachments in Ihrem Render-Pass können Namen haben, die den Ausgabevariablen in Ihrem Shader entsprechen:

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

In Ihrem 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 werden bis zu 4 gleichzeitige Farb-Attachments (GBUFFER0 bis GBUFFER3) unterstützt.

Vollständiges Beispiel für einen Augment-Shader

Hier ist ein vollständiges Beispiel für einen G-Buffer-Pass beim Deferred Rendering:

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

In Verbindung mit einem Render-Pass (RenderPassTexture -Instanzen werden als direkte Unterelemente des umschließenden „ View3D “ deklariert und über ihre ID referenziert):

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

Beibehaltung des Materialverhaltens

Der Augment-Shader läuft zusätzlich zur normalen Material-Pipeline und nicht an deren Stelle. Das bedeutet:

  • Die Texturen, Eigenschaften und Berechnungen des Materials werden weiterhin ausgeführt
  • Sie fügen zusätzliche Ausgänge hinzu, ersetzen aber nicht den primären Farbausgang
  • Die RGBA-Ausgabe des ursprünglichen Materials wird weiterhin in den ersten Farbanhang geschrieben, sofern sie nicht überschrieben wird

Wenn Sie lediglich benutzerdefinierte Daten ausgeben müssen und die Standard-Materialberechnungen nicht benötigen, sollten Sie stattdessen die Verwendung von „ RenderPass.OverrideMaterial “ in Betracht ziehen.

Wann sollte man „Augment“ und wann „Override“ verwenden?

Verwenden Sie „ AugmentMaterial “, wenn:

  • Sie Materialeigenschaften (Metallizität, Rauheit, Normalen) für G-Buffer benötigen
  • Sie vorhandene Materialfunktionen wie Textur-Mapping nutzen möchten
  • Sie mehrere Render-Targets mit Materialdaten benötigen

Verwenden Sie „ OverrideMaterial “, wenn:

  • Sie für alle Objekte ein identisches Verhalten benötigen (Depth-Prepass, Schattenkarten)
  • Sie keine materialbezogenen Eigenschaften benötigen
  • Sie maximale Leistung durch das Umgehen von Materialberechnungen erzielen möchten

Integrierte Shader-Funktionen

Erweiterte Shader und benutzerdefinierte Materialien, die in Render-Passes verwendet werden, haben über ein Include-System Zugriff auf die integrierte Shader-Infrastruktur von „ Qt Quick 3D “. Diese Shader-Bibliotheksdateien (.glsllib) bieten Funktionen für Beleuchtung, Schatten, Tonemapping und mehr.

Include-Syntax:

Verwenden Sie in Ihrem Shader-Code \#include -Direktiven, um Funktionen zu importieren:

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

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

Verfügbare Shader-Bibliotheken:

Diese Shader-Bibliotheken befinden sich in der Engine unter „ src/runtimerender/res/effectlib/ “ und gelten als halböffentlich: Sie können in benutzerdefinierten Shadern verwendet werden, bieten jedoch nicht dieselben Garantien hinsichtlich der Binärkompatibilität wie der Rest der öffentlichen Qt-API.

BibliothekBeschreibung
tonemapping.glsllibStellt die Funktion „ qt_tonemap() “ für die Konvertierung von HDR in LDR unter Verwendung des integrierten Tonemapping-Algorithmus bereit
lightsData.glsllibEnthält Informationen zur Szenenbeleuchtung (Lichtpositionen, Farben, Richtungen usw.), auf die bei benutzerdefinierten Beleuchtungsberechnungen zugegriffen werden kann
shadowMapping.glsllibStellt Funktionen zum Abtasten aus den Standard-Schattenkarten bereit, wodurch benutzerdefinierte Materialien Schatten erhalten können
funcprocessPunctualLighting.glsllibEnthält „ qt_processPunctualLighting() “ und andere integrierte Beleuchtungsfunktionen für Standard-PBR-Beleuchtungsberechnungen
sampleProbe.glsllibFunktionen zum Abrufen bildbasierter Beleuchtungsproben (qt_sampleDiffuse(), qt_sampleGlossyPrincipled())

Beispiel: Benutzerdefinierte Beleuchtung mit integrierten Funktionen

Hier ist ein Beispiel für einen Fragment-Shader, der die integrierten Beleuchtungsfunktionen nutzt:

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

Hinweis: Diese Shader-Bibliotheken sind eine halböffentliche API. Sie sind zwar stabil und für die Verwendung in benutzerdefinierten Shadern vorgesehen, Qt garantiert jedoch nicht, dass ihre interne Implementierung oder die verfügbaren Funktionen zwischen den Releases unverändert bleiben. Es ist jedoch unwahrscheinlich, dass sich häufig verwendete Funktionen wie „ qt_tonemap() “ und „ qt_processPunctualLighting() “ wesentlich ändern werden.

Fortgeschrittenes Beispiel: Deferred Rendering

Deferred Rendering ist eine Technik, bei der Geometrieinformationen in einem ersten Durchgang in mehrere Texturen (sogenannte G-Buffer oder Geometrie-Buffer) gerendert werden und die Beleuchtungsberechnungen in einem zweiten Durchgang unter Verwendung der gespeicherten Geometriedaten durchgeführt werden. Dieser Ansatz ist besonders effizient, wenn viele Lichtquellen vorhanden sind, da jedes Pixel unabhängig von der Anzahl der Lichtquellen nur einmal schattiert wird.

Architektur des Deferred Rendering

Die Pipeline für das verzögerte Rendering besteht aus zwei Hauptdurchläufen:

  1. Geometrie-Durchlauf (G-Buffer-Durchlauf): Rendert die Szenengeometrie auf mehrere Renderziele und speichert dabei Materialeigenschaften wie Albedo, Normalen, Rauheit, Metallizität und Weltposition.
  2. Beleuchtungsdurchlauf: Rendert ein Vollbild-Quad, das die G-Buffer abtastet und für jedes Pixel Beleuchtungsberechnungen auf Basis der gespeicherten Geometriedaten durchführt.

Implementierung des G-Buffer-Durchgangs

Definieren Sie zunächst einen G-Buffer-Durchlauf, der Materialdaten an mehrere Renderziele ausgibt:

GBufferPass.qml: Die G-Buffer-Renderziele werden als erforderliche Eigenschaften bereitgestellt, damit der umschließende „ View3D “ sie bereitstellt und ihre Ausgaben lesen kann:

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

Der Augment-Shader greift auf die von der Material-Pipeline berechneten Materialeigenschaften zu und schreibt sie in die drei G-Buffer-Anhänge. Normalen werden für die Speicherung im Bereich von [-1,1] auf [0,1] umcodiert.

Implementierung des Beleuchtungsdurchlaufs

Der Beleuchtungsdurchlauf rendert ein Vollbild-Quad, das die G-Buffer abtastet und die Beleuchtung berechnet:

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

Der Beleuchtungs-Vertex-Shader (lighting.vert) erzeugt ein Vollbild-Quad in normierten Gerätekoodinaten, und der Fragment-Shader (lighting.frag) nimmt Abtastungen aus den G-Buffern vor und führt die Beleuchtungsberechnungen durch.

Vollständige Integration

So integrieren Sie beide Pässe in einem 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-Ablauf

Das Rendering verläuft wie folgt:

  1. G-Buffer-Durchlauf: Szenenobjekte auf Layer0 und Layer1 werden gerendert. Für jedes Objekt schreibt der Augment-Shader Albedo, Normalen, Rauheit, Metallizität und Position in drei Farb-Attachments. Die Tiefe wird in die gemeinsam genutzte Tiefentextur geschrieben.
  2. Beleuchtungsdurchlauf: Das Vollbild-Quad auf Layer13 wird gerendert. Sein benutzerdefiniertes Material liest die drei G-Buffer-Texturen und die Tiefentextur aus, rekonstruiert die Szeneninformationen und führt Beleuchtungsberechnungen (Richtungslichter, bildbasierte Beleuchtung usw.) durch, um die endgültige beleuchtete Farbe zu erzeugen.
  3. Anzeige: Das Ergebnis des Beleuchtungsdurchgangs wird über „ SimpleQuadRenderer “ angezeigt.

Vorteile und Einschränkungen

Vorteile:

  • Effizient bei vielen Lichtquellen (jedes Pixel wird einmal beleuchtet)
  • Komplexität der Beleuchtung unabhängig von der Komplexität der Szene
  • Einfache Implementierung von Screen-Space-Effekten
  • Entkoppelt Geometrie- und Beleuchtungsdurchläufe

Einschränkungen:

  • Höhere Speicherbandbreite aufgrund von Lese- und Schreibvorgängen im G-Buffer
  • Kein Hardware-MSAA (erfordert alternatives Anti-Aliasing)
  • Transparenz erfordert einen separaten Forward-Pass
  • Komplexere Pipeline bei der Einrichtung und Wartung

Ein vollständiges, funktionierendes Beispiel finden Sie unter „Qt Quick 3D – User Passes Example“.

Beispiel für eine vollständige Rendering-Pipeline

Eine realistische, benutzerdefinierte Render-Pipeline muss häufig mehrere Pass-Typen kombinieren: undurchsichtige Geometrie, Skybox-Hintergrund, 2D-Overlays und transparente Objekte. Hier ist ein vollständiges Beispiel, das zeigt, wie man diese mithilfe von „ SubRenderPass “ für eine hierarchische Komposition organisiert:

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

Schlüsselbegriffe

SubRenderPass für die hierarchische Organisation:

Der Hauptdurchlauf verwendet „ SubRenderPass “-Befehle, um untergeordnete Durchläufe nacheinander auszuführen. Der übergeordnete Durchlauf selbst führt keine Rendering-Schritte durch (RenderablesFilter.None), sondern koordiniert lediglich die untergeordneten Durchläufe. Dies sorgt für eine klare Struktur und ermöglicht die gemeinsame Nutzung des Tiefenpuffers über alle Unterdurchläufe hinweg.

Pass-Modi:

  • RenderPass.UserPass (Standard): Benutzerdefiniertes Rendering mit Ihrer Geometrie und Ihren Materialien
  • RenderPass.SkyboxPass: Rendert die Umgebungs-Skybox aus SceneEnvironment
  • RenderPass.Item2DPass: Rendert 2D- Qt Quick -Inhalte, die in die 3D-Szene eingebettet sind

Renderreihenfolge:

  1. Opake Geometrie wird in den G-Buffer gerendert
  2. Deferred Lighting wird auf das Vollbild-Quad angewendet
  3. Skybox wird hinter der Geometrie gerendert (Tiefentest aktiviert, Tiefenschreiben deaktiviert)
  4. 2D-UI-Overlays werden darüber gerendert
  5. Transparente Objekte werden zuletzt mit Überblendung gerendert

Gemeinsame Nutzung des Tiefenpuffers:

Der „ mainDepthStencilTexture “ wird über „ PreserveDepthStencilContents “ zwischen den Durchläufen gemeinsam genutzt. Dies stellt sicher, dass die Skybox hinter der Geometrie gerendert wird und transparente Objekte korrekt gegen opake Geometrie geprüft werden.

Item2D-Inhalt:

Qt Quick 2D-Elemente (Schaltflächen, Rechtecke, Text usw.), die in „ Node “-Objekten platziert sind, werden über „ RenderPass.Item2DPass “ gerendert. Diese Elemente werden im 3D-Raum positioniert, jedoch als 2D-Overlays gerendert, die Maus- und Touch-Eingaben empfangen können.

Überlegungen zur Leistung

Benutzerdefinierte Render-Durchläufe bieten maximale Kontrolle, erfordern jedoch eine sorgfältige Abwägung der Auswirkungen auf die Leistung.

Auswahl der Texturformate

Wählen Sie Texturformate entsprechend Ihren Anforderungen aus:

FormatGröße pro PixelAnwendungsfall
RGBA84 ByteEndausgabe, einfache Farbpuffer
RGBA16F8 ByteHDR-Inhalt, Zwischenpuffer, Normalen
RGBA32F16 ByteHochpräzise Berechnungen, Positionen
R16F2 BytesEinkanal-HDR (Tiefe, AO usw.)

Für einen 1920x1080-Framebuffer:

  • RGBA8: ~8 MB
  • RGBA16F: ~16 MB
  • RGBA32F: ~32 MB
  • Drei RGBA16F-G-Buffer: ~48 MB

Empfehlung: Verwenden Sie RGBA16F für Zwischenpuffer und RGBA8 oder R8, wo dies ausreichend ist.

Deferred- vs. Forward-Rendering

AspektDeferred RenderingForward-Rendering
Viele LichtquellenEffizient (O(Lichtquellen + Pixel))Aufwendig (O(Lichtquellen * Objekte))
SpeicherbandbreiteHoch (Lese-/Schreibvorgänge im G-Buffer)Geringer
TransparenzErfordert einen separaten DurchlaufNatürliche Unterstützung
MSAAWird nicht direkt unterstütztHardware-MSAA funktioniert
Komplexität der EinrichtungKomplexerEinfacher

Verwenden Sie Deferred Rendering, wenn:

  • Sie viele Lichtquellen haben (10+)
  • Screen-Space-Effekte wichtig sind
  • Die Szene überwiegend undurchsichtig ist

Verwenden Sie Forward-Rendering, wenn:

  • Wenige Lichtquellen (<5)
  • Hohe Transparenz
  • Die Speicherbandbreite ist begrenzt
  • Eine einfachere Pipeline ist vorzuziehen

Optimierung der Reihenfolge der Render-Durchläufe

Render-Durchläufe werden in der Reihenfolge ausgeführt, in der sie auftreten. Optimieren Sie durch:

  • Opake Geometrie vor transparenter Geometrie rendern
  • Verwenden Sie einen Depth-Prepass, wenn dies vorteilhaft ist (reduziert Overdraw in komplexen Szenen)
  • Minimierung von Wechseln des Renderziels
  • Wiederverwendung von Tiefenpuffern über mehrere Durchläufe hinweg, sofern möglich

Render-Target-Flags

Die Eigenschaft „ renderTargetFlags “ steuert das Speicherverhalten:

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“: Beibehaltung vorhandener Daten (z. B. beim Multi-Pass-Rendering auf dasselbe Ziel).

„DoNotStoreDepthStencilContents“: Hinweis, dass Tiefen- und Stencil-Daten verworfen werden können (auf manchen Hardwarekomponenten kann dies die Leistung verbessern, da Speicherschreibvorgänge vermieden werden).

Wann sollten Benutzer-Render-Passes verwendet werden?

Erwägen Sie die Verwendung von Benutzer-Render-Pässen, wenn:

  • Implementierung von Deferred Rendering
  • Erstellung komplexer Multi-Pass-Effekte
  • Wenn eine explizite Kontrolle über die Renderreihenfolge erforderlich ist
  • Implementierung benutzerdefinierter Rendering-Techniken
  • Erstellung von Screen-Space-Effekten, die Geometriedaten erfordern

Vermeiden Sie User-Passes, wenn:

  • Das Standard-Rendering Ihren Anforderungen entspricht
  • Sie lediglich eine Nachbearbeitung benötigen (verwenden Sie stattdessen „ Effect “)
  • Sie lediglich benutzerdefinierte Material-Shader benötigen (verwenden Sie stattdessen „ CustomMaterial “)
  • Die Leistung entscheidend ist und zusätzliche Durchläufe eine Verschwendung darstellen würden

Häufige Muster und Anwendungsfälle

Benutzerdefinierte Render-Durchläufe ermöglichen viele fortgeschrittene Rendering-Techniken. Hier sind einige gängige Muster:

Deferred Shading/Lighting

Speichern Sie Geometrieattribute in G-Buffern und führen Sie die Beleuchtung im Bildraum durch. Ausführlich behandelt im fortgeschrittenen Beispiel: Deferred Rendering.

Kantenerkennung und Konturierung

Rendern Sie die Szene zunächst normal und wenden Sie anschließend in einem zweiten Durchgang die Kantenerkennung an:

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

Benutzerdefinierte Post-Processing-Ketten

Verknüpfen Sie mehrere Post-Processing-Effekte miteinander, indem Sie die Ausgabe jedes Durchgangs über „ RenderOutputProvider “ in den nächsten einspeisen. Ein anschauliches Beispiel für eine mehrstufige Kette finden Sie unter „Pass Chaining Example“ (scenePass → process1 → process2 → display).

Selektive Drahtgitter-Überlagerung

Rendern Sie einige Objekte als Vollkörper und andere als Drahtgitter-Überlagerungen. Siehe das Beispiel unter „Arbeiten mit Ebenen“.

Debug-Visualisierung

Erstellen Sie Debug-Pässe, die Normalen, Tiefe oder andere Daten visualisieren:

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

Benutzerdefiniertes Shadow-Mapping

Implementieren Sie benutzerdefiniertes Shadow-Mapping mit expliziter Steuerung:

// 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

Wichtige Hinweise

Beachten Sie bei der Arbeit mit benutzerdefinierten Render-Pässen folgende wichtige Punkte:

Schattenbehandlung

Benutzerdefinierte Render-Pässe beziehen das interne Schatten-Rendering von „ Qt Quick 3D “ nicht automatisch mit ein. Wenn Sie interne Pässe deaktivieren und Schatten benötigen, müssen Sie:

  • Ihre eigenen Shadow-Mapping-Passes implementieren
  • Die Szene aus der Perspektive der Lichtquelle in eine Tiefentextur rendern
  • die Schattenkarte in Ihre Beleuchtungsberechnungen einbeziehen

Alternativ benötigen Sie in einfacheren Fällen möglicherweise keine Schatten oder können vorgefertigte Schattenkarten verwenden.

HDR und Tonemapping

Bei der Verwendung von Renderzielen mit Gleitkommadaten (RGBA16F, RGBA32F) erfolgt das Rendering im linearen HDR-Raum. Sie müssen im abschließenden Anzeigedurchgang Tonemapping anwenden, um die Daten in darstellbares LDR umzuwandeln:

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 qt_tonemap() t eine Funktion in Shadern, die für diesen Zweck verwendet werden kann.

Erzeugung von Tiefentexturen

Wenn Sie Tiefe als Textur benötigen (für Effekte wie Tiefenschärfe), verwenden Sie „ DepthTextureAttachment “ anstelle von „ DepthStencilAttachment “:

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

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

Löschwerte und Verwaltung von Render-Zielen

Jeder Durchlauf kann Löschwerte festlegen:

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

Wenn ein Durchgang den Inhalt nicht beibehält (Standard), werden die Render-Ziele vor dem Rendern auf diese Werte zurückgesetzt. Wenn „ PreserveColorContents “ oder „ PreserveDepthStencilContents “ festgelegt ist, bleibt der vorhandene Inhalt erhalten und die Löschwerte werden ignoriert.

Bewährte Vorgehensweisen zur Organisation von Ebenen

Organisieren Sie Ihre Ebenen logisch:

  • Ebene 0–2: Objekte der Hauptszene
  • Ebene 3–4: Transparente Objekte (separater Durchgang)
  • Ebene 10+: Vollbild-Quads für die Nachbearbeitung
  • Ebene 15+: UI-/Debug-Overlays

Dokumentieren Sie die Verwendung der Ebenen in Kommentaren und achten Sie auf Konsistenz in Ihrer gesamten Anwendung.

Shader-Kompatibilität

Augment-Shader und benutzerdefinierte Materialien, die in Render-Durchläufen verwendet werden, müssen mit der Shader-Infrastruktur von Qt Quick 3D kompatibel sein:

  • Verwenden Sie GLSL-kompatible Syntax
  • Fügen Sie geeignete \#include -Direktiven für „ Qt Quick 3D “-Funktionen ein
  • Beachten Sie die verfügbaren integrierten Variablen
  • Testen Sie auf allen Zielplattformen (OpenGL, Vulkan, Metal, D3D)

Einschränkungen bei mehreren Render-Zielen

Bei Verwendung mehrerer Render-Targets (MRT):

  • Maximal 4 Farb-Attachments
  • Alle Attachments müssen dieselben Abmessungen haben
  • Der Blend-State kann pro Attachment über PipelineStateOverride
  • Nicht jede Hardware unterstützt MRT (überprüfen Sie die Gerätefunktionen)

Transparenz

Transparente Objekte stellen beim Deferred Rendering eine Herausforderung dar. Gängige Ansätze:

  • Forward-Pass für transparente Objekte: Opake Geometrie deferred rendern, transparente Geometrie in einem separaten Forward-Pass
  • Separater transparenter G-Buffer: Transparente Geometrie in einen separaten Satz von G-Buffern mit aktiviertem Blending rendern
  • Gewichtete, blendungsbasierte, reihenfolgeunabhängige Transparenz: Verwendung spezieller OIT-Techniken

In den meisten Fällen ist ein separater Forward-Pass für transparente Objekte die einfachste Lösung.

API-Referenz

Die folgende Tabelle enthält eine vollständige Übersicht über alle Typen im Zusammenhang mit Benutzer-Render-Pässen:

FunktionQML-TypBeschreibung
Definition des HauptrenderdurchlaufsRenderPassDefiniert einen Render-Durchlauf mit Befehlen und Materialmodus
Renderziel-TexturenRenderPassTextureAls Renderziel verwendete Textur (Farbe oder Tiefe/Stencil)
Anbieter der TexturausgabeRenderOutputProviderStellt die Ausgabe des Render-Passes als Textur-Eingabe bereit
EbenenkonstantenContentLayerSingleton, das Layer-Filterkonstanten bereitstellt
FarbausgabeColorAttachmentLegt ein Farb-Renderziel fest
Tiefe/Stencil (Standard)DepthStencilAttachmentVerwendet impliziten Tiefen-/Stencil-Puffer
Tiefe/Stencil (Textur)DepthTextureAttachmentVerwendet eine explizite Tiefentextur
ObjektfilterungRenderablesFilterFiltert Objekte nach Ebene und Typ
Pipeline-ZustandPipelineStateOverrideÜberschreibt den Grafik-Pipeline-Status
Ausführung verschachtelter DurchläufeSubRenderPassFührt einen weiteren Render-Durchlauf aus
Shader-DefinitionenAddDefineFügt eine Shader-Präprozessor-Definition hinzu
Zielspezifischer ÜberblendungsstatusrenderTargetBlendBlend-Konfiguration für MRT
Anzeige-HelperSimpleQuadRendererRendert die endgültige Ausgabe in View3D
TypBeschreibung
CustomMaterialBenutzerdefiniertes Material mit Vertex-/Fragment-Shadern
EffectNachbearbeitungseffekte
View3D3D-Ansicht mit der Eigenschaft „ renderOverrides “
Model3D-Modell mit der Eigenschaft „ layers “

Beispiele

Siehe auch

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