Files
XCEngine/docs/used/Renderer下一阶段_Unity风格Shader体系正式化计划_完成归档_2026-04-07.md

20 KiB
Raw Permalink Blame History

Renderer 下一阶段Unity 风格 Shader 体系正式化计划

日期:2026-04-06

1. 阶段结论

当前 renderer 主线已经可以阶段性收口,但 Shader / Material / Pass 体系还没有真正统一。

现在仓库里的 shader 体系处于一个过渡态:

  • 逻辑上已经有 Shader -> Pass -> Variant 模型
  • authoring 外观上已经接近 Unity ShaderLab
  • 运行时已经能按 pass 和 backend variant 选择 shader
  • 三后端已经能稳定跑通 builtin shader

但它仍然不是 Unity 风格的正式体系,因为当前 authoring 仍然暴露了太多 backend 与 binding 细节:

  • .shader 文件里仍然显式写 #pragma backend D3D12/OpenGL/Vulkan
  • .shader 文件里仍然显式写 Resources { name(type, set, binding) }
  • material 仍然可以显式指定 shaderPass
  • shader 关键字、变体、include、pass state、SubShader 选择还没有真正成体系

所以这一阶段的主线不是继续做新渲染效果,而是把 shader 体系升级成:

  • authoring 层完全按 Unity 风格书写
  • 三后端编译路径统一收敛到 importer / compiler 层
  • runtime 只消费正式 shader artifact / material contract

这一步不是 SRP 本身,但它是后续 SRP 能否成立的硬地基。


2. 阶段目标

本阶段要达成的核心目标只有一件事:

把当前“伪 ShaderLab + 后端直连”的过渡体系升级为“Unity 风格 authoring + 引擎内部统一 shader IR + 三后端编译产物”的正式体系。

完成后应达到:

  1. shader authoring 采用 Unity 风格 .shader 语法,不再要求作者显式写 backend variant 分发表。
  2. 新语法不再要求作者手写 set/binding 级资源绑定表。
  3. HLSL 成为 raster shader 的单一 authoring 语言源。
  4. D3D12 / Vulkan / OpenGL 的差异退到 importer / compiler / artifact 层。
  5. material 只管 property / keyword / texture / render state不再负责点名 pass。
  6. renderer 按 LightMode / pass contract 选 pass而不是靠 material 的临时字符串兜底。
  7. builtin shader 全部迁移到新体系并保持三后端回归稳定。
  8. 保留 legacy shader 兼容层,避免一次性炸掉现有工程内容。

3. 非目标

本阶段明确不做:

  • SRP 本体
  • render graph
  • deferred / clustered
  • Shader Graph
  • Surface Shader
  • 完整 Unity ShaderLab 100% 全语法一次性覆盖
  • volume 系统
  • 更多新渲染效果

本阶段也不追求一口气把 Unity 的所有 authoring 特性补完,例如:

  • Fallback
  • UsePass
  • CustomEditor
  • GrabPass
  • tessellation / geometry / ray tracing authoring

这些可以后续补,但不应阻塞当前阶段把“统一 shader 体系”先做正确。


4. 当前真实状态与根本问题

4.1 当前已经具备的能力

当前工程已经具备:

  • .shader 文件解析能力
  • Shader 的 pass / property / resource / variant 数据模型
  • builtin shader 资产化
  • material property / texture / render state / tag 载体
  • renderer 中按 builtin pass metadata 选择可用 shader pass

这意味着当前不是从零开始设计。

4.2 当前最根本的问题

当前最大的问题不是“没有 shader 体系”而是“authoring 层和 runtime 层的边界还没收干净”。

具体表现为:

  1. authoring 层暴露后端差异

    • .shader 文件直接写 #pragma backend D3D12/OpenGL/Vulkan
    • shader 作者必须知道后端与源码文件映射
  2. authoring 层暴露 RHI binding 细节

    • .shader 文件显式写 Resources { ConstantBuffer / Texture2D / Sampler, set, binding }
    • 这更像 Vulkan/D3D12 binding 清单,不是 Unity 风格 shader authoring
  3. material 还带着临时 pass 选择职责

    • Material::SetShaderPass() 仍然存在
    • renderer 仍然优先吃 material 显式指定的 pass
    • 这会阻碍未来 RendererFeature / SRP 规范化
  4. 关键字与变体体系缺失

    • 没有正式的 multi_compile / shader_feature
    • 没有变体剥离与编译缓存策略
  5. include 与共享库体系缺失

    • 没有正式的 shader include 搜索路径、预处理、公共库组织
  6. pass state 仍然不在 shader authoring 的统一语义内

    • 诸如 Cull / ZWrite / ZTest / Blend / ColorMask / Stencil 还没有完整进入 shader authoring contract
  7. 三后端仍然是物理三套源码直连

    • 当前虽然“逻辑上一个 shader asset”
    • 但作者本质上还在维护三套 shader stage 文件

