Untoon Engine

Renderer-Architecture

本页目录

渲染器架构说明

文档目标

这一页给引擎维护者和图形程序员看:untoon 在渲染器层改了哪些地方,读代码时从哪里进。

分支基于 Unreal Engine 5.8。相对上游 release,Untoon 的改动横跨材质、SceneTexture、Deferred Renderer、Nanite compute shading 和 pass 调度,几个部分之间有数据依赖。

相机优先显示集成

新增功能复用 MSM_Untoon,不改变 GBuffer/GBufferToon 或 GPUScene primitive 数据步长。材质资产不需要迁移。

  • 引擎接口:Engine/Source/Runtime/Engine/Classes/Components/UntoonRenderGroupComponent.h;Engine/Source/Runtime/Engine/Public/Materials/MaterialExpressionToonPriority.h。
  • 传统编译与 MIR:Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressionsUntoonVisibility.cpp。CachedExpressionData 保存默认关闭的角色标志,ShaderParameters 复用 PackedFlags16 的空余位,供 shader permutation 与 mesh processor 一致筛选。
  • 优先显示:Engine/Source/Runtime/Renderer/Private/Untoon/UntoonPriorityRendering.cpp;Engine/Shaders/Private/Untoon/UntoonPriorityCommon.ush、UntoonPriorityShader.usf、UntoonPriorityDepth.usf。

渲染组标识位于组件/Proxy 的普通 CPU 字段,上传时按 persistent primitive index 映射到紧凑组索引。图集记录同时包含 GPU view ID、组 ID、源屏幕矩形、图集偏移和有效位;每帧重建,无效记录只返回零覆盖,不借用其他组数据,不占用 CustomDepth/CustomStencil。

Scene.UntoonVisibility 是独立 Scene Uniform Buffer 扩展。Mode 1 的基线 Pass 仅绑定默认空图集,避免对未来目标图集形成 RDG 环依赖;Mode 2 在图集填充后绑定最终参数。原生深度与 Velocity 的 opaque 让位材质必须使用真实材质像素着色器,不能被 position-only/null PS 优化跳过。

优先 Pass 会把整张场景深度保守地重做一遍;组测试的 viewport 保持原视图投影,scissor 限制在角色的屏幕范围内。每组临时深度和目标纹理保持场景尺寸以复用材质坐标,组覆盖再拷入紧凑 R32F 图集;临时资源可由 RDG 复用。最坏情况仍有全场景深度重绘和逐组头发重绘成本,不能把图集尺寸视为全部内存或 GPU 成本。

恢复头发发生在 Nanite 深度导出之后、最终 HZB 和后续着色之前。头发替换可见表面时清空该像素 Nanite shading mask,更新 decal stencil,并清除旧速度;DDM_AllOpaqueNoVelocity 的普通网格 Velocity 延后到最终深度之后。所有新增 mesh shader 有对应 PSO collector,绘制与预缓存共享状态和筛选规则。

LOD 过渡由 UntoonCameraMeshShader 传递每次绘制的判定参数,复现原生 stencil raster/compute 的坐标和阈值规则。完整基线在预深度清除 LOD stencil 前生成;逐组补绘和 Toon 光照不依赖该 stencil 后续仍然存在。Masked 裁剪统一使用原生 coverage 计算,避免额外的硬裁剪破坏抖动遮罩。

支持范围、资源检查和运行验收见优先显示验收。未经编译、编辑器或 GPU 验证的部分仍须在可用构建上确认。

架构层级

untoon 的改动可以按七层来看:

  1. 引擎 API 层
  2. 材质编译与 Shader 生成层
  3. GBuffer / Scene Texture 层
  4. Renderer Pass 层
  5. Nanite post-light export 层
  6. Light Parameter / Scene Parameter 层
  7. Lumen / Ray Tracing / Debug 兼容层

1. 引擎 API 层

涉及的文件:

  • Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h
  • Engine/Source/Runtime/Engine/Classes/Components/LightComponent.h
  • Engine/Source/Runtime/Engine/Classes/Components/LocalLightComponent.h
  • Engine/Source/Runtime/Engine/Classes/Engine/Scene.h

这里定义的内容:

  • MSM_Untoon
  • Untoon 灯光参数
  • Untoon 场景参数
  • Untoon 材质节点对外接口

2. 材质编译与 Shader 生成层

