Files
XCEngine/docs/used/Material Inspector与Shader属性面板收口计划_2026-04-07.md

20 KiB
Raw Blame History

Material Inspector与Shader属性面板收口计划

日期2026-04-07

1. 背景

当前 Rendering / Shader / Material 主线已经基本建立起正式架构:

  • Shader 侧已经具备统一的 authoring/schema 能力。
  • Material 运行时与 artifact 路径已经逐步从旧兼容逻辑中收口。
  • Editor 中的 Material Inspector 仍然没有完全切换到“由 Shader schema 驱动”的正式工作流。

当前最突出的实际问题有两类:

  1. Material 选择 Shader 后Shader 中声明的属性没有被正式暴露到 Inspector 面板上。
  2. Material 面板上还残留了一些历史字段或临时 UI用户无法清晰判断哪些是正式能力哪些只是过渡产物。

此外,当前工程条件已经允许做更彻底的清理:

  • project/ 中旧材质资产可直接编辑。
  • 当前不存在必须长期兼容的历史材质资产包袱。

因此,这一阶段的目标不是“继续兼容旧面板”,而是把 Material 编辑链路彻底切换到正式路径,并为后续 Shader/Material 编辑器扩展打好基础。

2. 本阶段目标

本阶段要完成以下目标:

  1. 让 Material Inspector 成为 Shader schema 驱动的正式编辑入口。
  2. 让材质面板只暴露当前正式架构中的概念,不再混入旧字段和误导性配置。
  3. 保证 Editor 面板、运行时 Material、源资产、artifact、reimport 之间的数据一致性。
  4. 用单测和必要的编辑器验证把这条链路收口,而不是停留在“能显示”层面。

3. 不在本阶段处理的内容

以下内容不属于本阶段主目标,避免范围失控:

  • 自定义 Material Drawer 系统。
  • Shader Graph / 节点式材质编辑器。
  • 面向美术工作流的复杂 Material 预设库。
  • SRP 层级的材质检查器定制机制。
  • 完整的 Shader GUI 仿 Unity 高级扩展体系。

这些能力后续可以做,但必须建立在当前正式链路已经稳定收口的前提上。

4. 当前已确认的问题

4.1 Inspector 没有正式反射 Shader 属性

当前 Material Inspector 还没有完整读取 Shader schema 并按类型生成属性控件,导致:

  • Shader 中声明的属性无法完整暴露。
  • 用户无法直接编辑当前材质真正生效的参数。
  • 材质默认值、覆盖值、纹理槽与资源引用状态之间关系不清晰。

4.2 材质面板仍存在历史路径残留

之前材质层面曾承担过一些本不应由材质承担的职责,例如:

  • 材质自己选择 builtin pass。
  • Inspector 里出现与当前正式架构不一致的旧字段。

这类逻辑已经开始清理,但 Editor 面板仍需同步彻底收口。

4.3 Editor 与运行时 Material 状态可能脱节

如果面板的编辑状态不是直接围绕正式 Material 数据模型建立,就容易出现:

  • Inspector 显示值与运行时实际值不一致。
  • 资源重载后 UI 状态失真。
  • artifact / reimport 后材质参数丢失或回退异常。

4.4 缺少围绕正式工作流的测试闭环

仅靠手工点 Inspector 验证不足以支撑后续迭代。必须补足以下覆盖:

  • Shader 切换后属性集合重建。
  • 默认值与显式覆盖值的切换。
  • Texture 属性与资源引用链路。
  • Keyword 与 Render State 的持久化。
  • artifact/reimport 后数据一致性。

5. 设计原则

本阶段严格遵循以下原则:

5.1 Shader 定义什么Material 就暴露什么

Material 不是独立定义属性结构的地方。属性结构必须由 Shader schema 决定Inspector 只是将其可视化并允许编辑。

5.2 Material 只承载实例数据,不承载管线选择策略

材质应承载:

  • Shader 引用
  • Shader schema 对应的属性值
  • Keyword 状态
  • 合法的 render state 覆盖

材质不应再承载:

  • 独立指定 builtin pass 的旧路径
  • 与 Shader metadata 冲突的临时策略字段

