DreamShaderLang
生成与产物

资产路径

Name= 与 Root= 如何变成 Unreal package 路径、object 路径和磁盘文件 —— 包括 Plugin.X 根。

每个会生成资产的块都带同样两个头部属性,由同一个解析器把它们变成一个目标位置。

项目说明
适用于ShaderShaderFunctionShaderLayerShaderLayerBlend —— 四者完全一致
种类头部属性
Root="Plugin.X"since 1.2.0

语法概览

记号含义示例
<x>占位符——替换成实际内容,尖括号本身不写出来。Name = <string>
[ x ]可选——整段可以整体省略。[, Root = <string>]
{ a | b }多选一——从竖线分隔的写法里取其中一个。{ Node( … ) | Comment( … ) }
可重复——前一项可以出现任意多次。<property-declaration> …
Shader        (Name = "<name-path>" [, Root = "<root>"]) { … }
ShaderFunction(Name = "<name-path>" [, Root = "<root>"]) { … }

<name-path> := [<folder> /] … <leaf>
<root>      := "" | Game | Plugin.<PluginName> | Plugins.<PluginName>
             | Plugin / <PluginName> | Plugins / <PluginName>
             | / <MountRoot> [/ <folder>] …
             | <folder> [/ <folder>] …

Name必填 的。缺少时报 Shader(Name="...") is required.,函数类块则报 {Kind}(Name="...") is required.Root 可选,默认是空字符串。属性名按不区分大小写匹配,所以 name=ROOT= 都能识别。

解析顺序

  1. Root 变成根 package 路径。 去空白;\ 换成 /;记住原串是否以 / 开头;去掉首尾的 /;按 / 切分。任何一步得到空结果都退回 /Game
  2. Name 变成若干文件夹加一个 leaf。 去空白;\ 换成 /;去掉所有首尾 /;按 / 切分。最后一段 是资产名,前面每一段都是追加到根后面的文件夹。
  3. 每一段都经过 ObjectTools::SanitizeObjectName。空段被静默跳过。
  4. PackageName = <root>/<folders…>/<Leaf>ObjectPath = <PackageName>.<Leaf>
  5. PackageNameFPackageName::IsValidObjectPath 校验。

Root= 的分派

分派看的是 Root第一段,比较时不区分大小写。

第一段附加条件根 packageRoot 取走的文件夹段数
(省略、为空、/、或全是空白)/Game
Game/Game1
Plugin.<Name>该插件的挂载点1
Plugins.<Name>该插件的挂载点1
Plugin存在第二段由第 1 段命名的插件2
Plugins存在第二段由第 1 段命名的插件2
其它任意值Root 串以 / 开头原样使用 /<第 0 段>1
其它任意值不以 / 开头/Game,第 0 段变成文件夹0

省略或为空

RootNamePackageObject 路径
(省略)M_Flat/Game/M_Flat/Game/M_Flat.M_Flat
""Materials/M_Flat/Game/Materials/M_Flat/Game/Materials/M_Flat.M_Flat
"/"M_Flat/Game/M_Flat/Game/M_Flat.M_Flat

Game

RootNamePackage
GameM_Flat/Game/M_Flat
/GameMaterials/M_Flat/Game/Materials/M_Flat
Game/MaterialsM_Flat/Game/Materials/M_Flat
game/materialsM_Flat/Game/Materials/M_Flat

比较不区分大小写,但输出的根永远是字面量 /Game

Plugin.<Name>Plugins.<Name>

点号形式用一段就指定了插件,后面的全部是文件夹。

RootNamePackage磁盘位置
Plugin.MoonToonMat/Test/MoonToon/Mat/Test<Project>/Plugins/MoonToon/Content/Mat/Test.uasset
Plugins.MoonToonTest/MoonToon/Test<Project>/Plugins/MoonToon/Content/Test.uasset
Plugin.MoonToon/SharedTest/MoonToon/Shared/Test<Project>/Plugins/MoonToon/Content/Shared/Test.uasset

Plugin/<Name>Plugins/<Name>

斜杠形式要花两段来指定插件。

RootNamePackage
Plugin/MoonToonTest/MoonToon/Test
Plugins/MoonToonMat/Test/MoonToon/Mat/Test
Plugins/MoonToon/SharedTest/MoonToon/Shared/Test

