本页内容

中的用户自定义渲染通道Qt Quick 3D

Qt Quick 3D 提供了一个用于 3D 渲染的高级 API,可自动处理大部分渲染细节。然而,对于高级应用场景,应用程序可能需要对渲染管线进行完全控制。用户自定义渲染通道通过允许应用程序禁用内部渲染管线并定义自己的自定义通道来实现这一点。

用户渲染通道支持以下高级渲染技术:

  • 延迟着色与照明
  • 多通道渲染效果
  • 自定义后处理管道
  • 基于图层过滤的选择性渲染
  • 屏幕空间效果(环境光遮蔽、反射等)
  • 自定义阴影映射技术
  • 调试可视化渲染通道

自定义程度

Qt Quick 3D 提供三种互补的渲染自定义级别,每种都适用于不同的使用场景:

级别范围使用场景
Effect后处理在场景渲染完成后应用特效(如模糊、调色等)
CustomMaterial按材质定义的着色器针对单个材质的自定义顶点着色器和片段着色器
RenderPass (用户渲染通道)完整的渲染管线控制延迟渲染、多通道渲染、自定义渲染目标

用户渲染通道(RenderPass )提供了最大的控制权,允许您补充或完全替换默认的渲染管线。这与CustomMaterial 和Effect 相辅相成:CustomMaterial 用于自定义单个对象的渲染方式,而用户渲染通道则控制整体的渲染策略和架构。

使用用户渲染通道

用户渲染阶段有两种使用方式:

补充内部渲染通道

您可以在不禁用内部渲染通道的情况下,在默认渲染管线中添加自定义的RenderPass 对象。这对于渲染到纹理(随后由Effect 或CustomMaterial 使用)或创建辅助渲染目标非常有用。

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
}

替换内部渲染阶段

若要完全控制渲染管线,请通过设置renderOverrides 属性来禁用Qt Quick 3D 的内部渲染:

View3D {
    renderOverrides: View3D.DisableInternalPasses

    // Your custom render passes go here
}

当内部渲染通道被禁用时,Qt Quick 3D 将不会执行任何默认渲染。这意味着您必须:

  • 定义至少一个RenderPass 来渲染您的场景
  • 通过 `SimpleQuadRenderer ` 或类似机制提供用于显示的最终输出纹理
  • 处理所有渲染方面,包括深度缓冲区、透明度等

注意:禁用 内部渲染阶段可让您完全掌控渲染过程,但也需承担全部责任。如需使用自动阴影渲染、透明度排序和环境反射等功能,必须在自定义渲染阶段中自行实现。

核心概念

用户自定义的渲染通道由几个关键组件构成:

RenderPass

RenderPass 类型是主要构建模块。它定义了一个渲染操作,包含一组控制渲染内容及方式的命令。每个渲染通道可以:

  • 渲染到一个或多个颜色纹理(最多可同时渲染 4 个渲染目标)
  • 写入深度和模板信息
  • 根据图层过滤要渲染的对象
  • 覆盖图形管道状态(混合、剔除等)
  • 使用原始材质、通过自定义着色器对其进行增强,或完全覆盖它们

RenderPassTexture

RenderPassTexture 类型定义了用作渲染目标的纹理。这些可以是各种格式的颜色纹理(如RGBA8、RGBA16F、RGBA32F等),也可以是深度/模板纹理。渲染通道纹理作为某个渲染通道的输出,并可作为后续渲染通道的纹理输入。

RenderOutputProvider

RenderOutputProvider 类型通过将一个渲染通道的输出纹理作为纹理输入提供给材质或其他渲染通道,从而实现渲染通道之间的连接。这对于多通道渲染至关重要,因为后续渲染通道需要读取先前渲染通道的结果。

ContentLayer

ContentLayer 单例提供了一系列层常量(Layer0 至 Layer23),用于过滤哪些对象应在哪个渲染通道中渲染。通过将对象分配到特定层,并在渲染通道中使用 `RenderablesFilter `,您可以精确控制每个渲染通道中渲染的内容。

渲染命令

每个RenderPass 都包含一组用于配置其行为的命令:

材质模式