5.3 Editor 不发明第二套数据模型

Inspector 的中间态必须服务于正式数据模型,而不是绕开 Material 自己维护一套平行逻辑。

5.4 面板必须可验证

每一步改动都必须能通过测试或明确的编辑器验证闭环证明正确,不接受只靠目测“看起来差不多”。

6. 分阶段执行计划

Phase 1现状审查与残留路径清点

目标

彻底梳理当前 Material Inspector、Material 资源、Shader schema、artifact 序列化之间的实际数据流。

任务

  • 审查 InspectorPanel 当前 Material 面板的数据来源和写回路径。
  • 审查 Material 当前正式字段与历史遗留字段。
  • 审查 MaterialLoaderAssetDatabase、artifact schema 当前是否仍保留不必要兼容路径。
  • 识别 Material 面板中哪些字段是正式路径,哪些属于旧方案残留。

完成标准

  • 列清楚当前正式数据流。
  • 列清楚必须删除的旧字段/旧 UI。
  • 列清楚缺失的 schema 反射入口。

Phase 2Material Inspector 数据模型收口

目标

让 Material 面板的数据模型只围绕正式架构组织。

任务

  • 移除 Material 层面的旧 pass 选择 UI 与残余兼容路径。
  • 整理 Inspector 内部状态结构,只保留:
    • Shader 资源引用
    • Shader schema 对应属性
    • Keywords
    • Render Queue / Render State
    • Tags仅保留当前正式允许暴露的部分
  • 避免 Inspector 保存与运行时 Material 不一致的冗余字段。

完成标准

  • 面板字段与正式 Material 模型一一对应。
  • 旧字段彻底不再出现在材质编辑界面和保存链路中。

Phase 3Shader schema 驱动的属性面板生成

目标

Material Inspector 能基于 Shader schema 动态生成属性编辑 UI。

任务

  • 读取 Shader 声明的属性定义与默认值。
  • 按属性类型渲染控件:
    • float
    • int
    • bool
    • float2/3/4
    • texture
  • 正确显示每个属性的:
    • 属性名
    • 显示名
    • 默认值
    • 当前覆盖值
    • 纹理槽绑定状态
    • 资源引用路径/AssetRef 状态
  • 明确“未覆盖时使用 Shader 默认值”的表现形式。

完成标准

  • 选定 Shader 后Inspector 中能稳定暴露该 Shader 的正式属性集合。
  • 面板能正确编辑并持久化这些属性。

Phase 4Editor 与运行时一致性收口

目标

保证 Material Inspector 编辑结果与运行时 Material、artifact、reimport 行为一致。

任务

  • 对齐 Inspector 写回逻辑与 Material 正式 API。
  • 验证 Shader 切换时属性重建、默认值回退、无效属性清理。
  • 验证纹理属性在源资产、artifact、异步资源加载中的一致性。
  • 验证关键词与 render state 在 reload/reimport 后不丢失。

完成标准

  • Inspector 改动能被运行时正确读取。
  • Reload / Reimport / Artifact Round Trip 后材质数据不漂移。

Phase 5测试补齐与阶段验收

目标

为正式工作流建立可持续回归验证。

任务

  • 补充或更新 material_tests
  • 补充或更新 asset_tests
  • 若材质解析或 builtin pass 匹配受影响,补充必要的 rendering_unit_tests
  • 重新编译 XCEditor,执行人工冒烟验证。

验收项

  • material_tests 全绿。
  • asset_tests 全绿。
  • 必要的 rendering_unit_tests 全绿。
  • XCEditor 编译通过。
  • Material Inspector 中 Shader 属性暴露、编辑、保存、重载行为正确。

7. 预期交付结果

本阶段完成后,工程应达到以下状态:

  1. Material 面板正式切换为 Shader schema 驱动。
  2. 材质不再承担旧 pass 选择职责。
  3. Inspector 中只剩下当前正式架构允许存在的字段。
  4. 材质属性、纹理槽、关键词、render state 都能稳定编辑并正确持久化。
  5. Shader / Material 后续扩展拥有清晰入口,不再建立在旧兼容逻辑之上。

8. 风险与注意事项

8.1 Shader 切换会触发属性重建