编译与生成相关的文件:

  • Engine/Source/Runtime/Engine/Private/Materials/*
  • Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderGenerationUtil.cpp
  • Engine/Source/Runtime/Engine/Public/MaterialCompiler.h

这一层做的事:

  • Untoon 材质节点编译
  • Toon Light、ToonShadowMask custom output 生成
  • ToonPassSwitch 的 pass-dependent 分支生成
  • GBuffer Slot 分配
  • Shading Model 宏与分支生成

维护时要特别注意 MIR 与传统 HLSL translator 的一致性。Nanite compute material shader 也会依赖材质 custom output 和 pass switch 语义。

3. GBuffer / Scene Texture 层

相关文件:

  • Engine/Source/Runtime/Renderer/Private/SceneTextures.cpp
  • Engine/Source/Runtime/Renderer/Internal/SceneTextures.h
  • Engine/Source/Runtime/RenderCore/Public/GBufferInfo.h
  • Engine/Shaders/Private/SceneTexturesCommon.ush

这里管四件事:

  • 增加 GBufferUntoon
  • 创建 UntoonShadow
  • 提供 UntoonShadowTexture 读取 helper
  • 为 Untoon pass 和 Nanite compute export 提供 SceneTexture 输入

当前 UntoonShadow 通道语义:

  • R:RenderLights 使用红色写掩码写入的表面阴影
  • G:RenderToonShadowMask 写入的材质 Shadow Mask,1 表示阴影
  • B:Shadow blur 使用蓝色写掩码写入的光照遮罩,1 表示光照区域
  • A:Untoon 覆盖标记,1 表示当前像素属于 Untoon shadow mask export 覆盖范围

Nanite ToonLight export 会通过 compute UAV 写回 SceneColor,因此 SceneColor 需要具备 TexCreate_UAV 创建能力。

4. Renderer Pass 层

Pass 调度与实现文件:

  • Engine/Source/Runtime/Renderer/Private/Untoon/ToonBasePassRendering.*
  • Engine/Source/Runtime/Renderer/Private/Untoon/ToonLightPassRendering.*
  • Engine/Source/Runtime/Renderer/Private/Untoon/ToonShadowMaskRendering.*
  • Engine/Source/Runtime/Renderer/Private/Untoon/ToonShadowBlur.*
  • Engine/Source/Runtime/Renderer/Private/Untoon/ToonOutlinePassRendering.*
  • Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.*

这一层的职责:

  • Pass 的注册、调度与执行
  • 普通 mesh 的 Untoon ShadowMask 和 ToonLight mesh pass
  • FToonLightPassLightParameters 的构建
  • Untoon 光照和阴影的独立计算
  • 最终描边合成

当前有效标准顺序是:

  1. 常规 Base Pass 写入 SceneColor、常规 GBuffer 和 GBufferToon
  2. RenderLights 写入 UntoonShadow.R
  3. RenderToonShadowMask 写入 UntoonShadow.G 和覆盖标记 UntoonShadow.A
  4. AddToonMainLightShadowBlurPass 基于 G 的反向光照区域生成晕染,并写入 UntoonShadow.B
  5. RenderToonLightPass 合成 ToonLight 到 SceneColor
  6. Outline / 后处理继续消费 Untoon 数据

RenderToonBasePass 仍在 DeferredShadingRenderer.cpp 中保留调用点,但 ToonBasePassRendering.cpp 当前用 if (false) 禁用了实际执行体。因此它是保留入口,不是当前有效数据流的一环。

实现细节上,Shadow Blur 是在 RenderToonLightPass 准备 FToonLightPassLightParameters 时触发;架构上仍应把它理解为 UntoonShadow.G/A 和 ToonLight 合成之间的中间数据生成步骤。

flowchart LR
    A["Base Pass<br/>GBufferToon"] --> B["RenderLights<br/>UntoonShadow.R"]
    B --> C["RenderToonShadowMask<br/>UntoonShadow.G/A"]
    C --> D["Shadow Blur<br/>UntoonShadow.B"]
    D --> E["RenderToonLightPass<br/>SceneColor"]
    E --> F["RenderToonOutlinePass"]

5. Nanite post-light export 层

Nanite 与 Base Pass 相关文件:

  • Engine/Source/Runtime/Renderer/Private/Nanite/NaniteShading.cpp
  • Engine/Source/Runtime/Renderer/Private/Nanite/NaniteShading.h
  • Engine/Source/Runtime/Renderer/Private/BasePassRendering.*
  • Engine/Shaders/Private/BasePassPixelShader.usf
  • Engine/Shaders/Private/Common.ush

Nanite 不进入普通 EMeshPass::UntoonShadowMaskPass 和 EMeshPass::UntoonLightPass,因此 Untoon 为 Nanite 增加了两条 post-light compute export:

  • DispatchUntoonToonShadowMaskPass
  • DispatchUntoonToonLightPass

Nanite ToonShadowMask export:

  • 从 Nanite base pass shading commands 中筛选 MSM_Untoon 材质
  • 复用 Nanite 可见性、shading mask、shading bins 和材质 bindings
  • 创建可读取的 UntoonShadow snapshot
  • 重新评估 ToonShadowMaskOutput
  • 写入 UntoonShadow.G,并将 UntoonShadow.A 置为覆盖标记

Nanite ToonLight export:

  • 复用 Nanite 可见像素
  • 读取与普通 mesh 相同语义的 PreLightSceneColorCopy / PostLightSceneColorCopy
  • 使用完整 LightPassLight 上下文
  • 重新评估 ToonLightOutput
  • 通过 compute UAV 合成回 SceneColor

维护要求:

  • Nanite 与普通 mesh 的材质编译环境必须表达相同 pass 语义
  • Nanite ShadowMask export 必须能读取 UntoonShadow.R
  • Nanite ToonLight export 必须能读取 PreLightSceneColorCopy / PostLightSceneColorCopy
  • post-light export 只能处理最终可见 Nanite 像素

6. Shader 层

Shader 文件:

  • Engine/Shaders/Private/Untoon/*
  • Engine/Shaders/Private/ShadingModels.ush
  • Engine/Shaders/Private/DeferredLightPixelShaders.usf
  • Engine/Shaders/Private/BasePassPixelShader.usf
  • Engine/Shaders/Private/Common.ush

这里放的是:

  • Untoon 光照函数
  • GBufferToon 材质载荷编码与读取
  • Untoon Shadow Mask 输出
  • pass switch 与 post-light export helper
  • Nanite compute export 的 root flag 判断
  • Outline 计算与合成

重要环境定义:

  • TOONMAINLIGHT
  • TOONLIGHTPASS
  • UNTOON_POST_LIGHT_TOON_SHADOW_MASK_EXPORT
  • UNTOON_ALLOW_UNTOONSHADOW_TEXTURE_READ
  • UNTOON_NANITE_TOON_SHADOW_MASK_EXPORT
  • UNTOON_NANITE_TOON_LIGHT_EXPORT

这些 define 是材质图判断当前阶段的标准接口,不应随意改变语义。

7. 兼容路径

当前分支还触达以下兼容区域:

  • Nanite
  • Lumen
  • Ray Tracing
  • Deferred Lighting
  • Virtual Shadow Maps
  • Pixel Inspector / Debug View

兼容原则:

  • Untoon 不替换 UE 默认 Deferred Lighting 或 Lumen。
  • Untoon 结果主要通过后续 pass 写入 UntoonShadow 和最终 SceneColor。
  • Nanite 几何仍走 Nanite 主路径,Untoon 只补齐 Nanite 缺失的 post-light 材质导出机会。
  • Lumen GI 本身仍按 UE 主路径工作,Untoon 最终合成通常不反向写回 Lumen 解算输入。

调试入口

常用调试开关:

  • r.Untoon.ShadingQuality
  • r.Untoon.ShadowBlur.ShadingQuality
  • r.Untoon.Outline.ShadingQuality
  • r.Untoon.DebugLogNaniteToonShadowMaskExport
  • r.Untoon.DebugForceNaniteToonShadowMaskG
  • r.Untoon.DebugForceNaniteToonLightSceneColor
  • r.Untoon.DebugClearToonShadowMaskG

调试时:

  • 如果 Untoon Shadow Blur 或 Outline 成本过高,先用 r.Untoon.ShadingQuality 做全局降档,再用 pass 级 CVar 单独覆盖。
  • 如果 UntoonShadow.G 为空,先看 ToonShadowMask export 是否 build / dispatch。
  • 如果 G 正确但画面不变,检查 ToonLight export 是否写回 SceneColor。
  • 如果 force 输出有效但材质结果错误,检查材质编译环境和 LightPassLight 绑定。
  • 如果需要新增或整理 RDG、CPU trace、Stats、CSV、LLM 标记,先参考 追踪宏速查 统一命名和宏选择。

推荐阅读顺序

要理解整体架构,按这个顺序读代码:

  1. LightComponent.h
  2. MaterialExpressionToon*
  3. ShaderGenerationUtil.cpp
  4. SceneTextures.cpp 与 SceneTexturesCommon.ush
  5. Renderer/Private/Untoon/*
  6. Renderer/Private/Nanite/NaniteShading.*
  7. Shaders/Private/BasePassPixelShader.usf
  8. Shaders/Private/Untoon/*

源码标记规则

渲染器维护时需要区分 Untoon 自有目录和上游引擎文件:

  • Engine/Shaders/Private/Untoon 不需要 Untoon Engine Start/End marker。
  • Engine/Source/Runtime/Renderer/Private/Untoon 不需要 Untoon Engine Start/End marker。
  • 其他 engine、renderer、shader、runtime 文件中的 Untoon 改动必须位于 marker 内。
  • marker 只保留一层;如果当前代码已经在 marker 内,不要再添加嵌套 marker。

完整说明见 维护者规则。

后续扩展建议

继续加 Untoon 功能时,这几条约束值得保留:

  • 新的材质语义先定义清楚数据归属,再进入 GBuffer 或 SceneTexture
  • 新的渲染阶段优先复用现有 pass 调度框架
  • Nanite 路径不能只复用普通 mesh shader,需要明确 compute material export 语义
  • 新参数需要同时考虑编辑器暴露、Shader 结构体、MIR / HLSL translator 和序列化兼容性
  • 所有新增能力都要标注相对 release 的冲突点