每个RenderPass 都具有materialMode 属性,用于控制渲染过程中材质处理的方式。这三种模式提供了不同级别的材质控制:

原始材质模式

此模式渲染对象时,其已分配的材质保持不变。当您需要控制渲染管线结构(多通道渲染、自定义渲染目标),但希望保持材质行为标准时,此模式非常有用。

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

增强材质模式

此模式将自定义着色器代码注入到现有的材质管道中。这在延迟渲染中特别有用,此时您需要在保持材质基本行为的同时,将额外数据(如法线、位置等)输出到多个渲染目标中。

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

增强着色器文件包含一个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);
}

更多详情请参阅《针对多个渲染目标的增强着色器》。

OverrideMaterial 模式

此模式将所有对象材质替换为单一材质。这对于阴影贴图、深度预渲染或调试可视化等专用渲染通道非常有用。

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

渲染通道命令

命令在commands 属性中指定,并按定义的顺序执行。

颜色附件

ColorAttachment 命令指定一个颜色渲染目标。name 属性定义了在增强着色器中如何访问该附加对象。

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

每个渲染通道最多可设置 4 个颜色附件(用于多个渲染目标)。

深度附件

处理深度有两种方式:

DepthStencilAttachment 使用隐式深度/模板缓冲区:

DepthStencilAttachment { }  // Creates depth/stencil buffer automatically

DepthTextureAttachment 使用显式纹理作为深度输出:

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

DepthTextureAttachment {
    target: depthTex  // Explicit depth texture
}

当需要在多个渲染通道之间共享深度缓冲区,或将其用作着色器中的纹理输入时,请使用DepthTextureAttachment 。

可渲染对象过滤器

RenderablesFilter 命令根据对象的分层分配和可渲染类型来控制哪些对象被渲染。

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

layerMask 属性使用位或(OR)运算来组合图层。renderableTypes 属性的取值可以是:

  • RenderablesFilter.Opaque: 仅渲染不透明对象
  • RenderablesFilter.Transparent: 仅渲染透明对象
  • RenderablesFilter.Opaque |RenderablesFilter.Transparent:同时渲染两者

管道状态覆盖

PipelineStateOverride 命令可对图形管道状态进行精细控制。它可覆盖深度测试、混合、剔除、多边形模式等设置。

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

更多示例请参见“图形管线状态控制”。

子渲染通道

SubRenderPass 命令允许通过在当前渲染阶段内执行另一个渲染阶段,来实现渲染阶段的分层组合。

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

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

添加定义

AddDefine 命令可添加影响着色器编译的着色器预处理器定义。

AddDefine {
    name: "USE_SPECIAL_MODE"
    value: 1
}

简单示例:单通道渲染

以下是一个展示用户自定义渲染通道的最小示例:

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

此示例:

  1. 禁用内部渲染通道
  2. 创建一个渲染目标纹理(colorTarget)
  3. 定义了一个将渲染结果写入该纹理的渲染通道
  4. 使用RenderOutputProvider 来暴露该纹理
  5. 使用以下代码显示结果SimpleQuadRenderer
  6. 渲染一个带有标准材质的球体

图层操作

ContentLayer 单例提供了一系列常量,用于将对象组织到图层中,从而能够精细控制每个渲染通道中渲染的内容。

图层常量

Qt Quick 3D 提供 24 个可由用户分配的图层:

  • ContentLayer.Layer0 通过ContentLayer.Layer23 :单独图层
  • ContentLayer.LayerAll:所有用户图层合并
  • ContentLayer.LayerNone: 无图层

注意:图层 24-31 预留供内部使用。

将对象分配到图层

使用layers 属性将对象分配到图层:

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

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

可通过位或(bitwise OR)操作将对象归属于多个图层。

按图层过滤

在渲染步骤中使用 `RenderablesFilter ` 来选择要渲染的图层:

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

用例:选择性渲染

一个实际示例是将某些对象渲染为线框叠加层:

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

纹理管理

用户渲染通道在很大程度上依赖于纹理,这些纹理既可作为渲染目标,也可作为后续渲染通道或材质的输入。

渲染通道纹理格式

