日常工作流
保存、防抖、生成这个循环 —— 什么会触发重建、什么会被跳过,以及改一个 .dsh 会让什么失效。
日常循环是:改源文件、保存、让插件编译、看结果。编辑器扩展负责改善"编辑"这一半;生成由 Unreal 插件 负责,而且只发生在编辑器进程里。
这个循环
- 修改
DShader/下的.dsm、.dsf或.dsh。 - 先处理编辑器扩展本地报出来的问题 —— 语法、未知标识符、错误的 import。
- 保存。 源目录监视器发现变化。
- 防抖计时器等过 Save Debounce Seconds —— 默认
0.25,钳制到[0.05, 10.0]—— 于是连续多次保存 只触发一次编译。 - DreamShader 编译该文件,并把结果输出到 Output Log。
- 通过 Tools ▸ DreamShader ▸ Material Content Browser 或编辑器扩展的预览查看材质。
自动编译由两项项目设置控制:Auto Compile On Save(默认开启)和 Save Debounce Seconds。关闭 自动编译后,监视器完全忽略文件变化,需要手动触发编译。
一次编译做了什么
每个阶段都是一道闸门:第一个失败的阶段会中止编译,它的消息就是本次编译结果。
| # | 阶段 | 说明 |
|---|---|---|
| 1 | 文件种类闸门 | .dsh 从不生成资产 —— 按设计就在这里失败 |
| 2 | 加载 prepared source | 把每个 import 递归内联成一整段文本 |
| 3 | 内容闸门 | 按扩展名逐文件做子串扫描,检查它可以声明什么 |
| 4 | 解析 | 整段 prepared 文本作为一个解析单元 |
| 5 | 计算 hash | 对 prepared 文本做 CRC32,格式化为八位十六进制 |
| 6 | 写 helper include | 仅当该单元至少声明了一个 Function |
| 7 | 材质函数资产 | 每个 ShaderFunction / ShaderLayer / ShaderLayerBlend 一个,按声明顺序 |
| 8 | 材质本体 | 仅当该单元声明了顶层 Shader |
函数资产在材质之前生成,所以同一文件里的 Shader 可以调用旁边声明的 ShaderFunction。解析器警告
从不导致编译失败,它们会以 Warnings: 开头附加到结果消息后面。
成功时的输出形如:
Generated DreamShader thin-custom material /Game/Materials/M_Emissive from .../M_Emissive.dsm.
Generated ShaderFunction /Game/Functions/F_Tint from .../M_Emissive.dsm.
Generated DreamShader helper include '...' from .../Common.dsm.
Skipped /Game/Materials/M_Emissive from .../M_Emissive.dsm; source hash is unchanged.一个文件也可能编译成功但什么可放置的产物都没有:只包含 Function、GraphFunction 或
VirtualFunction 声明的单元会成功,并给出对应说明。而完全没有任何顶层块的单元是失败。
三种文件在实际使用中的区别
| 种类 | 你改它是为了 | 保存它会 |
|---|---|---|
.dsm | 改一个材质 | 编译该材质,以及同文件里声明的函数资产 |
.dsf | 改一个可复用函数资产 | 编译它声明的函数资产 |
.dsh | 改共享 helper | 什么都不生成 —— 见下文 |
什么会触发编译
| 触发方式 | 是否强制 | 目标 | 范围 |
|---|---|---|---|
| 保存时自动编译(监视器 + 防抖) | 否 | 内存 | 被保存的文件 —— 最常见的路径 |
| Generate all in-memory materials —— 编辑器启动,以及每次修改 Default Compiler Backend | 是 | 内存 | 所有项目源文件 |
| Material Content Browser 的 Compile / Compile all / 刷新缩略图 | 是 | 内存 | 单个源文件,或全部列出的源文件 |
| 实时预览渲染 | 是 | 内存 | 单个源文件 |
| Materialize,以及为内存材质创建子实例 | 是 | 磁盘 | 单个源文件,持久化 |
Commandlet -run=DreamShader | 由调用方决定 | 磁盘 | 持久化 |
| Cook,仅在 cook director 进程上 | 是 | 磁盘 | 所有项目源文件,持久化 |
交互式编辑器从不为单个材质写出 .uasset。生成的资产一直留在内存里,直到 cook、commandlet 或显式
Materialize 把它们写到磁盘。见内存材质。
什么时候会跳过重建
每个生成的资产会在 package 元数据里存两个键:
| 键 | 值 |
|---|---|
DreamShader.SourceFile | 相对项目目录的源文件路径,使用正斜杠 |
DreamShader.SourceHash | prepared 源文本的 CRC32,八位十六进制 |
非强制编译时,如果资产存在、存储的源路径匹配(忽略大小写)、存储的 hash 匹配(区分大小写),这次工作就
会被跳过,结果是 Skipped {AssetPath} from {File}; source hash is unchanged.
由于 hash 是在 import 内联之后计算、并逐字节比较的:
| 改动 | 效果 |
|---|---|
改 M_Foo.dsm | 让 M_Foo.dsm 失效 |
改被两个材质 import 的 Common.dsh | 让两个材质都失效 |
| 调整空白、改注释 | 也会失效 —— 比较的是文本,不是语义 |
| 把项目挪到别的目录 | 什么都不失效 —— 存的是项目相对路径 |
| 重命名源文件 | 存储的路径不再匹配,于是不会跳过任何东西 |
语言层面没有清除已存 hash 的办法。要通过非强制入口触发重建,只能改源文本(任意改动),或者删掉生成的 资产。
改 .dsh 之后
保存 header 什么都不会生成。它会失败并输出
DreamShader header '{File}' does not generate assets directly. Recompile dependent .dsm or .dsf files instead.。
改 .dsh 会让所有 import 它的 .dsm 和 .dsf 的 hash 失效,但这些文件只有在它们自己被编译时才会
重建。
能让依赖方重建的路径:
- 分别保存每个依赖的
.dsm/.dsf; - Tools ▸ DreamShader ▸ Recompile DSM,它把所有项目
.dsm和.dsf压入待编译队列; - Generate all in-memory materials —— 编辑器启动,以及每次修改 Default Compiler Backend;
- Material Content Browser 的 Compile / Compile all;
- commandlet 或 cook。
Gen 页把 header 显示为 ◆ function / header,副标题是 used by {N} material(s),改之前就能看到影响
范围。
一次重建会毁掉什么
生成的资产是源文件的产物,不是文档。重新生成会清掉生成的注释框、断开每个材质属性输入、删除所有表达式、
把材质渲染状态重置为引擎默认值,然后重新应用 Settings 并重建。资产内部一切手动改动都会消失,只有一个
例外:文本不以字面量 DreamShader: 开头的注释框会保留。
在默认 backend 下,重新生成还会清空手动设置在生成材质实例上的所有参数覆盖 —— 标量、向量、纹理、
静态开关一视同仁 —— 而且没有任何诊断。把值写进源文件的 Properties 默认值里,或者在一个子级
UMaterialInstanceConstant 上覆盖,重新生成永远不会碰它。见
重新生成。
DreamShader 也拒绝覆盖不是自己生成的资产:磁盘上没有 DreamShader.SourceFile 元数据的资产会以
Asset '…' already exists and was not generated by DreamShader. 失败。这道守卫不覆盖 ThinCustom
实例路径,所以不要手工创建 UDreamShaderMaterialInstance 资产。
出错时看哪里
解析和生成错误会进 Output Log 的 LogDreamShader 类别、Material Content Browser 的源文件列表,以及
Saved/DreamShader/Bridge/diagnostics.json —— 后者是编辑器扩展读取的文件。
| 位置 | 显示什么 |
|---|---|
| Output Log | 全部消息,按顺序 |
| Gen 页 | 每个文件只显示一条 —— 第一条被接受的诊断 |
| 编辑器扩展 | 按文件的完整列表,定位到行列 |
行列号指向你实际编辑的那个文件:import 行会被替换成空行,每个被内联的文件用来源标记包起来,所以位置 不会偏移。到错误速查里查具体消息。
迁移已有材质
在 Content Browser 里右键 UMaterial 或 UMaterialFunction,使用 DreamShader ▸ Export DSM /
Export DSF。输出落在 DShader/Decompiled/ 下并会自动打开。把它当作初稿 —— 在正式作为源文件之前,
先整理命名、helper 分层和 import。见反编译导出。
下一步
- 生成流程 —— 每个阶段和它可能给出的每条消息
- 重新生成 —— 完整的"什么能保留"清单
- 编辑器工具 —— Tools 菜单、浏览器标签页、清理命令
- VSCode 与 Rider —— 诊断、预览和 Package 命令
- 命令行 —— headless 编译