必须定义清楚以下规则:

  • 与新 Shader schema 匹配的属性如何保留。
  • 不匹配的旧属性如何移除。
  • 默认值何时回退,何时保留用户覆盖。

8.2 Texture 属性不仅是 UI 问题

Texture 属性同时涉及:

  • Inspector 显示
  • AssetRef
  • 资源路径
  • artifact 存储
  • 延迟加载

因此不能只改面板显示,必须连同资源路径一起验证。

8.3 Render State 需要坚持“正式最小集”

Material 面板不应再次演化成随意堆字段的临时入口。所有 render state 暴露都必须建立在当前正式支持的能力之上。

9. 建议执行顺序

建议严格按以下顺序落地:

  1. 先完成旧字段与旧路径清点。
  2. 再重构 Inspector 数据模型。
  3. 再接 Shader schema 驱动属性 UI。
  4. 再收口 Editor 与运行时一致性。
  5. 最后统一补测与验收。

不能反过来先堆 UI再回头补数据模型否则很容易再次生成新的临时方案。

10. 阶段结论

当前最应该推进的不是继续增加 Material 编辑功能花样,而是把 Material Inspector 这条正式链路做对、做稳、做干净。

只要这一阶段收口完成,后续无论是:

  • 更完整的 Shader authoring
  • Material 默认面板
  • 针对 Renderer/SRP 的材质体系扩展
  • 更接近 Unity 的 Shader/Material 编辑工作流

都会建立在清晰、稳定、可验证的基础之上。

11. Phase 1 审查结果

状态:已完成

本阶段已对当前 Material Inspector、运行时 Material、Shader schema、material source/artifact 路径进行了实际代码审查,结论如下。

11.1 当前 Inspector 数据模型先天不完整

当前 InspectorPanel::MaterialAssetState 只维护了以下内容:

  • shaderPath
  • renderQueue
  • renderState
  • tags

它没有正式承载以下关键数据:

  • Shader schema 属性列表
  • 材质属性当前值
  • 纹理槽与贴图引用
  • 关键词状态
  • Render State Override 开关本身

这意味着当前 Inspector 不是“少画了几个控件”,而是其内部状态模型本身就无法表示正式的材质编辑数据。

11.2 当前 Inspector 保存链路会丢失材质的正式内容

当前 BuildMaterialAssetFileText() 仅写出:

  • shader
  • renderQueue
  • tags
  • renderState

它不会写出:

  • properties
  • textures
  • keywords

因此,只要一个材质已经拥有 Shader 属性、纹理槽或关键词,若用户通过当前 Inspector 保存一次,该材质源文件就可能被覆盖成一个严重简化后的版本,导致正式材质数据丢失。

11.3 当前 Inspector 的实时写回同样不完整

当前 ApplyResolvedMaterialStateToResource() 仅把面板状态写回到运行时 Material 的以下部分:

  • SetShader
  • SetRenderQueue
  • SetRenderState
  • ClearTags / SetTag

它不会写回:

  • Shader schema 属性值
  • 纹理绑定
  • 关键词

因此 Inspector 当前即使支持继续加控件,如果不先重构数据模型,运行时同步仍然会不完整。

11.4 Render State Override 当前没有被正式建模

运行时 Material::SetRenderState() 会自动把 HasRenderStateOverride 置为 true

但当前 Inspector

  • 没有暴露 “是否启用 Render State Override”
  • 保存时始终写出完整 renderState
  • 应用时始终调用 SetRenderState

这意味着只要用户通过当前材质面板保存,材质就会被强制推进到显式 Render State Override 路径。这个行为不符合正式设计,必须在下一阶段一起修正。

11.5 运行时底层能力其实已经基本齐备

当前 runtime/resource 层已经具备正式所需的大部分能力:

  • ShaderPropertyDesc 已包含 name / displayName / type / defaultValue / semantic
  • Shader 已提供 GetProperties() / FindProperty() / keyword declaration 能力
  • Material 已提供完整的 SetFloat/SetInt/SetBool/SetTexture...
  • Material 已支持 Shader schema 默认值同步
  • MaterialLoader 已支持解析 properties / textures / keywords / tags / renderState
  • material artifact 当前已支持这些正式数据的持久化