RenderPassTexture 支持多种格式以满足不同使用场景:

颜色格式:

格式描述使用场景
RGBA8每通道8位标准色彩输出,内存占用更低
RGBA16F16 位浮点HDR 渲染,中间缓冲区
RGBA32F32 位浮点高精度计算
R8、R16、R16F、R32F单通道变体灰度数据、专用缓冲区

深度格式:

格式描述
深度1616位色深
深度2424 位色深
深度3232 位色深
深度24模板824 位深度 + 8 位模板

纹理定义示例:

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

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

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

不同渲染通道间的纹理共享

多个渲染通道可以共享同一张深度纹理,从而实现跨通道的深度测试:

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

渲染输出提供程序

RenderOutputProvider 将渲染通道输出作为Texture 的输入暴露出来:

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

attachmentSelector 属性用于指定当渲染通道包含多个渲染目标时应使用哪个颜色附件:

  • RenderOutputProvider.Attachment0: 第一个颜色附件
  • RenderOutputProvider.Attachment1: 第二个颜色附件
  • RenderOutputProvider.Attachment2: 第三个颜色附件
  • RenderOutputProvider.Attachment3: 第四个颜色附件

通道链式处理示例

多个处理阶段可以串联起来,每个阶段处理前一阶段的输出:

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

管道状态控制

PipelineStateOverride 命令可对图形管道状态进行精细控制,支持自定义深度测试、混合、剔除等操作。

深度测试与写入

控制深度值的测试和写入方式:

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

depthFunction 属性可设置为:

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

混合

启用并配置透明度混合:

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

对于多个渲染目标,请使用针对每个目标的混合状态。PipelineStateOverride 暴露了每个附件的值类型属性targetBlend0 至targetBlend7 (类型为renderTargetBlend ),而混合因子和运算枚举位于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
}

剔除

控制面剔除:

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

在此处设置cullMode 将覆盖材质原本为该渲染通道指定的值。选项:None (不进行剔除)、Front (剔除前向面)、Back (剔除后向面)。

线框渲染

将几何体渲染为线框:

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

polygonMode 可以是Fill (默认)或Line (线框)。

剪切矩形

将渲染范围限制在矩形区域内:

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

视口控制

覆盖视口:

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

完整示例:线框叠加

本示例先正常渲染场景,然后叠加线框版本:

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

用于多个渲染目标的增强着色器

在使用“RenderPass.AugmentMaterial ”模式时,您需要提供一个增强着色器,该着色器会将自定义代码注入材质的光栈着色器中。这在延迟渲染中尤为有用,因为您需要将材质数据输出到多个渲染目标(MRT)中。

MAIN_FRAGMENT_AUGMENT 函数

您的增强着色器文件必须定义一个MAIN_FRAGMENT_AUGMENT() 函数:

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

该函数在片段着色器中调用,时间点为材质计算完成后,此时您可以访问材质属性,并能够向自定义输出写入数据。

可用的内置变量

在MAIN_FRAGMENT_AUGMENT() 内部,引擎会替换一组用于暴露材质管道数据的宏。只有以下列出的宏属于增强着色器 API 的一部分:

宏类型描述
BASE_COLORvec4材质基色(线性颜色空间,经过材质处理后)。
METALNESSfloat材质金属度,范围为 0.0 到 1.0。
ROUGHNESSfloat材质粗糙度,范围为 0.0 到 1.0。
WORLD_NORMALvec3世界坐标系下的表面法线(法线贴图后)。
WORLD_TANGENTvec3世界坐标系下的切线向量。
WORLD_BINORMALvec3世界坐标系中的副法线向量。
DIFFUSE_LIGHTvec3累积的漫反射光贡献。
SPECULAR_LIGHTvec3累积的镜面光贡献。
EMISSIVE_LIGHTvec3材质的发光贡献。
F0vec3法线入射时的菲涅尔反射率。
F90vec3掠入射时的菲涅尔反射率。

注意:某些 示例(包括下文中的延迟渲染示例)通过qt_varWorldPos 读取世界空间中的片段位置。 这是引擎底层的变量名称,并非增强着色器宏,且与《内置着色器功能》中描述的.glsllib 文件同属半公开类别:虽然在实践中有效,但无法保证在不同版本间保持稳定。