5. 核心设计结论

5.1 目标不是“看起来像 Unity”而是“真正采用 Unity 风格 authoring 模型”

最终目标应当是:

  • 一个 .shader 文件描述一个逻辑 shader
  • shader 内部有 Properties / SubShader / Pass / Tags / State / Program
  • renderer 消费的是 import 后的统一 IR / artifact
  • backend 差异不暴露给 shader 作者

5.2 HLSL 作为单一 authoring 语言源

本阶段必须明确:

  • 新体系下 raster shader 统一使用 HLSL authoring
  • D3D12 直接编 HLSL
  • Vulkan 由 HLSL 编到 SPIR-V
  • OpenGL 由 HLSL 编到 SPIR-V再转 GLSL 430

原因:

  • 如果 authoring 仍然保留 GLSL/HLSL 三套并行,永远不可能真正统一写法
  • 只有 single-source authoring才能接近 Unity 的真实体验

5.3 backend 差异必须退到 importer / compiler 层

新 authoring 文件中不应再出现:

  • #pragma backend ...
  • backend 专属 stage 文件路径表

这些内容应由 importer 根据 target backend 生成产物。

5.4 resource binding 不再由 shader 作者手写 Resources(set,binding)

Unity 风格 shader authoring 不要求作者手写 descriptor set / binding。

因此新体系下应改为:

  • material 暴露属性来自 Properties
  • engine 内建 constant buffer / texture / sampler 来自约定与 reflection
  • importer 通过 HLSL reflection + 约定库推导 runtime resource layout

也就是说:

  • authoring 层写“语义”
  • importer 层生成“绑定布局”
  • runtime 层消费“绑定布局”

5.5 pass 选择必须回归 renderer而不是 material

新体系中:

  • material 只绑定 shader 与 property / keyword / texture
  • renderer 按 LightMode 选 pass
  • Material::shaderPass 进入弃用与最终移除路径

这与 Unity 的 ShaderTagId / LightMode 思路对齐,也更利于未来 SRP。

5.6 必须保留 legacy 兼容层

当前仓库已经有一批 builtin shader 和测试资产。

因此不能激进地“一刀切重做”,而应:

  • legacy .shader 继续可加载
  • 新 Unity 风格 .shader 进入新 importer 路径
  • builtin shader 分批迁移
  • runtime 统一落在同一套 Shader / artifact / variant 模型上

6. 目标架构

建议把 shader 体系正式分成 5 层。

6.1 Authoring 层

职责:

  • 让开发者以 Unity 风格书写 shader

建议语法子集:

  • Shader
  • Properties
  • SubShader
  • Pass
  • Tags
  • LOD
  • HLSLINCLUDE
  • HLSLPROGRAM
  • ENDHLSL
  • #pragma vertex
  • #pragma fragment
  • #pragma target
  • #pragma multi_compile
  • #pragma shader_feature
  • #pragma shader_feature_local
  • Cull
  • ZWrite
  • ZTest
  • Blend
  • ColorMask
  • Stencil
  • Offset

6.2 Importer / Parser 层

职责:

  • 解析 Unity 风格 .shader
  • 生成统一的内部 ShaderIR

建议引入:

  • ShaderAuthoringParser
  • ShaderIR
  • ShaderSubShaderIR
  • ShaderPassIR
  • ShaderKeywordDecl
  • ShaderPassStateDesc
  • ShaderProgramIR

6.3 Compiler / Reflection 层

职责:

  • 编译 authoring 中的 HLSL
  • 为不同 backend 生成最终编译产物
  • 生成 resource layout / constant layout / keyword variant metadata

建议技术路径:

  • D3D12DXC -> DXIL/DXBC
  • VulkanDXC -> SPIR-V
  • OpenGLDXC -> SPIR-V -> SPIRV-Cross -> GLSL 430

输出:

  • 每个 pass / stage / keyword-set / backend 的编译产物
  • 反射出的 constant buffer / texture / sampler 布局

6.4 Artifact 层

职责:

  • 保存运行时真正消费的 shader 产物

建议引入新版 artifact

  • xcshader2 或继续升级现有 xcshader

