Files
XCEngine/docs/plan/Shader与Material系统下一阶段计划.md

15 KiB
Raw Blame History

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. 下一阶段的目标

下一阶段只做一件事:把 ShaderMaterial 从“能支撑当前 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

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 分成三层,不混写

推荐明确拆成三层:

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_testsmaterial_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 .materialproperties 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/57material_tests 51/51
  • 下一步:把这套 pass binding plan 继续推到 Unlit / ObjectId 等 pass收敛成真正共享的 shader/material 执行边界

阶段 C把 Pass Binding 扩展为正式材质执行链路

目标:

  • 不只是 builtin forward 能吃 pass resources
  • UnlitObjectId 也逐步切到同一套 shader/material contract
  • pipeline state key 与 descriptor binding plan 从“按功能写逻辑”升级到“按 shader pass contract 解析”

交付标准:

  • 至少 ForwardLit + Unlit + ObjectId 共用同一套 shader/material 执行边界
  • 新增 pass 不再默认要求先写一套新的硬编码 binding 路径

当前进展(2026-04-03

  • 已完成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
    • UnlitForwardLit 现在共用同一套 input layout、material schema、binding plan 与 descriptor 组装路径
  • 已验证:rendering_unit_tests 61/61shader_tests 27/27material_tests 51/51
  • 下一步:评估是否抽出 ForwardLit / Unlit / ObjectId 共用的 pass layout 构建与 descriptor set 组装骨架,并继续推进 DepthOnly / ShadowCaster

阶段 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覆盖 BuiltinForwardPipelineForwardLit / Unlit pass 选择路径
    • 复用 textured_quad_scene 构图做最小差异回归,验证 shader/material 新 contract 不改变既有画面输出
  • 已验证:rendering_integration_unlit_scene
    • D3D12通过
    • OpenGL通过
    • Vulkan通过
  • 下一步:补 rendering_integration_object_id_scene 或等价的 object-id integration coverage完成本阶段收口

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 打补丁,而是把 ShaderMaterial 正式提升为 Unity 风格渲染架构中的稳定中层资产与执行契约。