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:
| Ebene | Gültigkeitsbereich | Anwendungsfälle |
|---|---|---|
| Effect | Nachbearbeitung | Wendet Effekte nach dem Rendern der Szene an (Unschärfe, Farbkorrektur usw.) |
| CustomMaterial | Materialspezifische Shader | Benutzerdefinierte Vertex- und Fragment-Shader für einzelne Materialien |
| RenderPass (Benutzerdefinierte Render-Durchläufe) | Vollständige Kontrolle über die Pipeline | Deferred 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:
- ColorAttachment: Legt ein Farb-Renderziel fest
- DepthStencilAttachment: Legt die Behandlung von Tiefe und Stencil fest
- DepthTextureAttachment: Verwendet eine Textur für die Tiefenausgabe
- RenderablesFilter: Filtert Objekte nach Ebene und Typ (undurchsichtig/transparent)
- PipelineStateOverride: Steuert den Status der Grafikpipeline
SubRenderPass: Führt innerhalb dieses Durchgangs einen weiteren Renderdurchgang aus- AddDefine: Fügt Shader-Präprozessor-Definitionen hinzu
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 automaticallyDepthTextureAttachment 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 rendernRenderablesFilter.Transparent: Nur transparente Objekte rendernRenderablesFilter.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:
- Deaktiviert interne Durchläufe
- Erstellt eine Render-Target-Textur (colorTarget)
- Definiert einen Render-Pass, der auf diese Textur rendert
- Verwendet „ RenderOutputProvider “, um die Textur sichtbar zu machen
- Zeigt das Ergebnis an mit SimpleQuadRenderer
- 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 EbenenContentLayer.LayerAll: Alle Benutzer-Ebenen zusammenContentLayer.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:
| Format | Beschreibung | Anwendungsfall |
|---|---|---|
| RGBA8 | 8 Bit pro Kanal | Standard-Farbausgabe, geringerer Speicherbedarf |
| RGBA16F | 16-Bit-Gleitkomma | HDR-Rendering, Zwischenpuffer |
| RGBA32F | 32-Bit-Gleitkomma | Hochpräzise Berechnungen |
| R8, R16, R16F, R32F | Einkanal-Varianten | Graustufendaten, spezielle Puffer |
Tiefenformate:
| Format | Beschreibung |
|---|---|
| Farbtiefe 16 | 16-Bit-Farbtiefe |
| Tiefe24 | 24-Bit-Farbtiefe |
| Tiefe 32 | 32-Bit-Farbtiefe |
| Tiefe24Stencil8 | 24-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-AttachmentRenderOutputProvider.Attachment1: Zweites Farb-AttachmentRenderOutputProvider.Attachment2: Dritter Farb-AttachmentRenderOutputProvider.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“,LessOrEqualGreater, „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:
| Makro | Typ | Beschreibung |
|---|---|---|
BASE_COLOR | vec4 | Material-Grundfarbe (linearer Farbraum, nach der Materialverarbeitung). |
METALNESS | float | Metallizität des Materials im Bereich von 0,0 bis 1,0. |
ROUGHNESS | float | Materialrauheit im Bereich von 0,0 bis 1,0. |
WORLD_NORMAL | vec3 | Oberflächennormale im Weltraum (nach dem Normal-Mapping). |
WORLD_TANGENT | vec3 | Tangentialvektor im Weltraum. |
WORLD_BINORMAL | vec3 | Binormalvektor im Weltraumkoordinatensystem. |
DIFFUSE_LIGHT | vec3 | Kumulierter Beitrag des diffusen Lichts. |
SPECULAR_LIGHT | vec3 | Kumulierter Beitrag des Spiegelungslichts. |
EMISSIVE_LIGHT | vec3 | Emissionsbeitrag des Materials. |
F0 | vec3 | Fresnel-Reflexionsgrad bei senkrechtem Einfall. |
F90 | vec3 | Fresnel-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.
| Bibliothek | Beschreibung |
|---|---|
tonemapping.glsllib | Stellt die Funktion „ qt_tonemap() “ für die Konvertierung von HDR in LDR unter Verwendung des integrierten Tonemapping-Algorithmus bereit |
lightsData.glsllib | Enthält Informationen zur Szenenbeleuchtung (Lichtpositionen, Farben, Richtungen usw.), auf die bei benutzerdefinierten Beleuchtungsberechnungen zugegriffen werden kann |
shadowMapping.glsllib | Stellt Funktionen zum Abtasten aus den Standard-Schattenkarten bereit, wodurch benutzerdefinierte Materialien Schatten erhalten können |
funcprocessPunctualLighting.glsllib | Enthält „ qt_processPunctualLighting() “ und andere integrierte Beleuchtungsfunktionen für Standard-PBR-Beleuchtungsberechnungen |
sampleProbe.glsllib | Funktionen 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:
- Geometrie-Durchlauf (G-Buffer-Durchlauf): Rendert die Szenengeometrie auf mehrere Renderziele und speichert dabei Materialeigenschaften wie Albedo, Normalen, Rauheit, Metallizität und Weltposition.
- 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:
- 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.
- 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.
- 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 MaterialienRenderPass.SkyboxPass: Rendert die Umgebungs-Skybox aus SceneEnvironmentRenderPass.Item2DPass: Rendert 2D- Qt Quick -Inhalte, die in die 3D-Szene eingebettet sind
Renderreihenfolge:
- Opake Geometrie wird in den G-Buffer gerendert
- Deferred Lighting wird auf das Vollbild-Quad angewendet
- Skybox wird hinter der Geometrie gerendert (Tiefentest aktiviert, Tiefenschreiben deaktiviert)
- 2D-UI-Overlays werden darüber gerendert
- 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:
| Format | Größe pro Pixel | Anwendungsfall |
|---|---|---|
| RGBA8 | 4 Byte | Endausgabe, einfache Farbpuffer |
| RGBA16F | 8 Byte | HDR-Inhalt, Zwischenpuffer, Normalen |
| RGBA32F | 16 Byte | Hochpräzise Berechnungen, Positionen |
| R16F | 2 Bytes | Einkanal-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
| Aspekt | Deferred Rendering | Forward-Rendering |
|---|---|---|
| Viele Lichtquellen | Effizient (O(Lichtquellen + Pixel)) | Aufwendig (O(Lichtquellen * Objekte)) |
| Speicherbandbreite | Hoch (Lese-/Schreibvorgänge im G-Buffer) | Geringer |
| Transparenz | Erfordert einen separaten Durchlauf | Natürliche Unterstützung |
| MSAA | Wird nicht direkt unterstützt | Hardware-MSAA funktioniert |
| Komplexität der Einrichtung | Komplexer | Einfacher |
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 testingWichtige 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:
| Funktion | QML-Typ | Beschreibung |
|---|---|---|
| Definition des Hauptrenderdurchlaufs | RenderPass | Definiert einen Render-Durchlauf mit Befehlen und Materialmodus |
| Renderziel-Texturen | RenderPassTexture | Als Renderziel verwendete Textur (Farbe oder Tiefe/Stencil) |
| Anbieter der Texturausgabe | RenderOutputProvider | Stellt die Ausgabe des Render-Passes als Textur-Eingabe bereit |
| Ebenenkonstanten | ContentLayer | Singleton, das Layer-Filterkonstanten bereitstellt |
| Farbausgabe | ColorAttachment | Legt ein Farb-Renderziel fest |
| Tiefe/Stencil (Standard) | DepthStencilAttachment | Verwendet impliziten Tiefen-/Stencil-Puffer |
| Tiefe/Stencil (Textur) | DepthTextureAttachment | Verwendet eine explizite Tiefentextur |
| Objektfilterung | RenderablesFilter | Filtert Objekte nach Ebene und Typ |
| Pipeline-Zustand | PipelineStateOverride | Überschreibt den Grafik-Pipeline-Status |
| Ausführung verschachtelter Durchläufe | SubRenderPass | Führt einen weiteren Render-Durchlauf aus |
| Shader-Definitionen | AddDefine | Fügt eine Shader-Präprozessor-Definition hinzu |
| Zielspezifischer Überblendungsstatus | renderTargetBlend | Blend-Konfiguration für MRT |
| Anzeige-Helper | SimpleQuadRenderer | Rendert die endgültige Ausgabe in View3D |
Verwandte Typen
| Typ | Beschreibung |
|---|---|
| CustomMaterial | Benutzerdefiniertes Material mit Vertex-/Fragment-Shadern |
| Effect | Nachbearbeitungseffekte |
| View3D | 3D-Ansicht mit der Eigenschaft „ renderOverrides “ |
| Model | 3D-Modell mit der Eigenschaft „ layers “ |
Beispiele
- Qt Quick 3D - Beispiel „User Passes“: Vollständiges Beispiel für Deferred Rendering
Siehe auch
- Programmierbare Materialien, Effekte, Geometrie und Texturdaten: Übersicht über die Anpassungsfunktionen von „ Qt Quick 3D “
- Qt Quick 3D Architektur: Die Rendering-Pipeline verstehen
© 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.