artifact 内容至少包含:

  • shader 名称 / guid
  • properties
  • subshader / pass tags
  • pass state
  • keyword declarations
  • keyword variant table
  • backend binaries / backend source payload
  • reflected resource layout
  • include 依赖与 hash

6.5 Runtime 层

职责:

  • renderer 根据 pass contract、keywords、backend 选择最终 variant
  • material 根据 property/texture 生成常量与资源绑定
  • pipeline cache 根据 shader variant + render state 建 key

7. Unity 风格 authoring 范围定义

7.1 第一阶段必须支持的语法

第一阶段建议正式支持:

Shader "XCEngine/Example/Lit"
{
    Properties
    {
        _BaseColor ("Base Color", Color) = (1,1,1,1)
        _BaseMap ("Base Map", 2D) = "white" {}
        _Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5
    }

    HLSLINCLUDE
    #include "ShaderLibrary/Core.hlsl"
    ENDHLSL

    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="ForwardLit" }
            Cull Back
            ZWrite On
            ZTest LEqual
            Blend One Zero

            HLSLPROGRAM
            #pragma target 4.5
            #pragma vertex Vert
            #pragma fragment Frag
            #pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
            #pragma shader_feature_local _ XC_ALPHA_TEST
            ENDHLSL
        }
    }
}

7.2 第一阶段暂不支持的语法

第一阶段可暂缓:

  • Fallback
  • UsePass
  • GrabPass
  • CustomEditor
  • Category
  • Dependency
  • Surface Shader
  • CG fixed-function 时代遗留语义

这些需要列入兼容性说明,但不应阻塞首版落地。


8. 统一的 shader library 与 include 体系

这一层是“写法统一”能否成立的关键。

8.1 必须引入正式的 include 库

建议新增:

  • engine/assets/shaderlib/ShaderLibrary/Core.hlsl
  • engine/assets/shaderlib/ShaderLibrary/Common.hlsl
  • engine/assets/shaderlib/ShaderLibrary/SpaceTransforms.hlsl
  • engine/assets/shaderlib/ShaderLibrary/Lighting.hlsl
  • engine/assets/shaderlib/ShaderLibrary/MaterialInput.hlsl
  • engine/assets/shaderlib/ShaderLibrary/Shadow.hlsl

目标:

  • builtin shader 共用统一宏与公共函数
  • authoring 层不再重复声明一堆 per-object / lighting 结构

8.2 统一的内建常量组

建议统一为接近 Unity 的内建分组:

  • UnityPerFrameXCPerFrame
  • UnityPerCameraXCPerCamera
  • UnityPerDrawXCPerDraw
  • UnityPerMaterialXCPerMaterial

建议引擎内部最终保留 XC* 前缀实现名,但 authoring 宏层提供 Unity 风格别名。

8.3 内建纹理/采样器由 include 库与 reflection 管理

例如:

  • 主纹理
  • 阴影图
  • 环境图
  • sampler state

这些都不再由 .shader 手填 Resources(set,binding)


9. Material 体系同步改造

shader 统一如果不带 material 一起改,最后会停在半路。

9.1 material 的职责边界

新体系里 material 负责:

  • 选择 shader
  • 保存 property override
  • 保存 texture override
  • 保存 keyword 开关
  • 保存材质级 render queue / tag override如保留

新体系里 material 不再负责:

  • 指定 shaderPass
  • 硬编码 backend 资源名
  • 猜测 shader 内的 descriptor set / binding

9.2 material constant buffer 正式化

必须建立:

  • Properties -> UnityPerMaterial/XCPerMaterial 布局
  • importer 生成 property layout
  • material 按 layout 打包 GPU 常量
  • layout/hash 进入 pipeline/material cache key

9.3 keyword 体系正式化

建议引入:

  • global keywords
  • local keywords
  • material keyword set
  • variant lookup key

material 应持有:

  • ShaderKeywordSet

renderer 运行时根据:

  • shader
  • pass
  • keyword set
  • backend

选择最终 shader variant。


10. Renderer 运行时契约调整

10.1 pass 选择规则统一为 LightMode

renderer 选 pass 时只看:

  • 当前 pipeline 阶段需要的 LightMode
  • shader/subshader/pass 是否匹配
  • backend 是否有有效 variant

例如:

  • 主几何:ForwardLit / Unlit
  • 阴影:ShadowCaster
  • 深度:DepthOnly
  • ObjectIdObjectId
  • FinalOutputFinalColor

10.2 material 显式 shaderPass 进入弃用路径

