DreamShaderLang
语言核心

布局

布局

布局

DreamShader » DreamShaderLang » 布局

一个用于固定生成节点位置并在生成的材质图中声明注释框的 section。

声明于.dsm, .dsf — 位于 Shader, ShaderFunction, ShaderLayer, ShaderLayerBlend 内
种类section
生成节点位置,以及每条 Comment 语句对应一个 UMaterialExpressionComment
不接受于VirtualFunction

(自 2.0.0 起) 由哪一趟处理读取该 section 取决于工程的 图布局风格。Blocks(默认)以及在编译器 IR 上计算的其他风格,会在各自的 排布之上服从每一条 Node 和 Comment 语句。Classic 是 1.x 的布局,按 1.x 的方式使用该 section,即 显式布局与自动布局 所描述的方式。.dss 写出的语句与 #pragma layout(Node, …) / #pragma layout(Comment, …) 相同 —— 见 DreamShaderLang 2.0。

语法概览

Layout [=]
{
    Node( Var = "<variable>", X = <int>, Y = <int> ) ;
    Comment( Name = "<title>", X = <int>, Y = <int>, W = <int>, H = <int>
             [, Color = float4( <r>, <g>, <b>, <a> )] ) ;
    …
}

语句是调用,每条后面可选跟一个 ;。语句名和所有参数键都按大小写不敏感匹配。{ … } 块之前的 = 是可选的 (自 1.5.0 起)。

Node

把一个已经生成出来的表达式钉到确切位置上。

参数必需类型默认值
Var是文本—
X是整数—
Y是整数—

Var 指的是源码里的一个值。会被记录的名字有三类:

名字类别记录时机
Graph 语句的目标变量该语句产出了节点 —— 声明的局部变量和被赋值的输出变量都算
Properties 声明名图确实读取了该属性
Inputs / Outputs 声明名仅限 ShaderFunction、ShaderLayer 和 ShaderLayerBlend,其中每一条都会变成 FunctionInput / FunctionOutput 节点

MaterialExpressionEditorX/Y 和编辑器图节点的 NodePosX/Y 都会被写入,所以位置在重新打开材质后依然保留。

[!NOTE] 匹配不到任何东西的 Var 会被静默忽略 —— 无论是拼写错误,还是钉了一个图从未读取过的属性, 都没有诊断。

Comment

在确切矩形上创建一个注释框。

参数必需类型作用
Name是文本框标题 —— 见说明。
X是整数左边缘。
Y是整数上边缘。
W是整数宽度;Classic 下至少 120,IR 风格下至少 64。
H是整数高度;Classic 下至少 80,IR 风格下至少 64。
Color否float4 字面量框颜色;不写就用布局自己的默认色。

[!WARNING] W 和 H 是必填参数。Comment(Name="X", X=0, Y=0) 是 DSH3275,参数缺失和格式错误都报这个码。