写入命名输出

渲染通道中的颜色附件名称可以与着色器中的输出变量相对应:

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

在扩展着色器中:

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 最多支持 4 个同时存在的颜色附件(GBUFFER0 至 GBUFFER3)。

完整的增强着色器示例

以下是一个延迟渲染 G-缓冲区通道的完整示例:

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

与渲染通道配合使用时(RenderPassTexture 实例被声明为外围View3D 的直接子节点,并通过id进行引用):

View3D {
    RenderPassTexture { id: gbuffer0; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer1; format: RenderPassTexture.RGBA16F }
    RenderPassTexture { id: gbuffer2; format: RenderPassTexture.RGBA16F }

    RenderPass {
        id: gbufferPass
        materialMode: RenderPass.AugmentMaterial
        augmentShader: "gbuffer_augment.glsl"
        commands: [
            ColorAttachment { target: gbuffer0; name: "GBUFFER0" },
            ColorAttachment { target: gbuffer1; name: "GBUFFER1" },
            ColorAttachment { target: gbuffer2; name: "GBUFFER2" },
            DepthStencilAttachment { }
        ]
    }
}

保留材质行为

增强着色器是在常规材质管道的基础上额外运行的,而不是取代它。这意味着:

  • 材质的纹理、属性及计算过程仍会照常进行
  • 您添加的是额外输出,而非替换主颜色输出
  • 除非被覆盖,否则原始材质的 RGBA 输出仍会写入第一个颜色附件

如果您仅需输出自定义数据,且不需要标准材质计算,请考虑改用RenderPass.OverrideMaterial 。

何时使用 Augment 与 Override

在以下情况下使用AugmentMaterial :

  • 您需要为 G-缓冲区提供材质属性(金属度、粗糙度、法线)时
  • 希望利用现有材质功能(如纹理映射)
  • 需要包含材质数据的多个渲染目标

在以下情况下使用OverrideMaterial :

  • 当您需要所有对象具有相同的行为(深度预处理、阴影贴图)时
  • 您不需要按材质设置的属性
  • 您希望通过绕过材质计算来获得最佳性能

内置着色器功能

渲染通道中使用的增强着色器和自定义材质可通过包含系统访问Qt Quick 3D 的内置着色器基础设施。这些着色器库文件(.glsllib )提供了光照、阴影、色调映射等功能。

包含语法:

在着色器代码中使用\#include 指令来导入功能:

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

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

可用的着色器库:

这些着色器库位于引擎的src/runtimerender/res/effectlib/ 目录下,被视为半公开:它们可在用户着色器中使用,但不具备与Qt其余公开API相同的二进制兼容性保证。

库描述
tonemapping.glsllib提供qt_tonemap() 函数,用于通过内置的色调映射算法将 HDR 转换为 LDR
lightsData.glsllib包含场景照明信息(光源位置、颜色、方向等),可在自定义照明计算中访问
shadowMapping.glsllib提供从默认阴影贴图中采样的函数,使自定义材质能够接收阴影
funcprocessPunctualLighting.glsllib包含用于标准 PBR 光照计算的 `qt_processPunctualLighting() ` 及其他内置光照函数
sampleProbe.glsllib用于采样基于图像的照明探针的函数(qt_sampleDiffuse() 、qt_sampleGlossyPrincipled() )

示例:使用内置函数实现自定义照明

以下是一个使用内置照明功能的片段着色器示例:

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

注意:这些 着色器库属于半公开 API。虽然它们很稳定且旨在用于自定义着色器,但 Qt 并不保证其内部实现或可用函数在不同版本之间保持不变。不过,像qt_tonemap() 和qt_processPunctualLighting() 这样常用的函数不太可能发生重大变化。

高级示例:延迟渲染

延迟渲染是一种技术,其工作原理是在第一遍渲染中将几何信息渲染到多个纹理(称为 G-缓冲区或几何缓冲区)中,然后在第二遍渲染中利用存储的几何数据进行光照计算。当存在大量光源时,这种方法特别高效,因为无论光源数量多少,每个像素仅需渲染一次。