建议执行顺序:

  1. 第一阶段保留字段,但标记为 legacy
  2. runtime 优先按 pass contract / LightMode 选 pass
  3. 只有 legacy 资产才允许 fallback 到 shaderPass
  4. migration 完成后移除 shaderPass 主路径职责

10.3 builtin pass metadata 继续保留,但收进 importer/runtime

当前基于 semantic 的 builtin pass binding 解析仍然有价值,但应改成:

  • authoring/IR 层表达语义
  • compiler/reflection 层生成 binding plan
  • runtime 只消费 binding plan

而不是继续靠散落的名字匹配和 fallback。


11. 三后端统一策略

11.1 D3D12

目标:

  • 直接消费 HLSL 编译产物
  • 反射得到 root signature / resource layout 所需元数据

第一阶段可继续沿用 ps_5_0 / vs_5_0,但建议同时规划升级:

  • 后续逐步转 SM 6.x

11.2 Vulkan

目标:

  • 统一吃由 HLSL 编到 SPIR-V 的产物
  • 摆脱独立 .vk.glsl 长期维护

11.3 OpenGL

目标:

  • 不再长期维护独立 .glsl authoring 文件
  • importer 自动生成 OpenGL 目标 GLSL 430

这一步会是整个阶段最大的工程风险之一,但它是“写法统一”绕不开的核心点。

11.4 迁移期间的策略

在新体系落稳前,允许:

  • legacy backend-specific variant 继续存在
  • 新 Unity 风格 shader 走统一 HLSL single-source 路径

两套 importer 并行一段时间,最终再逐步淘汰 legacy path。


12. 分阶段实施计划

Phase A冻结目标与建立兼容边界

目标:

  • 明确“什么叫 Unity 风格 shader”
  • 明确 legacy 与新 authoring 的兼容边界

工作项:

  1. 写清 Unity 风格支持子集。
  2. 明确旧 .shader 的 legacy 模式规则。
  3. 明确新 authoring 中禁止出现:
    • #pragma backend
    • Resources(set,binding)
  4. 明确 material 中 shaderPass 的弃用策略。

完成标准:

  • 文档、命名、兼容边界全部写死

Phase B建立新的 Shader Authoring Parser 与 IR

目标:

  • .shader authoring 能导入到统一 ShaderIR

工作项:

  1. 新增 parser支持
    • Shader / Properties / SubShader / Pass / Tags
    • HLSLINCLUDE / HLSLPROGRAM
    • #pragma vertex / fragment / target / multi_compile / shader_feature
    • pass state DSL
  2. 生成 ShaderIR
  3. 支持 include 依赖收集
  4. 保留 legacy importer

完成标准:

  • authoring parser 单测齐全
  • 可以把一个 Unity 风格 shader 解析成稳定 IR

Phase C建立单一 HLSL 编译链

目标:

  • 打通 HLSL -> D3D12/Vulkan/OpenGL 编译管线

工作项:

  1. 接入 DXC 编译 HLSL。
  2. Vulkan 产出 SPIR-V。
  3. OpenGL 产出 GLSL 430。
  4. 建立 reflection 数据抽取:
    • cbuffer
    • texture
    • sampler
    • entry point
    • keywords
  5. 缓存编译产物与依赖 hash。

完成标准:

  • 一份 HLSL authoring 能生成三后端产物
  • OpenGL 不再依赖手写 .glsl 作为新体系长期主路径

Phase DMaterial 与 property/keyword/runtime binding 正式化

目标:

  • material 能正式驱动新 shader artifact

工作项:

  1. 引入正式 property layout。
  2. 引入 material keyword set。
  3. 生成 PerMaterial 常量缓冲布局。
  4. texture/sampler 绑定从 reflection/约定生成。
  5. 让 material 运行时不再关心 set/binding

完成标准:

  • material property / texture / keyword 真正接入 GPU 绑定链

Phase Erenderer pass 选择与 pipeline cache 收口

目标:

  • renderer 完全按 pass contract 驱动 shader

工作项:

  1. LightMode 选 pass。
  2. shaderPass 降级为 legacy fallback。
  3. pipeline cache key 引入:
    • shader artifact id
    • pass id
    • keyword variant id
    • render state
  4. builtin pass 的 runtime contract 全部切到新 artifact。

完成标准:

  • renderer 主路径不再依赖 material 显式 pass 指定

Phase F分批迁移 builtin shader

建议迁移顺序:

  1. unlit
  2. forward-lit
  3. depth-only
  4. shadow-caster
  5. object-id
  6. skybox
  7. color-scale-post-process
  8. final-color