Root="Plugin"Root="Plugins"没有 第二段时并不指代插件。它们会落到分派表最后一行,被当成普通文件夹名, 生成 /Game/Plugin/Game/Plugins。没有任何诊断 —— 资产只是安静地落到了你没打算放的地方。

显式挂载根

/ 开头、又不属于上面任何形式的 Root,会被原样当作一个挂载点。

RootNamePackage
/MyMountTest/MyMount/Test
/MyMount/SubTest/MyMount/Sub/Test
/EngineTest/Engine/Test

第一段必须原样通过 SanitizeObjectName,否则报 DreamShader Root '{Root}' has an invalid package root.。挂载点本身在这里 检查是否存在 —— 未挂载的根会在后面的 IsValidObjectPath 闸门处失败。

裸相对路径

其余不以 / 开头的 Root 都变成 /Game 下的文件夹。

RootNamePackage
FooTest/Game/Foo/Test
Foo/BarTest/Game/Foo/Bar/Test
Foo/BarDeep/Test/Game/Foo/Bar/Deep/Test

这一分支和上一分支的差别只在开头那个斜杠:Root="Foo/Bar"/Game/Foo/BarRoot="/Foo/Bar"/Foo/Bar

插件根的要求

两种插件写法走同一个校验器。下面每一道闸门都必须按顺序通过,各有各的消息。

#要求失败消息
1插件名非空,且经过 SanitizeObjectName 后不变DreamShader Root '{Root}' has an invalid plugin name.
2插件管理器认识这个名字DreamShader Root '{Root}' references project plugin '{Plugin}', but no enabled plugin with that name was found.
3它是 项目 插件,基目录位于项目的 Plugins 目录下DreamShader Root '{Root}' must reference a project plugin under '{PluginsDir}'.
4它已启用DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin is not enabled.
5它可以包含内容DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin cannot contain content.
6磁盘上存在它的 Content 目录DreamShader Root '{Root}' references project plugin '{Plugin}', but its Content directory does not exist: '{ContentDir}'.
7它的内容已挂载 since UE 5.6DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin content is not mounted.

第 7 道闸门在 UE 5.3 – 5.5 上不存在;在那些引擎上,未挂载的插件会在稍后的 object 路径校验中被抓到。

根 package 路径就是插件自己的挂载资产路径 —— 规范化成正斜杠、去掉结尾斜杠、强制加上开头斜杠。如果结果退化成空或者 /,则使用 /<PluginName>

引擎插件、以及安装在引擎目录下的商城插件,即使已启用、已挂载,也会被第 3 道闸门拒绝。只有物理位置在 <Project>/Plugins 下的插件才被接受。要写进引擎侧的挂载点,请用显式挂载根分支(Root="/SomeMount"),它会完全跳过插件校验器。

各资产类型

四种块的路径解析完全一致。不同的是创建的类,以及函数类块被打上的 material function usage。

源块资产类Material function usage
ShaderGraph backendUMaterial
ShaderThinCustom backend since 1.5.0UDreamShaderMaterialInstance 加一个隐藏的 UMaterial 子对象
ShaderFunctionUMaterialFunctionDefault
ShaderLayer since 1.3.0UMaterialFunctionMaterialLayerMaterialLayer
ShaderLayerBlend since 1.3.0UMaterialFunctionMaterialLayerBlendMaterialLayerBlend

当解析出来的路径上已经存在资产时,它的类必须匹配:

类型匹配规则
ShaderFunction精确 类匹配 —— 该路径上是 UMaterialFunctionMaterialLayer 会被拒绝
ShaderLayerShaderLayerBlendIsA 期望的类即可
ShaderGraph backend已有对象必须是 UMaterial
ShaderThinCustom backend已有对象必须是 UDreamShaderMaterialInstance

类匹配不等于归属检查。除 ThinCustom 路径之外,DreamShader 还会拒绝覆盖不是自己生成的资产 —— 见 重新生成

磁盘映射

Package 根磁盘目录
/Game/…<Project>/Content/…
/<PluginName>/…<Project>/Plugins/<PluginName>/Content/…
/<MountRoot>/…该挂载点注册到的任何位置

文件是 <directory>/<Leaf>.uasset,而且 只在持久化模式下 才会写出。日常编辑器工作中什么都不会落盘 —— 见 内存材质

