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

842 lines
20 KiB
Markdown
Raw Normal View 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
建议技术路径:
- D3D12`DXC -> DXIL/DXBC`
- Vulkan`DXC -> SPIR-V`
- OpenGL`DXC -> 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 第一阶段必须支持的语法
第一阶段建议正式支持:
```shaderlab
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 的内建分组:
- `UnityPerFrame``XCPerFrame`
- `UnityPerCamera``XCPerCamera`
- `UnityPerDraw``XCPerDraw`
- `UnityPerMaterial``XCPerMaterial`
建议引擎内部最终保留 `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`
- ObjectId`ObjectId`
- FinalOutput`FinalColor`
### 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 的控制
承接关系应当是:
```text
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 体系上。