Files
XCEngine/docs/used/Shader与Material系统下一阶段计划_完成归档_2026-04-04.md

507 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Shader 与 Material 系统下一阶段计划
日期:`2026-04-03`
## 1. 阶段结论
当前 Renderer 这一轮主线已经完成收口,但完成的是“基础运行时闭环”,不是“最终的 Unity 风格 shader/material 体系”。
已经完成并应视为当前基线的内容:
- `Shader` 运行时契约已具备 `properties / passes / resources / backend variants`
- builtin shader 资源已经外置,运行时不再依赖 C++ 内嵌 fallback
- `Material` 对 builtin forward 的基础属性解析已经优先走 shader semantic
- `BuiltinForwardPipeline` 已按 shader pass `resources` 合约生成 pipeline layout 与 descriptor binding
- `Shader` 已接入 `AssetDatabase / Library` 流程shader import 会生成 `main.xcshader`
- `ShaderLoader` 已支持加载 `.xcshader` artifact
- shader manifest 依赖与 `Material -> Shader` 依赖已进入资产追踪链
- `rendering_unit_tests`
- `rendering_integration_textured_quad_scene`
- `rendering_integration_backpack_lit_scene`
- `shader_tests`
- `material_tests`
三后端验证当前是稳定的:
- D3D12通过
- OpenGL通过
- Vulkan通过
但这仍然只是“Shader / Material Runtime 的第一层骨架”。
当前真正还没有完成的是:
- Unity 风格的 shader authoring 入口
- 正式的 material schema / instance contract
- 从 shader property layout 到 GPU material layout 的通用映射
- 从 builtin forward 扩展到更通用 pass 的正式执行模型
## 2. 旧计划归档说明
以下两份计划文档已经完成历史使命,应归入 `docs/used/`
- `Renderer模块设计与实现.md`
- `Renderer下一阶段_ShaderMaterial与Pass体系设计.md`
原因不是它们“写错了”,而是:
- 第一份解决的是 Renderer 模块从无到有的问题,当前骨架已经落地
- 第二份解决的是“为什么下一阶段先做 shader/material/pass contract而不是 render graph”的阶段判断这份判断已经部分兑现整体已过期
本文件接手它们之后的主线,只保留对当前 checkout 仍然有效的目标。
## 3. 当前真实问题
### 3.1 Shader 运行时有了,但 authoring 还没有正式化
现在的运行时已经能消费:
- shader property
- pass tag
- pass resource binding
- backend variant
但作者侧仍然不是最终形态。
当前还缺:
- 面向用户的 Unity 风格 `.shader` 语法入口
- import 阶段把 authoring 语法转换为 runtime contract 的正式流程
- shader 资产与 Library artifact 的稳定产物边界
### 3.2 Material 仍偏“资源容器”,还不是正式材质实例系统
当前 `Material` 已有:
- shader 引用
- render state
- property / texture 覆盖
- tag / queue
但它还缺少真正用于 Renderer 执行的正式约束:
- 基于 shader property schema 的类型验证
- property 默认值与 override 的统一解析
- per-pass material constant layout
- texture / sampler / buffer 到 pass resource 的正式映射
- renderer 侧可缓存、可失效、可复用的 material binding plan
### 3.3 现有 pass contract 仍偏 builtin-forward 视角
当前 forward 主链已经能跑,但完整的 pass contract 还没有正式化为可扩展系统。
后续至少要能稳定承接:
- `ForwardLit`
- `Unlit`
- `DepthOnly`
- `ShadowCaster`
- `ObjectId`
否则后面一旦开始做阴影、深度预通道、更多 editor/runtime helper pass就会重新退化回 pipeline 内部的条件分支拼装。
### 3.4 三后端问题的本质不是“语法不同”,而是“资产如何统一”
当前真正要解决的,不是简单回答“到底用 GLSL 还是 HLSL”而是明确三层边界
1. 对外 authoring 语法是什么
2. import 后的内部运行时资产是什么
3. 每个 backend 最终执行的 variant 是什么
这三层不分开,后面一定会把 authoring、runtime、backend 编译链搅在一起。
## 4. 下一阶段的目标
下一阶段只做一件事:把 `Shader``Material` 从“能支撑当前 builtin forward 的运行时拼装”升级为“能长期承接 Unity 风格渲染架构的正式系统”。
### 4.1 对外 authoring 语法目标:严格向 Unity 对齐
最终对外公开的 shader 语法目标,必须与 Unity 的使用方式保持一致。
目标形态应当是:
- 单个 `.shader` 文件作为逻辑 shader 入口
- `Shader / Properties / SubShader / Pass` 的层级结构
- pass 内通过 `HLSLPROGRAM ... ENDHLSL` 或等价块组织代码
- 通过 `#pragma vertex` / `#pragma fragment` 指定 stage 入口
也就是说:
- 对外 authoring 视角应当是“Unity 风格的一体化 shader 文件”
- 不是要求作者直接去维护一堆 runtime JSON manifest
- 也不是让上层逻辑直接感知 D3D12/OpenGL/Vulkan 各自的底层差异
### 4.2 对内 runtime 资产目标:继续保留 contract 模型
虽然 authoring 目标要严格向 Unity 靠拢,但 runtime 不应直接拿 authoring AST 当执行数据。
运行时仍应落到清晰的 contract
```text
Shader Asset
-> Properties
-> Passes
-> Tags
-> Resource Bindings
-> Backend Variants
```
原因很直接:
- Renderer 需要稳定、扁平、可缓存的数据结构
- 三后端最终执行的仍然是 backend variant
- material schema 与 pass binding 都需要基于 import 结果,而不是原始文本
结论是:
- 外部写法要像 Unity
- 内部执行模型继续使用现在这套 runtime contract并进一步完善
### 4.3 Material 目标:从资源对象升级为正式材质实例
Material 下一阶段的核心不是“多支持几个 SetFloat”而是建立正式实例语义。
Material 至少要明确:
- 引用哪个 shader
- 使用哪个 pass 或 pass 策略
- 每个 property 的默认值、覆盖值、类型与序列化规则
- 每个 pass 对应的 material constant block 如何布局
- 每个 texture / sampler / buffer 如何映射到 pass resource
Renderer 侧则必须能把这些内容稳定编译成:
- material binding key
- material constant payload
- descriptor update plan
- pipeline compatibility key
## 5. 推荐架构
### 5.1 分成三层,不混写
推荐明确拆成三层:
```text
Unity-like Shader Authoring (.shader)
-> Shader Importer
-> Runtime Shader Contract
-> Backend Variants / Material Binding Plan
```
三层职责分别是:
- authoring 层:给人写、给 editor 看
- importer 层:把 authoring 语法转成稳定运行时资产
- runtime 层:给 renderer 执行、缓存、绑定、测试
### 5.2 Shader 的 public contract 与 backend contract 分离
下一阶段不建议把“Unity 风格语法”直接等同于“单源码自动跨平台编译已完全成熟”。
更务实的路线是:
- public contract 先统一成 Unity 风格 `.shader`
- importer 先产出统一 runtime contract
- backend variant 暂时仍允许按 backend 持有各自的编译输入或中间产物
这意味着:
- 作者看到的是统一 shader
- Renderer 消费的是统一 runtime contract
- backend 最终执行的仍然可以是不同 variant
这个分层既不违背 Unity 风格目标,也不会过早把工程拖进复杂的全平台 shader 编译链泥潭。
### 5.3 Vertex / Fragment 的外部写法按 Unity 组织,内部可拆分
对外语义上vertex / fragment 应当属于同一个 pass。
也就是说public authoring 角度要符合 Unity
- 一个 shader
- 一个或多个 subshader
- 一个或多个 pass
- 每个 pass 里通过 pragma 指定 vertex / fragment
但内部 import/runtime 完全可以把它们拆成:
- pass descriptor
- vertex stage variant
- fragment stage variant
外部合一,内部拆开,这是最稳妥的做法。
## 6. 下一阶段实施顺序
### 阶段 D0先打通 Shader Import / Artifact 基础设施(已完成)
这是当前 checkout 在 `2026-04-03` 已经完成的新增里程碑。
完成内容:
- `ShaderImporter` 已接管 `.shader / .hlsl / .glsl / .vert / .frag / .geom / .comp`
- shader import 结果会写入 `Library/Artifacts/.../main.xcshader`
- `ShaderLoader` 已支持直接读取 `.xcshader`
- shader manifest 中声明的 stage 源文件会进入依赖追踪
- `Material` 对引用 shader 的直接依赖也已进入依赖追踪
- `shader_tests``material_tests` 已覆盖 shader artifact 生成、加载与重导场景
这一步的意义不是“最终方案已完成”,而是先把 shader 纳入和 texture/material/mesh 一致的资产闭环。
没有这一步,后续不管做 Unity 风格 frontend还是做 material schema都会一直建立在“运行时临时解析源码”的不稳定基础上。
### 阶段 0当前基线确认
这部分已经完成,不再作为待办:
- runtime shader contract 已建立
- builtin forward 已按 pass resources 驱动
- 三后端渲染回归通过
### 阶段 A建立 Unity 风格 Shader Authoring Frontend
目标:
- 新增 Unity 风格 `.shader` authoring 入口
- importer 能解析最小闭环子集
第一批建议支持的子集:
- `Shader`
- `Properties`
- `SubShader`
- `Tags`
- `Pass`
- `HLSLPROGRAM / ENDHLSL`
- `#pragma vertex`
- `#pragma fragment`
交付标准:
- builtin `forward-lit`
- builtin `unlit`
- builtin `object-id`
- builtin `infinite-grid`
至少迁移其中一条主线并成功跑通三后端测试。
### 阶段 B建立正式的 Material Schema 与 Instance Contract
目标:
- shader importer 输出可供 material 消费的 property schema
- material 对 property override 做类型校验与默认值回退
- 为 pass 生成 material constant layout 与 resource mapping
交付标准:
- material 不再只靠 builtin alias 名字兜底
- shader property semantic 变成正式主路径,而不是兼容性补丁
- renderer 能从 shader schema 生成 material binding payload
当前建议把这一阶段作为下一步主线。
原因:
- shader artifact 与依赖追踪已经到位shader 现在可以作为稳定 schema 来源
- material 仍然缺少基于 shader property 的正式类型校验、默认值回退和资源映射
- renderer 目前虽然能消费 pass resources但 material binding 仍偏 builtin-forward 特判
当前进展(`2026-04-03`
- 已完成shader schema 驱动的 property 类型校验与默认值回退
- 已完成source `.material``properties` authoring 入口
- 已完成material constant layout runtime contract
- `Material` 现在会生成正式的 constant layout 元数据
- layout 字段包含 `name / type / offset / size / alignedSize`
- renderer 读取的已不再只是裸字节 payload而是 `layout + payload` 组合
- 已完成builtin pass resource mapping / material binding plan runtime contract
- `RenderMaterialUtility` 现在统一提供 `PassResourceBindingLocation / BuiltinPassResourceBindingPlan`
- 显式 shader pass `resources` 与 legacy builtin forward fallback 已走同一套解析与校验路径
- `BuiltinForwardPipeline` 已改为消费通用 binding plan而不是继续内联 forward 特判绑定逻辑
- 已验证:`rendering_unit_tests` 57/57`material_tests` 51/51
- 下一步:把这套 pass binding plan 继续推到 `Unlit / ObjectId` 等 pass收敛成真正共享的 shader/material 执行边界
### 阶段 C把 Pass Binding 扩展为正式材质执行链路
目标:
- 不只是 builtin forward 能吃 pass resources
- `Unlit``ObjectId` 也逐步切到同一套 shader/material contract
- pipeline state key 与 descriptor binding plan 从“按功能写逻辑”升级到“按 shader pass contract 解析”
交付标准:
- 至少 `ForwardLit + Unlit + ObjectId` 共用同一套 shader/material 执行边界
- 新增 pass 不再默认要求先写一套新的硬编码 binding 路径
当前进展(`2026-04-04`
- 已完成builtin `ObjectId` pass 接入通用 pass binding plan
- builtin object-id shader 已显式声明 `PerObject` 资源合约
- `BuiltinObjectIdPass` 已改为消费通用 binding plan不再硬编码 `set0/binding0` 常量布局
- 显式 shader `resources` 与 legacy object-id fallback 现在走同一套解析与校验路径
- 已完成builtin `Unlit` shader / pipeline 主线接入共享执行边界
- 新增 builtin `unlit` shader 资产与 `BuiltinResources` 入口
- `BuiltinForwardPipeline` 现在会在 `ForwardLit + Unlit` 之间按 material/shader metadata 解析目标 pass
- `Unlit``ForwardLit` 现在共用同一套 input layout、material schema、binding plan 与 descriptor 组装路径
- 已完成:抽出 `ForwardLit / Unlit / ObjectId` 共用的 pass layout / descriptor set 组装骨架
- `RenderMaterialUtility` 现在统一提供 `BuiltinPassSetLayoutMetadata``TryBuildBuiltinPassSetLayouts(...)`
- `BuiltinForwardPipeline``BuiltinObjectIdPass` 现在都复用同一套 `binding plan -> set layout -> pipeline layout` 组装路径
- forward 侧只保留 set0 compatibility fallback 与 draw/update 逻辑object-id 侧只保留自身约束校验与 per-object 常量写入
- 已完成builtin `DepthOnly / ShadowCaster` shader 与独立 pass skeleton 落地
- 新增 builtin `depth-only``shadow-caster` shader 资产,以及 `BuiltinResources` 对应入口
- 新增 `BuiltinDepthOnlyPass / BuiltinShadowCasterPass`,作为独立 `RenderPass` 复用同一套 shared pass-layout skeleton
- 两个 pass 当前先收敛到 `PerObject` 常量路径opaque 物体走 builtin fallback`ShadowCaster` 额外尊重 `MeshRenderer.castShadows`
- 这一步解决的是“pass contract 与执行骨架缺失”,还没有把 shadow map / light-space request flow 一次性做完
- 已完成:`CameraRenderRequest + CameraRenderer` 正式接入 `DepthOnly / ShadowCaster` request 流程
- 新增 `depthOnly / shadowCaster` request支持独立 surface、clear flags / clear color以及可选 `RenderCameraData` override
- `CameraRenderer` 现在按 `ShadowCaster -> DepthOnly -> Forward -> ObjectId` 的顺序执行这些请求,`BuiltinDepthStylePassBase` 也会按 request clear flags 清理目标
- 已验证:`rendering_unit_tests` 73/73
- 已验证:`rendering_integration_textured_quad_scene` 3/3D3D12 / OpenGL / Vulkan
- 已验证:`rendering_integration_unlit_scene` 3/3D3D12 / OpenGL / Vulkan
- 已验证:`rendering_integration_object_id_scene` 3/3D3D12 / OpenGL / Vulkan
- 下一步:如果继续沿 renderer 主线收口,优先补 `DepthOnly / ShadowCaster` 的真实 cross-backend integration coverage并决定是否把 shadow-map 结果继续接回 forward lighting 消费链如果先控制范围request 流程这一块已经收口
### 阶段 D扩展 AssetDatabase / Library Artifact 能力
目标:
- 在已完成的 shader artifact 基础上,继续扩展 import 产物边界
- backend variant 的编译输入、中间产物或缓存策略进入 `Library/Artifacts`
- 为后续 Unity 风格 `.shader` frontend 预留稳定 importer 输出层
交付标准:
- 资源改动能够稳定触发 shader/material 重新导入
- editor/runtime 读取的是 import 后资产,不是源码临时解析
- shader importer 不再只服务当前 JSON manifest 兼容路径,也能承接下一步 authoring frontend
### 阶段 E测试与回归收口
目标:
- 给 shader importer、material schema、binding plan 增加 unit tests
- 对 forward/unlit/object-id 增加 integration coverage
- 保持 D3D12 / OpenGL / Vulkan 一致回归
最低验证集:
- `shader_tests`
- `material_tests`
- `rendering_unit_tests`
- `rendering_integration_textured_quad_scene`
- `rendering_integration_backpack_lit_scene`
必要时新增:
- `rendering_integration_unlit_scene`
- `rendering_integration_object_id_scene`
当前进展(`2026-04-04`
- 已完成:`rendering_integration_unlit_scene`
- 显式使用 builtin `unlit` shader + `Unlit` pass覆盖 `BuiltinForwardPipeline``ForwardLit / Unlit` pass 选择路径
- 复用 `textured_quad_scene` 构图做最小差异回归,验证 shader/material 新 contract 不改变既有画面输出
- 已验证:`rendering_integration_unlit_scene`
- D3D12通过
- OpenGL通过
- Vulkan通过
- 已完成:`rendering_integration_object_id_scene`
- 新增独立的 object-id integration scene直接验证 object-id 输出采样点,而不是再维护一张新的大尺寸 GT 图片
- 覆盖 `CameraRenderer + BuiltinObjectIdPass` 路径,以及 `Forward -> ObjectId -> Copy/Screenshot` 的跨 pass 回归
- 修正了 `BuiltinObjectIdPass` 在 Vulkan 下的顶点输入步长问题,并让 object-id pass 自己清理/写入 depth避免依赖前一 pass 的 depth 复用状态
- 已验证:`rendering_integration_object_id_scene`
- D3D12通过
- OpenGL通过
- Vulkan通过
- 阶段 E 当前状态:`unlit_scene``object_id_scene` 均已完成并通过三后端验证,本阶段可以收口
## 7. 当前阶段明确不做
下一阶段不应把范围扩散到下面这些方向:
- render graph
- shader graph
- 全平台单源码自动转译一次性做完
- 完整 SRP scripting API
- 大规模后处理框架
这些都依赖 shader/material 正式体系先稳定下来。
## 8. 成功标准
这个阶段完成时,应该满足下面几个判断:
- 作者侧已经可以写 Unity 风格的 `.shader`
- runtime 已不再依赖手写 JSON manifest 才能描述 pass contract
- material 能基于 shader schema 做正式绑定,而不是 builtin 特判兜底
- 至少 `ForwardLit / Unlit / ObjectId` 三类 pass 共用统一 shader/material 执行边界
- 三后端回归测试仍稳定通过
## 9. 一句话总结
下一阶段不是继续给 builtin forward 打补丁,而是把 `Shader``Material` 正式提升为 Unity 风格渲染架构中的稳定中层资产与执行契约。
## 10. 快速收口策略(`2026-04-04`
目标收窄为只处理 `shader / material` 核心主线不继续扩散到完整阴影功能、render graph、shader graph 或 editor 外围能力。
按下面顺序收口:
1. 先完成 Unity-like `.shader` authoring MVP importer
- 允许最小子集:`Shader / Properties / SubShader / Tags / Pass / HLSLPROGRAM / #pragma vertex / #pragma fragment`
- importer 输出继续复用当前 runtime shader contract`properties / passes / resources / backend variants`
- 这一阶段不追求完整 Unity ShaderLab只做 builtin 与主线材质系统需要的最小闭环
2. 再迁移 builtin shader 到新 authoring 入口
- 优先 `ForwardLit / Unlit / ObjectId / DepthOnly / ShadowCaster`
- 要求 importer 产出的 runtime contract 与当前 renderer 消费路径保持一致
3. 然后收紧 material 主路径
- 以 imported shader schema 为主路径完成 property 类型校验、默认值回退、constant layout 与 resource mapping
- builtin alias / canonical-name fallback 只保留兼容兜底,不再作为主执行路径
4. 最后做最小回归集收口
- `shader_tests`
- `material_tests`
- `rendering_unit_tests`
- 必要的 rendering integration smoke
当前进展(`2026-04-04`
- 已完成Step 1 `Unity-like .shader authoring MVP importer`
- `ShaderLoader` 新增 Unity-like `.shader` authoring 识别与解析入口,但保留现有 JSON manifest `.shader` 兼容路径不动
- importer 继续落到现有 runtime shader contract`properties / passes / resources / backend variants`
- `CollectSourceDependencies` 已覆盖新 authoring 路径,`AssetDatabase` 会继续追踪各 backend stage 文件依赖并参与重导入
- 已完成Step 2 `builtin shader 迁到新 authoring 入口`
- `forward-lit / unlit / object-id / depth-only / shadow-caster` 五个 builtin `.shader` 入口已全部切到 Unity-like authoring
- stage 源文件、builtin shader 路径与 renderer 消费 contract 保持不变,迁移只发生在 authoring 入口层
- 补充 `DepthOnly / ShadowCaster` builtin shader loader 覆盖,确保五类 builtin pass 都经过新 authoring 路径验证
- 已完成Step 3 `material 主路径收紧到 imported shader schema`
- `MaterialLoader` 在存在 shader schema 时,`properties / textures` 中的旧 key 会先解析到 shader property semantic再落到 shader 正式属性名
- 旧的 `baseColor / baseColorTexture` 等兼容 key 仍可导入,但只作为兼容输入存在,不再直接污染 material 主执行路径
- 当 material 绑定 shader schema 后,未知 texture binding 现在会被显式拒绝,不再静默忽略
- 已验证:`shader_tests` 中新增 authoring 直载与 artifact/reimport 覆盖
- 已验证:`shader_tests` 31/31 通过builtin `ForwardLit / Unlit / ObjectId / DepthOnly / ShadowCaster` 全部通过加载与 backend variant 覆盖
- 已验证:`material_tests` 53/53 通过schema 驱动的 property/texture 映射与未知 binding 拒绝路径都已覆盖
- 已完成Step 4 `最小回归集收口`
- `rendering_unit_tests` 73/73 通过builtin pass contract 与 renderer 侧消费路径在当前重构后保持稳定
- 本轮最小回归集结果:`shader_tests` 31/31`material_tests` 53/53`rendering_unit_tests` 73/73
- 以当前目标范围看,`shader / material` 核心主线已经可以阶段性收口
- 补充验收(`2026-04-04`
- 补跑 integration smoke 时发现并修复了一处真实回归Unity-like `.shader` authoring 将 HLSL 的 `MainVS / MainPS` 入口错误套到了 GLSL/Vulkan variant上层表现为 Vulkan `BuiltinForwardPipeline` 建 pipeline 失败
- 修复后补跑 `rendering_integration_textured_quad_scene``rendering_integration_unlit_scene``rendering_integration_object_id_scene`,三者在 `D3D12 / OpenGL / Vulkan` 下均通过
- 额外补跑 `rendering_integration_backpack_lit_scene`暴露出一条相邻但不同范围的问题direct `MeshLoader` 导入的 mesh/material/texture 生命周期路径存在异常退出/不干净收尾,已超出本次 `shader / material contract` 收口范围
- 因此本计划按“核心主线已收口,附带一条相邻 residual risk”归档若后续继续补强优先单独开一轮处理 `MeshLoader + imported subresource lifetime`
- 下一步:从当前目标范围出发,本计划可归档;后续如继续推进,请以新计划承接 `backpack_lit_scene` 这条相邻问题
当前阶段明确不做:
- 完整阴影贴图消费链
- render graph
- shader graph
- 完整 Unity ShaderLab 全语法
- 与 shader/material 主线无关的 editor/ui 扩展