延迟渲染架构

延迟渲染管道主要由两个阶段组成:

  1. 几何渲染阶段(G-Buffer 阶段):将场景几何体渲染到多个渲染目标中,并存储漫反射率、法线、粗糙度、金属度以及世界坐标等材质属性。
  2. 光照渲染阶段:渲染一个全屏四边形,该四边形会采样 G-Buffer,并基于存储的几何数据对每个像素执行光照计算。

G-缓冲区处理阶段的实现

首先,定义一个将材质数据输出到多个渲染目标的 G-缓冲区处理阶段:

GBufferPass.qml:G-Buffer渲染目标作为必需属性暴露出来,以便外围的View3D 能提供这些属性并读取其输出:

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

增强着色器访问由材质管道计算出的材质属性,并将它们写入三个 G-缓冲区附件中。法线在存储时被编码为 [0,1] 范围(原范围为 [-1,1])。

光照渲染阶段的实现

光照渲染阶段会渲染一个全屏四边形,该四边形会采样 G-缓冲区并计算光照:

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

光照顶点着色器(lighting.vert )在归一化设备坐标系中创建一个全屏四边形,而片段着色器(lighting.frag )则采样 G-缓冲区并执行光照计算。

完整集成

以下是在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 }
}

渲染流程

渲染流程如下:

  1. G-缓冲区渲染通道:渲染Layer0和Layer1上的场景对象。对于每个对象,增强着色器将反照率、法线、粗糙度、金属度和位置写入三个颜色附件中。深度则写入共享的深度纹理中。
  2. 光照渲染通道:渲染 Layer13 上的全屏四边形。其自定义材质会采样三个 G-buffer 纹理和深度纹理,重建场景信息,并执行光照计算(方向光、基于图像的光照等),以生成最终的光照颜色。
  3. 显示:光照渲染阶段的结果通过SimpleQuadRenderer 进行显示。

优点与局限性

优点:

  • 在光源较多时效率高(每个像素仅渲染一次)
  • 光照复杂度与场景复杂度无关
  • 易于实现屏幕空间效果
  • 将几何渲染与光照渲染解耦

局限性:

  • 由于G缓冲区的读写操作,需要更高的内存带宽
  • 不支持硬件 MSAA(需要采用其他抗锯齿方法)
  • 透明效果需要单独的前向渲染通道
  • 管道的设置和维护更为复杂

完整的可运行示例,请参见Qt Quick 3D - 用户处理阶段示例。

完整的渲染管道示例

一个逼真的自定义渲染管线通常需要结合多种渲染通道类型:不透明几何体、天空盒背景、2D 叠加层以及透明物体。以下是一个完整的示例,展示了如何利用SubRenderPass 进行分层合成来组织这些元素:

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

关键概念

SubRenderPass 关于分层组织:

主渲染通道(main pass)使用SubRenderPass 命令按顺序执行子渲染通道。父渲染通道本身不会进行任何渲染(RenderablesFilter.None ),它仅负责协调子渲染通道。这不仅提供了清晰的结构,还允许所有子渲染通道共享深度缓冲区。

渲染通道模式:

  • RenderPass.UserPass (默认):使用您的几何体和材质进行自定义渲染
  • RenderPass.SkyboxPass:从SceneEnvironment
  • RenderPass.Item2DPass:渲染嵌入 3D 场景中的 2DQt Quick 内容

渲染顺序:

  1. 不透明几何体渲染至 G-buffer
  2. 将延迟照明应用于全屏四边形
  3. 在几何体后方渲染天空盒(启用深度测试,禁用深度写入)
  4. 在顶层渲染2D UI叠加层
  5. 最后渲染透明对象并进行混合

深度缓冲区共享:

通过PreserveDepthStencilContents 在各渲染通道间共享mainDepthStencilTexture 。这确保了天空盒渲染在几何体之后,且透明对象能正确地与不透明几何体进行深度测试。

Item2D 内容:

Qt Quick 放置在Node 对象中的2D元素(按钮、矩形、文本等)由RenderPass.Item2DPass 渲染。这些元素虽位于3D空间中,但渲染为可接收鼠标/触摸输入的2D叠加层。