Color 字面量遵循通用的向量字面量语法:( 之前的 token 被忽略,所以 float4(…)、vec4(…) 和 (…) 都能解析。一个分量铺成 (x, x, x, 1),两个给 (x, y, 0, 0),三个给 (x, y, z, 1),第四个之后的分量被忽略。其他写法是 DSH3276。

生成的注释框始终使用 FontSize = 24 和分组模式,所以拖动框会带动它圈住的节点。

参数解析

规则细节
语句形态<Name>( <key> = <value> [, <key> = <value> ]… ) —— 其他写法是 DSH3274
语句名一个标识符,按大小写不敏感与 Node 和 Comment 比较;其他名字是 DSH3274
键匹配大小写不敏感
值处理单个带引号的字符串取它的文本;其他取 token 的文本,整数参数必须读成可选正负号加数字
重复键DSH3274 —— 不同于 Settings,那里是后写的键胜出
空值DSH3274
必需参数缺失或为空、坐标不是整数DSH3275
注释任意两个 token 之间都可以

既不是必需参数也不是 Color 的参数会交给布局,由它带警告 DSH7230 忽略;Node 上的 Color 也一样。

坐标系

位置是 Unreal 材质图编辑器坐标:X 向右增大,Y 向下增大,单位和编辑器节点位置相同。

手工摆放节点时,几个 Classic 常量可以作参照:

位置X
输出列:材质旁边最宽的节点,以及 FunctionOutput 节点900
输出绑定的 reroute 使用点(构造时)720
材质根节点图边界右侧 520

各列从输出列向左排,所以负 X 是“上游”,材质根节点位于所有节点右侧。完整表格以及 IR 风格自己的间距见 图布局。(自 2.0.0 起) 1.x 按节点种类给的构造位置(属性节点 -800、Expression( … ) 目标 1200 等)已经没有了: 每个节点都在同一列创建,再由布局摆放。

显式布局与自动布局

在 Classic 下,Layout 块不是在自动布局之上叠加 —— 它替换掉那套排序算法。

条件结果
至少一条 Node 匹配到了被记录的变量,或者至少声明了一条 Comment走显式布局
有 Layout 块,但没有 Node 匹配上、也没有 Comment自动布局照常运行,就当这个块不存在
没有 Layout 块自动布局运行

在显式路径上,块里没点名的表达式仍然会被摆放:位置从已经定位的邻居迭代传播 —— 位于已知消费者和已知 依赖的中点、已知消费者左边 360、或已知依赖右边 360 —— 重合槽位有扇出避让。仍然没被摆放的,会进入 所有已定位节点左侧的兜底列。

在 IR 风格下,被钉住的节点移到指定位置,风格摆在它周围的节点跟着一起移动。

[!NOTE] 布局总是会运行 (自 2.0.0 起),Ephemeral 材质和保存了的材质一样;唯一的例外是 Classic 风格的大图保护。见 图布局。

[!NOTE] 第二个 Layout 区段会替换第一个,而不是追加,并带有警告 DSH2258 (自 2.0.0 起)。一个块体里只有最后一个 Layout 块有效。

#Region / #EndRegion

区域指令位于 Graph 函数体文本中,而不是在 Layout 中。它们为一个图语句范围命名;布局会把每个区域变成一个注释框。

Graph = {
    #Region "Surface"
    Color = BaseColor.rgb;
    Rough = Roughness;
    #EndRegion

    #Region "Emissive"
    Glow = Tint * Intensity;
    #EndRegion
}
规则细节
识别# 行中紧随 # 之后的单词为 Region 或 EndRegion,按大小写不敏感匹配。.dsm / .dsf 中的另一个 # 行会先被预处理器拒绝(DSH1035);若某行到达 1.x 的 Graph 函数体(例如 #pragma 在 .dsh 中那样),则为 DSH2219
名称该行的其余部分,去除引号并修剪空白;在 #Region 上为必需项 —— DSH2216
嵌套区域可以嵌套;在没有打开区域时出现 #EndRegion 属于 DSH2217,在函数体末尾仍未关闭的区域属于 DSH2218
位置指令与其他任何 token 一样,因此每条诊断都保留其真实的行和列

在 Blocks 下,区域是独立的框,而包含区域的区域则是套在它们外围的框。在 Classic 下,区域内的语句会用它生成的变量打上区域名标签;在自动路径上,每个区域成为一个注释框,而在显式路径上,区域名贡献用于决定在何处插入跨块重路由的块边界。

[!NOTE] 预处理器会先于解析器看到每一行 # (自 1.9.0 起),并明确将 region / endregion 命名为要透传的行 —— 无论如何,都与此处匹配的方式完全一致。因此 #Region、#region 和 #REGION 都会原样到达解析器,区域指令不受条件编译影响。

[!NOTE] #Region 名称和 Layout Comment 名称相互独立。声明一个矩形恰好包含某区域节点的 Comment 并不会将两者合并。

说明

  • 注释标题。 在 Classic 下,每个框的标题都是 DreamShader: <Name>,因此 Comment(Name="Sampling", …) 读作 DreamShader: Sampling。在 IR 风格下,Comment 保持 其书写时的标题,而样式为区域和块生成的框则会带有该前缀。该 前缀标记一个框是编译器自身的;见重新生成。
  • Comment 的 Name 为空或空白时属于 DSH3275,因此永远不会为它创建任何框。
  • 反编译器会将位置和自由注释框写回为 #pragma layout 行,或写为带 -Format Legacy 的 Layout 块,因此材质可以导出、 编辑并重新生成,且位置保持不变。是否输出由 Export Decompiled Layout 项目设置控制,默认开启。见项目设置。
  • Classic 自动布局会在非常大的图上放弃 —— 在达到或超过 1200 个表达式时,它会记录 Skipping automatic layout for large DreamShader graph ({Count} nodes). Existing generated positions will be used. 并保留构建时的位置。Layout 块是控制这些 图的方式。见图布局。
  • Layout 在 VirtualFunction 内不被接受 (DSH2245);该块不生成图。

诊断

每条诊断都带有它所涉及语句或指令的行和列。

Layout 语句

代码触发条件
DSH3274区段不是以 { 打开;语句不是 <Name>( … );参数不是 Key = Value;键被写了两次;未闭合的 (;名称不是 Node 或 Comment
DSH3275必需参数缺失或为空,或坐标不是整数
DSH3276Color 不是向量字面量
DSH2258警告:第二个 Layout 区段
DSH7230警告:布局用不到的参数,已被忽略

#Region 指令

代码触发条件
DSH2216#Region 没有名称
DSH2217#EndRegion 时没有打开 #Region
DSH2218区域在 Graph 函数体末尾仍未关闭
DSH2219到达 Graph 函数体的另一行 #

完整列表见诊断索引。

示例

Shader(Name="Materials/M_Layout", Root="Game")
{
    Properties {
        VectorParameter Tint      = float4(0.4, 0.8, 1.0, 1.0);
        ScalarParameter Intensity = 2.0 [Slider(0, 10)];
    }

    Outputs {
        float3 Color;
        Base.EmissiveColor = Color;
    }

    Graph {
        #Region "Emissive"
        float3 Boosted = Tint.rgb * Intensity;
        Color = Boosted + vec3(0.05, 0.05, 0.05);
        #EndRegion
    }

    Layout {
        Comment(Name="Emissive", X=-1300, Y=-260, W=1100, H=520,
                Color=float4(0.10, 0.22, 0.16, 0.35));
        Node(Var="Tint",      X=-1200, Y=-160);
        Node(Var="Intensity", X=-1200, Y=  60);
        Node(Var="Boosted",   X= -800, Y=-160);
        Node(Var="Color",     X= -400, Y= -60);
    }
}

生成的图,在 Classic 下:

Comment      "DreamShader: Emissive"   at (-1300, -260)  size 1100 x 520
Tint         VectorParameter           at (-1200, -160)
Intensity    ScalarParameter           at (-1200,   60)
Boosted      Multiply                  at ( -800, -160)
Color        Add                       at ( -400,  -60)
DS_EmissiveColor_<n>  NamedReroute     positioned by propagation

另请参阅

  • Shader —— 其图由 Layout 确定位置的块
  • ShaderFunction —— 材质函数块也接受 Layout
  • Graph —— 编写 #Region 指令的位置
  • 预处理器 —— 另一种 # 语法,以及如何区分两者
  • 声明 —— 哪些语句会注册可命名的变量
  • 属性 —— 属性名也是有效的 Node Var 目标
  • 输出绑定 —— 为每个绑定创建的重路由对
  • 图布局 —— 布局样式及其块、常量和限制
  • 重新生成 —— DreamShader: 注释前缀规则
  • 临时材质 —— 仅存在于内存中的材质
  • 反编译器 —— 从现有材质往返导出位置
  • 项目设置 —— Export Decompiled Layout
  • 诊断索引 —— 每个代码

本页目录