DreamShaderLang
Graph 语言

Graph

Graph

Graph

DreamShader » Graph

块体是一串语句,编译器在生成阶段按顺序一次性降级为生成材质中的 UMaterialExpression 节点的那个 section。

声明于.dsm、.dsf —— 位于 Shader、ShaderFunction、ShaderLayer、ShaderLayerBlend 内部
种类section
生成目标 UMaterial / UMaterialFunction 上的 UMaterialExpression 节点
section 名按不区分大小写匹配;{ 前的 = 可省略 (自 1.5.0 起)

语法概要

Graph [=] {
    <graph-statement> …
}

Graph 块体并不是嵌入着色器的表达式语言——它是一个节点构建程序。其中没有任何东西以代码形式存活到运行时。存活下来的是它构建出的那张图。

要写与 DreamShaderLang 2.0 相同的东西——带循环、?: 和 &&——请见2.0 语言;dsc migrate 会为你改写 1.x 文件。

Graph 块可以出现在哪里

块GraphCode
Shader✔ 必需,除非 Outputs section 声明或绑定了东西(DSH2255)✘ DSH2246
ShaderFunction✔ —— 没有它块体为空✘ DSH2246
ShaderLayer / ShaderLayerBlend✔ —— 没有它块体为空✘ DSH2246
VirtualFunction✘ DSH2247✘ DSH2247
Function / GraphFunction块体是 HLSL,不是 Graph 块——

[!WARNING] Code = { … } 在每个带 Graph 的块里都是 DSH2246。请使用 Graph。

同一套语句语言还以简化形式用在另外两处:

位置形态
Outputs 绑定的右侧一条完整表达式,没有语句
Outputs 声明的 = <default>一条表达式:在第一条 Graph 语句之前声明的变量的初始值

求值模型

  1. legacy 前端把块体读成 2.0 语法树 (自 2.0.0 起)。一个 Shader 变成一个材质函数,其函数体依次是:每个 Outputs 声明(带它的初始值,没有就是该类型的零值)、Graph 语句、以及作为"写给材质"的赋值的 Outputs 绑定。函数块的 Graph 就是它的 Inputs 和 Outputs 所声明的那个函数的函数体。
  2. 绑定器先给每个名字和表达式定型,此时还没有任何节点。1.x 没有的写法、或定不了型的写法会带码中止构建——块体永远不会只构建一半。
  3. 每条语句自上而下各降级一次。 没有循环结构,没有 return,没有递归,没有重入。第 n 条语句只能看到第 1 … n-1 条语句产生的值。
  4. 每个表达式都是一个有类型的值:1–4 分量的数(种类为 float、half、int、uint 或 bool)、纹理、采样器、MaterialAttributes 值或 Substrate 值。变量名就是绑定到这样一个值上的名字。
  5. 降级后的图上跑几道 pass:常量折叠、合并相同节点、剪掉没人读的节点。然后发射器才创建 UMaterialExpression 节点。
  6. Outputs 绑定源最后求值,各自作为一条完整表达式,在块体留下的值上求值。

值得内化的结论

  • 没有运行时控制流。 一个 if 在构建期会降级两个分支;两边的节点集都会出现在最终材质里,由一个 If 节点(条件是静态参数时为 StaticSwitch)在每个变量上于两者之间选择。见 if / else。
  • 声明出来的变量不是存储。 给一个名字赋值只是把它重新绑定到另一个节点;原先绑定的节点如果还有别处读取就留在图里,否则被剪掉。
  • 相同的子表达式会合并。 去重 pass 会把运算相同、操作数相同的节点合成一个,所以同一个字面量、同一个 A + B、同一个 UE.TexCoord(Index=0) 无论写多少遍都只产生一个节点。见节点复用。
  • 有些写法自己不产生节点。 通道选择的开销见 Swizzle。
  • 类型会被检查,宽度也是。 两个宽度不同、又都不是标量的操作数是 DSH4226;把更宽的值存进更窄的变量会保留前导分量,并给出提示 DSH5289;对编译器而言,int、uint、bool 和 half 是各自的种类,在图里都是浮点。见转换。
  • 不认识的运算符会被拒绝,而不是被丢弃 (自 2.0.0 起)。1.x 遇到 %、&&、||、?:、[ ]、<< 之类会静默结束表达式,于是 a % b 构建出 a;现在每一种都是带码的错误(DSH2200–DSH2222)。见不支持的写法。

语句一览

形态示例参考
声明float3 c = A * K;声明
逗号声明符float a = 1, b, c = 3;声明
花括号初始化vec4 v = {rgb, 1.0};声明
赋值Color = Tint;语句
属性成员写入Attrs.BaseColor = Tint;MaterialAttributes
独立调用F_Split(In, OutA, OutB);调用
if / else / else ifif (m > .5) { … } else { … }if / else

带终止规则的完整语法概要表见语句。

注释与布局

注释由词法器读取 (自 2.0.0 起):// 行注释和 /* */ 块注释。块注释不嵌套,遇到第一个 */ 就闭合,始终不闭合的是 DSH2102。字符串字面量里的 // 不是注释。每条诊断的行列都是源文件里的位置。

#Region / #EndRegion

#Region "Name" 和 #EndRegion 行位于 Graph 块体内部,将它们之间的语句所产生的节点归入生成图里的一个注释框。每一行单独成行,是块体里的一条语句*(自 2.0.0 起)*;区域可以嵌套。没有名字的 #Region、没有打开的区域却写了 #EndRegion、块体结束时仍未关闭的区域以及其他 # 行是 DSH2216–DSH2219,预处理器不认识的行是 DSH1035。完整语法与嵌套语义见 Layout。

诊断指向哪里

每条诊断都相对源文件报告,指向它所说的那个写法:

<file>(<line>,<column>): DSHnnnn: <message>

码是契约,措辞可能会变。行列就是那个写法自己的位置,在 section 里和 if 块体里都一样*(自 2.0.0 起)*;不再有 In Graph if body: 前缀。一个阶段会报告它的全部错误,有错误的阶段会在下一阶段之前停止构建。码的清单见错误速查。

示例

Shader(Name="DreamShaderTests/Corpus/M_Arithmetic")
{
    Properties = {
        vec3  A = vec3(1.0, 0.5, 0.2);
        vec3  B = vec3(0.1, 0.2, 0.3);
        float K = 2.0;
    }
    Settings = { Domain = "UI"; ShadingModel = "Unlit"; }
    Outputs  = { vec3 Color; Base.EmissiveColor = Color; }
    Graph = {
        vec3 Sum    = A + B;
        vec3 Diff   = A - B;
        vec3 Scaled = A * K;
        vec3 Ratio  = A / K;
        Color = Sum + Diff - Scaled + Ratio;
    }
}

生成的节点:

VectorParameter A, VectorParameter B, ScalarParameter K   (property nodes)
Add       (A, B)          -> Sum
Subtract  (A, B)          -> Diff
Multiply  (A, K)          -> Scaled
Divide    (A, K)          -> Ratio
Add       (Sum, Diff)
Subtract  (.., Scaled)
Add       (.., Ratio)     -> Base.EmissiveColor

本节的页面

页面涵盖内容
语句每种语句形式、结束规则、赋值、表达式语句
声明T name = expr;、逗号声明符、花括号初始化器、默认值、作用域
表达式运算符表、优先级与结合性、一元运算符
字面量数字形式与后缀、true / false、字符串字面量
构造器构造器名称、splatting、packing、常量折叠
Swizzle通道集合、重排与重复
转换一个值如何适配它要去的位置
if / else条件、比较真值表、分支合并
MaterialAttributes属性值、成员的读取与写入、Substrate 互操作
调用调用 Function、GraphFunction、ShaderFunction、VirtualFunction、参数引脚
名称解析标识符与调用的查找顺序、遮蔽
节点复用相同节点的去重以及用户观察到的现象
不支持的写法循环、return、三目运算符、%、比较、索引——以及拒绝它们的那些码

另请参阅

本页目录