说明

  • Name 可以带文件夹,Root 只是前缀。 Root="Game" 下的 Name="A/B/C" 得到 /Game/A/B/C,资产名是 C
  • 重复的属性名会静默覆盖前一个。 Shader(Name="A", Name="B") 解析成 B,没有任何诊断。
  • 每一段都独立做净化,所以 Name="My Mat" 会得到叫 My_Mat 的 leaf,同样没有诊断。
  • 空段会被丢弃:Name="A//B" 就是 A/BRoot="Game//Sub" 就是 /Game/Sub
  • Content Browser 的状态列和 Materialize 操作用的是同一个解析器,所以在这里失败的路径,在 编辑器工具 里也会显示为无法解析。
  • 不要和 Path(Root, "…") 混淆,那是 另一套 路径语法 —— 它用来 引用 已有资产,而不是指定生成目标。见 Path 资产引用

诊断

消息触发原因处理
Shader(Name="...") is required.Shader 头部没有 Name 属性。
{Kind}(Name="...") is required.ShaderFunction / ShaderLayer / ShaderLayerBlend 头部没有 Name 属性。
DreamShader asset name must resolve to a non-empty asset path.Name 去空白、去斜杠后为空。
DreamShader asset name '{Name}' produced an invalid asset name.leaf 段净化后为空。
DreamShader asset name '{Name}' contains an invalid folder segment.Name 中某个非空文件夹段被净化成了空。
DreamShader asset path '{Path}' is not a valid Unreal object path.拼出的路径没通过 IsValidObjectPath,且引擎没有给出自己的原因。检查挂载根是否未挂载。
DreamShader Root '{Root}' contains an invalid folder segment.Root 中某个非空文件夹段被净化成了空。
DreamShader Root '{Root}' has an invalid plugin name.插件名为空,或被净化改动过。
DreamShader Root '{Root}' has an invalid package root.显式挂载根被净化改动过。
DreamShader Root '{Root}' references project plugin '{Plugin}', but no enabled plugin with that name was found.插件名不存在。
DreamShader Root '{Root}' must reference a project plugin under '{PluginsDir}'.引擎插件,或位于项目 Plugins 目录之外的插件。改用 Root="/SomeMount" 指向引擎侧挂载点。
DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin is not enabled.插件未启用。
DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin cannot contain content.纯代码插件。
DreamShader Root '{Root}' references project plugin '{Plugin}', but its Content directory does not exist: '{ContentDir}'.缺少 Content 目录。
DreamShader Root '{Root}' references project plugin '{Plugin}', but the plugin content is not mounted.插件内容未挂载。仅 UE 5.6+。
Failed to create package '{Package}'.package 创建失败。
Asset '{ObjectPath}' already exists and is not a Material.Graph backend,路径上的类不对。
Asset '{ObjectPath}' already exists and is not a DreamShader instance material. Delete it (or remove Backend="Instance") before switching backends.ThinCustom backend,路径上的类不对。详解
Asset '{ObjectPath}' already exists and is not a MaterialFunction asset.函数类块,路径上的类不对。
Asset '{ObjectPath}' already exists as '{ActualClass}', but {Kind} generation requires '{ExpectedClass}'. Delete or move the existing asset and regenerate it.函数类块,material function 子类不对。
Asset '{ObjectPath}' already exists and was not generated by DreamShader. Rename your shader or move/delete the existing asset before regenerating.材质上的归属守卫。详解
Asset '{ObjectPath}' already exists and was not generated by DreamShader. Rename your function or move/delete the existing asset before regenerating.材质函数上的归属守卫。详解

完整示例

Shader(Name="Mat/Test", Root="Plugin.MoonToon")
{
    Properties { vec3 Tint = vec3(1.0, 0.2, 0.2); }
    Settings   { Domain = "UI"; ShadingModel = "Unlit"; }
    Outputs    { vec3 Color; Base.EmissiveColor = Color; }
    Graph      { Color = Tint; }
}

解析出的目标:

Root      "Plugin.MoonToon"  ->  /MoonToon                       (plugin mount point)
Name      "Mat/Test"         ->  folders "Mat", leaf "Test"
package                          /MoonToon/Mat/Test
object path                      /MoonToon/Mat/Test.Test
on disk                          <Project>/Plugins/MoonToon/Content/Mat/Test.uasset

继续阅读

本页目录