也就是说,当前问题的主要矛盾不在资源系统,而在 Editor 侧没有接入正式模型。

11.6 当前阶段的根本性结论

下一阶段不能直接在现有材质面板上继续堆控件。

必须先完成:

  1. MaterialAssetState 的正式重构
  2. Save/Reload/Apply 链路对 properties / textures / keywords / renderState override 的建模
  3. Inspector 与运行时 Material 之间的一致性重建

只有先做完这些,后续的 Shader schema 驱动属性 UI 才不会继续建立在错误基础上。

11.7 Phase 2 的直接执行范围

基于本阶段审查结果,下一阶段将直接进入以下工作:

  1. 扩展 MaterialAssetState,让其正式承载属性、纹理槽、关键词与 render state override。
  2. 重构 Populate / Apply / Save / Reload 这四条关键链路。
  3. 彻底消除当前“保存一次就丢属性”的结构性问题。

12. Phase 2 执行结果

状态:已完成

本阶段已经完成 Material Inspector 数据模型的第一轮正式收口,重点是先把状态模型与保存链路做正确,而不是提前堆属性 UI。

12.1 已完成内容

  • MaterialAssetState 已扩展为正式承载:
    • keywords
    • properties
    • texture bindings
    • renderState override
  • PopulateMaterialAssetStateFromResource() 已从运行时 Material 同步:
    • Shader 路径
    • Render Queue
    • Render State
    • Render State Override 标志
    • Tags
    • Keywords
    • Properties / Texture Bindings
  • ApplyResolvedMaterialStateToResource() 已开始完整写回:
    • Shader
    • Render Queue
    • Render State
    • Render State Override
    • Tags
    • Keywords
    • Properties / Texture Bindings
  • BuildMaterialAssetFileText() 已开始正式写出:
    • keywords
    • properties
    • textures
    • 仅在启用 override 时才写 renderState
  • Inspector 已增加 Render State Override 开关,避免默认把材质强行推进到显式 override 路径。

12.2 本阶段解决的核心问题

本阶段已经解决了最危险的结构性问题:

  • 当前 Inspector 再保存材质时,不会像之前那样天然丢掉 properties / textures / keywords
  • Render State 是否为显式 override已经不再是隐藏副作用而成为正式状态的一部分。

12.3 本阶段仍未完成的部分

以下能力还没有在本阶段完成,这是下一阶段的工作重点:

  • 基于 Shader schema 的属性 UI 自动生成
  • 各属性类型的可视化编辑控件
  • 默认值/覆盖值的明确表现
  • 纹理槽的正式资源选择 UI
  • 关键词与属性在 Inspector 中的可视化编辑

因此Phase 2 的性质是“先把材质状态模型与保存链路做对”,而不是“材质面板功能已经完整”。

13. Phase 3 执行结果

状态:已完成

本阶段已经开始把正确的材质状态模型真正暴露到 Inspector 上,重点是基于 Shader schema 生成属性面板,并处理 Shader 切换时的状态重建。

13.1 已完成内容

  • Inspector 已新增 Properties 区块。
  • 属性区会基于当前 Shader 的 schema 动态生成,而不是写死字段。
  • 当前已接入的属性类型包括:
    • Float / Range
    • Int
    • Color
    • Vector
    • Texture2D / TextureCube
  • Texture 类型已接入资源选择控件,不再只是文本占位。
  • 每个属性当前会直接显示 Shader 中声明的默认值文本,作为当前参数的基线提示。

13.2 Shader 切换行为已收口

本阶段同时处理了一个关键一致性问题:

  • 当用户切换 Shader 时Inspector 会先基于新 Shader schema 重建 MaterialAssetState
  • 能与新 Shader 对齐的同名属性会尽量保留原值
  • 已不被新 Shader 声明的旧属性不会继续残留在保存结果里
  • 与新 Shader 不匹配的旧关键词也会被清理

这样可以避免旧材质属性被继续写回到新 Shader 材质文件中,从而减少生成无效材质源文件的风险。

13.3 本阶段仍未完成的部分