性能注意事项

用户自定义的渲染通道可提供最大的控制权,但需要仔细考虑其对性能的影响。

纹理格式选择

请根据您的需求选择纹理格式:

格式每像素大小应用场景
RGBA84字节最终输出、简单颜色缓冲区
RGBA16F8字节HDR内容、中间缓冲区、法线
RGBA32F16 字节高精度计算、位置
R16F2 字节单通道HDR(深度、AO等)

对于 1920x1080 的帧缓冲区:

  • RGBA8:约8 MB
  • RGBA16F:约16 MB
  • RGBA32F:约 32 MB
  • 三个 RGBA16F G-缓冲区:约 48 MB

建议:中间缓冲区使用 RGBA16F,在条件允许的情况下使用 RGBA8 或 R8。

延迟渲染与前向渲染

方面延迟渲染前向渲染
多个光源高效(O(光源数 + 像素数))开销大(O(光源数 * 物体数))
内存带宽高(G-缓冲区的读写操作)较低
透明度需要单独的渲染通道原生支持
MSAA不直接支持硬件 MSAA 功能正常
设置复杂度更复杂更简单

在以下情况下使用延迟渲染:

  • 当存在大量光源(10个以上)时
  • 屏幕空间特效很重要
  • 场景中不透明物体占多数

在以下情况下使用前向渲染:

  • 光源较少(<5)
  • 存在大量透明物体时
  • 内存带宽受限时
  • 更倾向于使用更简单的渲染管线

渲染通道排序优化

渲染通道按遇到时的顺序执行。优化方法:

  • 先渲染不透明几何体,再渲染透明几何体
  • 在有益时使用深度预渲染(可减少复杂场景中的过度渲染)
  • 尽量减少渲染目标的切换
  • 在可能的情况下跨渲染通道复用深度缓冲区

渲染目标标志

renderTargetFlags 属性控制内存行为:

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:保留现有数据(例如,用于对同一渲染目标进行多通道渲染)。

DoNotStoreDepthStencilContents:提示可丢弃深度/模板数据(在某些硬件上,这可通过避免内存写入来提升性能)。

何时使用用户渲染通道

在以下情况下应考虑使用用户渲染阶段:

  • 实现延迟渲染时
  • 创建复杂的多通道效果
  • 需要显式控制渲染顺序时
  • 实现自定义渲染技术
  • 构建需要几何数据的屏幕空间特效时

在以下情况下应避免使用用户渲染通道:

  • 默认渲染已满足需求时
  • 您仅需后处理(请改用Effect )
  • 仅需自定义材质着色器(请改用CustomMaterial )
  • 性能至关重要,额外的渲染通道会造成资源浪费

常见模式与用例

用户渲染通道支持许多高级渲染技术。以下是一些常见模式:

延迟着色/照明

将几何属性存储在 G-缓冲区中,并在屏幕空间中进行光照计算。详情请参见“高级示例:延迟渲染”。

边缘检测与轮廓渲染

先正常渲染场景,然后在第二遍渲染中应用边缘检测:

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

自定义后处理链

通过RenderOutputProvider 将各渲染通道的输出传递给下一通道,从而将多个后处理效果串联起来。关于多阶段链的实际示例,请参见《通道链示例》(scenePass →process1 →process2 → 显示)。

选择性线框叠加

将部分对象渲染为实心,其余对象渲染为线框叠加层。请参阅“图层操作”中的示例。

调试可视化

创建用于可视化法线、深度或其他数据的调试渲染通道:

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

自定义阴影映射

实现可显式控制的自定义阴影映射:

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

重要注意事项

在使用用户渲染通道时,请牢记以下要点:

阴影处理

用户渲染通道不会自动包含Qt Quick 3D 的内部阴影渲染。如果您禁用了内部渲染通道但需要阴影效果,则必须:

  • 实现自己的阴影映射渲染通道
  • 从光源视角渲染场景并将其渲染到深度纹理中
  • 在光照计算中采样阴影贴图

此外,对于较简单的场景,您可能不需要阴影,或者可以使用预烘焙的阴影贴图。