完成标准:

  • builtin shader 全部有新 authoring 版本
  • 旧版 backend 分发文件不再是长期主定义来源

Phase G文档、测试、旧路径收口

目标:

  • 让新旧体系的边界最终收口

工作项:

  1. 更新 tests/TEST_SPEC.md 中 shader/material 测试矩阵。
  2. 增加 authoring parser / compiler / runtime 回归测试。
  3. 更新开发文档。
  4. 标记 legacy 路径弃用阶段。

完成标准:

  • 新体系文档、测试、builtin 迁移都完成

13. 测试策略

13.1 Parser / Importer 单测

必须覆盖:

  • Properties 解析
  • SubShader / Pass / Tags 解析
  • HLSLINCLUDE / HLSLPROGRAM 解析
  • pragma 解析
  • pass state 解析
  • include 依赖收集
  • legacy / 新 authoring 双路径兼容

13.2 Compiler 单测

必须覆盖:

  • 单一 HLSL 源能生成三后端产物
  • reflection 结果稳定
  • keyword variant 正确展开
  • 编译错误日志可读、可定位到 authoring 源文件

13.3 Material 单测

必须覆盖:

  • property 默认值
  • property override
  • texture binding
  • keyword set
  • 常量缓冲布局打包

13.4 Rendering 单测

必须覆盖:

  • renderer 按 LightMode 选 pass
  • legacy shaderPass fallback 行为
  • keyword variant 参与 pipeline cache key
  • final color / post-process / shadow / object-id 不回退

13.5 集成测试

至少回归:

  • material_state_scene
  • transparent_material_scene
  • camera_stack_scene
  • directional_shadow_scene
  • multi_light_scene
  • skybox_scene
  • post_process_scene
  • final_color_scene
  • object_id_scene

要求:

  • 三后端全部跑通
  • GT 不回退

14. 风险与控制策略

风险 1OpenGL 是统一写法最难的一环

原因:

  • OpenGL 当前直接吃 GLSL
  • 统一 authoring 要求它改为编译链生成目标 GLSL

控制策略:

  • 先把 importer/IR 做好
  • OpenGL 先走“生成 GLSL 文本产物”路径
  • legacy OpenGL GLSL 文件在迁移期保留 fallback

风险 2一次性追 full Unity 语法会把阶段拖爆

控制策略:

  • 明确 first-class 子集
  • 先做 SRP 真正依赖的 authoring 基础
  • 非关键语法延后

风险 3material / pass 迁移会破坏当前 builtin renderer

控制策略:

  • legacy runtime path 保留一段时间
  • builtin shader 分批迁移
  • 每迁移一个 shader 就跑对应 integration

风险 4编译错误如果不可读会极大拖慢落地

控制策略:

  • 必须做 authoring 源到 backend 编译日志的映射
  • 错误日志要带 shader 名、pass 名、stage、backend、源文件行号

15. 收口判定

满足下面条件时,本阶段可视为完成:

  1. .shader authoring 采用 Unity 风格子集。
  2. 新体系 shader authoring 中不再出现 #pragma backend
  3. 新体系 shader authoring 中不再出现 Resources(set,binding)
  4. HLSL single-source 能生成 D3D12 / Vulkan / OpenGL 三后端产物。
  5. material 已正式接入 property / keyword / texture binding runtime。
  6. renderer 按 LightMode 正式选择 pass。
  7. shaderPass 只剩 legacy fallback不再是主路径。
  8. builtin shader 已完成新体系迁移。
  9. 三后端关键集成测试全部通过。

16. 与后续 SRP 的承接关系

这一阶段完成后,才能真正自然地承接:

  • RenderPipelineAsset
  • RenderPipeline
  • RendererFeature
  • ScriptableRenderPass
  • C# 层对 shader/material/keyword 的控制

承接关系应当是:

Unity-style Shader Authoring
    -> Shader Importer / IR / Artifact
    -> Native Material & Pass Runtime
    -> Native Renderer Pass Contract
    -> C# SRP / RendererFeature

也就是说:

  • 这一阶段不是 SRP
  • 但这是 SRP 成立前必须先做完的最后一层底座

17. 一句话总结

当前 shader 体系不是没有,而是还停在“过渡态”。

下一阶段的正确方向不是继续堆更多 shader 功能,而是:

  • .shader 的 authoring 真正统一成 Unity 风格
  • 把 backend 差异与 binding 细节收回 importer / compiler 层
  • 把 material / pass / variant/runtime contract 一次性做正式

只有这样,后面的 SRP 才不会建立在一层伪统一的 shader 体系上。