以下内容还需要后续阶段继续收口:

  • 属性默认值与显式覆盖值的正式“重置/回退”交互
  • 关键词的可视化编辑 UI
  • 更完整的属性类型与显示策略细化
  • 针对 Inspector 材质链路的专门自动化测试

因此Phase 3 的完成标准是“Shader schema 驱动的属性面板已经建立起来”,但还不是最终形态。

14. Phase 4 执行结果

状态:已完成

本阶段重点处理的是 authoring-state 与 runtime-state 的一致性问题,避免 Inspector 因为单纯从运行时对象反推而破坏材质源文件的语义。

14.1 已完成内容

  • Inspector 现在会回读材质源文件中实际 authored 的:
    • properties
    • textures
    • keywords
    • renderState
  • 材质状态中已加入“是否需要序列化回源文件”的 authored 标记。
  • 保存材质时,不再无条件把所有运行时属性都写回源文件。
  • 对于未在源文件中显式 authored 的属性,当前会继续保持“继承 Shader 默认值”的语义。
  • 当用户在 Inspector 中修改属性或贴图后,对应项才会被标记为显式 authored 并写回源文件。

14.2 本阶段解决的核心问题

本阶段解决的是一个架构层面的隐患:

  • 如果只从运行时 Material 反推回材质源文件,打开并保存一次材质,就会把 Shader 默认值全部固化进 .mat
  • 一旦默认值被固化,后续 Shader 默认值再调整,材质就不再继承新的默认值。

当前这条链路已经收口到更合理的状态:

  • 只有显式 authored 的 override 才会写回
  • 默认值仍然可以继续作为 Shader 侧的基线被继承

14.3 本阶段仍未完成的部分

以下内容仍然需要下一阶段继续完成:

  • 针对 Inspector 材质链路的专门自动化测试
  • 属性“重置到默认值”的正式交互
  • 关键词的可视化编辑 UI
  • 更完整的属性类型/显示策略覆盖

因此Phase 4 的性质是“先把 authoring 语义做正确”,为最后的测试与收口创造条件。

15. Phase 5 执行结果

状态:已完成

本阶段重点不是继续扩材质面板功能,而是把已经形成的正式链路变成“可持续回归验证”的状态,并把测试面收口到 Inspector 实际依赖的核心逻辑上。

15.1 已完成内容

  • 新增 MaterialInspectorMaterialState / MaterialInspectorMaterialStateIO 辅助模块,承载:
    • Material Inspector 状态结构
    • source authored presence 解析
    • Shader 切换时的属性/关键词重同步
    • 材质源文件序列化文本生成
  • 新增 tests/editor/test_material_inspector_material_state_io.cpp,覆盖:
    • authored 属性/纹理/关键词标记识别
    • Shader 切换时保留兼容 override、清理陈旧字段
    • 非 authored 默认值不写回源文件
    • 纹理 override 与 render state override 的正式序列化
  • editor_tests 构建链路已接入上述 helper 与新测试文件。

15.2 已执行验证

  • cmake --build build --config Debug --target XCEditor -j 8
  • cmake --build build --config Debug --target editor_tests -- /v:minimal
  • build/tests/Resources/Material/Debug/material_tests.exe
  • build/tests/Core/Asset/Debug/asset_tests.exe
  • build/tests/Editor/Debug/editor_tests.exe --gtest_filter=MaterialInspectorMaterialStateIOTest.*

验证结果:

  • XCEditor 编译通过
  • material_tests61 / 61 通过
  • asset_tests56 / 56 通过
  • MaterialInspectorMaterialStateIOTest4 / 4 通过

15.3 当前剩余风险

  • 全量 editor_tests.exe 在顺序执行 EditorActionRoutingTest.* 时仍存在既有的共享状态级别挂起现象。
  • 该挂起并非本次新增的材质面板测试本身触发:
    • 新增 MaterialInspectorMaterialStateIOTest 单独执行通过
    • EditorActionRoutingTest.PlayModeAllowsRuntimeSceneUndoRedoButKeepsSceneDocumentCommandsBlocked 单独执行通过
  • 因此,本阶段围绕 Material Inspector / Shader 属性面板的测试收口已经完成,但 Editor 其余历史测试链路仍需单独排查。