HDR 与色调映射

当使用浮点渲染目标(RGBA16F、RGBA32F)时,渲染结果处于线性 HDR 空间中。您必须在最终显示渲染阶段应用色调映射,将其转换为可显示的 LDR:

CustomMaterial {
    fragmentShader: "tonemap.frag"
}
// In tonemap.frag
vec3 tonemap(vec3 hdr) {
    // Simple Reinhard tonemapping
    return hdr / (hdr + vec3(1.0));
}

void MAIN() {
    vec3 hdrColor = texture(hdrInput, UV0).rgb;
    FRAGCOLOR = vec4(tonemap(hdrColor), 1.0);
}

Qt Quick 3D 在着色器中提供了qt_tonemap() 函数,可用于此目的。

深度纹理生成

当需要将深度作为纹理使用(例如实现景深效果)时,请使用DepthTextureAttachment 而非DepthStencilAttachment :

RenderPassTexture {
    id: depthTex
    format: RenderPassTexture.Depth24Stencil8
}

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

清除值与渲染目标管理

每个渲染通道均可指定清空值:

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

当渲染通道不保留内容(默认)时,渲染目标会在渲染前被清空为这些值。当设置为 `PreserveColorContents ` 或 `PreserveDepthStencilContents ` 时,现有内容将被保留,清空值将被忽略。

图层组织最佳实践

请按逻辑组织图层:

  • 图层0-2:主场景对象
  • 图层 3-4:透明对象(单独的渲染通道)
  • 图层 10 及以上:用于后处理的全屏四边形
  • 图层 15 及以上:UI/调试叠加层

请在注释中记录图层用途,并在整个应用程序中保持一致。

着色器兼容性

渲染通道中使用的增强着色器和自定义材质必须与Qt Quick 3D 的着色器基础设施兼容:

  • 使用与 GLSL 兼容的语法
  • 针对Qt Quick 3D 函数,请包含相应的\#include 指令
  • 注意可用的内置变量
  • 在所有目标平台(OpenGL、Vulkan、Metal、D3D)上进行测试

多渲染目标的限制

使用多个渲染目标(MRT)时:

  • 颜色附件最多为 4 个
  • 所有附件必须具有相同的尺寸
  • 可通过以下方式为每个附件指定混合状态PipelineStateOverride
  • 并非所有硬件都支持 MRT(请检查设备功能)

透明度

在延迟渲染中,透明对象的处理颇具挑战性。常见方法:

  • 针对透明物体采用正向渲染:不透明几何体采用延迟渲染,透明几何体则在单独的正向渲染通道中渲染
  • 单独的透明 G-缓冲区:将透明几何体渲染到一组启用了混合功能的独立 G-缓冲区中
  • 加权混合的顺序无关透明度:使用专门的 OIT 技术

在大多数情况下,为透明物体单独进行一次前向渲染是最简单的做法。

API 参考

下表提供了与用户渲染通道相关的所有类型的完整参考:

特性QML 类型描述
主渲染阶段定义RenderPass定义包含命令和材质模式的渲染通道
渲染目标纹理RenderPassTexture用作渲染目标的纹理(颜色或深度/模板)
纹理输出提供程序RenderOutputProvider将渲染通道输出作为纹理输入提供
图层常量ContentLayer提供图层滤波常数的单例
颜色输出ColorAttachment指定颜色渲染目标
深度/模板(默认)DepthStencilAttachment使用隐式深度/模板缓冲区
深度/模板(纹理)DepthTextureAttachment使用显式深度纹理
对象过滤RenderablesFilter按层和类型过滤对象
管道状态PipelineStateOverride覆盖图形管道状态
嵌套渲染阶段执行SubRenderPass执行另一个渲染通道
着色器定义AddDefine添加着色器预处理器定义
按目标的混合状态renderTargetBlendMRT 的混合配置
显示辅助程序SimpleQuadRenderer将最终输出渲染到View3D
类型描述
CustomMaterial带有顶点/片段着色器的自定义材质
Effect后处理效果
View3D具有renderOverrides 属性的3D视图
Model带有layers 属性的3D模型

示例

另请参阅

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