DreamShaderLang
内置节点

数学内置

Graph 块直接降级为算术材质节点的 19 个无前缀 HLSL 风格调用名。

有 19 个无前缀、按 HLSL 拼写的调用名会被直接降级为算术 UMaterialExpression 节点。它们覆盖 16 种 运算 —— 其中三个名字是别名。

float s     = sin(X);
float sa    = saturate(X);
vec3  mixed = lerp(A, B, sa);
vec3  unit  = normalize(mixed);
float d     = dot(unit, A);

它们是裸调用 —— 写 saturate(x),不是 UE.saturate(x) —— 也是唯一一个参数为位置参数的内置调用面。 可以写在 Graph 体内、Outputs 的绑定表达式中,或 Outputs 声明的初始化式中。

所有名字都不区分大小写SATURATE(x)Lerp(a, b, t)Sin(x) 都能解析。

这 19 个名字是保留名,会静默遮蔽你自己的代码。 命名为 lerpclampdotminmaxpowabs 或目录中任何其他写法的 FunctionGraphFunctionShaderFunctionVirtualFunction 或 property,在 Graph 块中都无法被调用到。名称解析时内置节点胜出,而且不会产生任何诊断

声明本身仍能编译、仍会生成对应资产;被改道的只是 Graph 调用点。要么给用户符号改名,要么从 Function 体内调用它,而不是从 Graph 块调用。

构造器名(float3vec4int2…)也是同样的保留方式,而且检测得更早。

语法概要

记号含义示例
<x>占位符——替换成实际内容,尖括号本身不写出来。Name = <string>
[ x ]可选——整段可以整体省略。[, Root = <string>]
{ a | b }多选一——从竖线分隔的写法里取其中一个。{ Node( … ) | Comment( … ) }
可重复——前一项可以出现任意多次。<property-declaration> …
{ abs | ceil | cos | floor | frac | fract | normalize | saturate | sin | sqrt } ( <x> )
{ dot | fmod | max | min | mod | pow } ( <x> , <y> )
{ clamp | lerp | mix } ( <x> , <y> , <z> )

目录

每个可接受写法一行。返回宽度是生成器赋给结果的分量数;权威表示该宽度是否参与 运算符与转换中的加宽规则。

写法参数个数降级为按序连接的输入 pin返回宽度权威
abs1UMaterialExpressionAbsInput参数的宽度继承自参数
ceil1UMaterialExpressionCeilInput参数的宽度继承自参数
clamp3UMaterialExpressionClampInputMinMax参数 1 的宽度取自参数 1
cos1UMaterialExpressionCosineInput参数的宽度继承自参数
dot2UMaterialExpressionDotProductAB恒为 1恒为权威
floor1UMaterialExpressionFloorInput参数的宽度继承自参数
fmod since 1.5.02UMaterialExpressionFmodA ← 被除数,B ← 除数参数 1 的宽度取自参数 1
frac1UMaterialExpressionFracInput参数的宽度继承自参数
fract since 1.5.01UMaterialExpressionFracInput参数的宽度继承自参数
lerp3UMaterialExpressionLinearInterpolateABAlpha参数 1 和 2 的 max参数 1、2 中任一为权威时设置
max2UMaterialExpressionMaxAB两个参数的 max任一参数为权威时设置
min2UMaterialExpressionMinAB两个参数的 max任一参数为权威时设置
mix3UMaterialExpressionLinearInterpolateABAlpha参数 1 和 2 的 max参数 1、2 中任一为权威时设置
mod since 1.5.02UMaterialExpressionFmodA ← 被除数,B ← 除数参数 1 的宽度取自参数 1
normalize1UMaterialExpressionNormalizeVectorInput参数的宽度继承自参数
pow2UMaterialExpressionPowerBaseExponent参数 1 的宽度取自参数 1
saturate1UMaterialExpressionSaturateInput参数的宽度继承自参数
sin1UMaterialExpressionSineInput参数的宽度继承自参数
sqrt1UMaterialExpressionSquareRootInput参数的宽度继承自参数

别名

三对别名。每对中的两种写法可以互换,生成的节点完全相同 —— 都没有被弃用。

别名对节点说明
lerp / mixLinearInterpolatemix 是 GLSL 写法
frac / fractFracfract 是 GLSL 写法 since 1.5.0
fmod / modFmodmod 是 GLSL 写法 since 1.5.0

Function 的 HLSL 体内部,标识符 mod 会被 GLSL 别名处理改写成 fmod。在 Graph 块中两种写法都被直接接受,不做改写。

参数规则

下列规则对上面每个内置节点都完全一致。

#规则违反时的后果
1参数个数精确 —— 没有默认值、没有可选参数、没有变参Math function '{Name}' expects exactly {N} argument(s).
2所有参数都是位置参数;不接受命名参数报成参数个数错误,见下
3每个参数都作为完整 Graph 表达式求值,允许嵌套内置调用内层错误被包装成 Math function '{Name}' argument {Index}: {Error}
4拒绝纹理对象值Math function '{Name}' only accepts numeric scalar/vector arguments.
5拒绝 MaterialAttributes同上
6拒绝 Substrate同上
7检查、不加宽、不广播参数的分量数这里什么都不发生;不匹配会在之后表现为 Unreal 材质翻译错误

规则 7 是最需要留意的。dot(vec3Value, floatValue) 在 DreamShader 这里没有任何诊断就通过了, 之后在 Unreal 自己的 shader 编译阶段失败。和算术运算符不同,这条路径没有标量/向量兼容性检查 —— 对比运算符与转换

命名参数

每个参数个数检查的判断都是「参数个数不对或者某个参数带名字」,两种情况都发出同一条个数错误消息。

给数学内置传命名参数报的是参数个数错误,而不是「不该带名字」。saturate(Input = X) —— 一个参数,名字还和节点 pin 完全对得上 —— 会失败于 Math function 'saturate' expects exactly 1 argument.

解决办法是去掉名字:saturate(X)。命名参数是 UE.* / Substrate.* 的特性,不是数学内置的特性。

名称解析

数学内置的名字在任何用户声明的名字之前解析。Graph 的调用分派器按下列顺序判断:

#候选参考
1向量/标量构造器名(float3vec4int2…)运算符与转换
2UE.SceneTextureUE.* 节点
3任何以 UE. 开头的被调用者UE.* 节点
4任何以 Substrate. 开头的被调用者Substrate
5数学内置 —— 本页
6SampleTexture2DUE.Expression
7已声明的 property,按参数 pin 调用形式Properties 类型
8FunctionGraphFunctionShaderFunctionVirtualFunction调用

拼错的内置名不会被报成数学错误。saturte(x) 会穿过全部八步,由调用路径报成 Unknown Graph function 'saturte'.

逐个内置节点的说明

clamp

clamp(Input, Min, Max) 会连接全部三个参数,并把节点的 ClampMode 保持在默认值 CMODE_Clamp。 要用 CMODE_ClampMinCMODE_ClampMax,走通用形式:

UE.Expression(Class = "Clamp", OutputType = "float1", Input = x, Min = a, ClampMode = "CMODE_ClampMin")

dot

唯一返回宽度固定的内置节点。无论参数宽度是多少,dot 总是产生 1 分量的权威结果,所以 float d = dot(A, B); 不需要 swizzle。

fmod、mod

参数 1 是被除数,参数 2 是除数,分别连到节点的 AB pin。结果取被除数的宽度。

反编译器没有处理 UMaterialExpressionFmod 的分支。已有的 Fmod 节点会被 导出成通用的 UE.Expression(Class="Fmod", …) 调用,而不是 fmod(…)。导出的源码是等价的, 只是无法回到内置写法。

lerp、mix

结果宽度是参数 1 和 2 的 max —— Alpha 参数不参与。用标量 Alpha 混合两个 vec3 得到 vec3

min、max

这两个名字共用一套实现,区别只在选择的节点类。两者都取两个参数宽度的 max

normalize

唯一一个输入 pin 不叫 Input 的内置节点。这条路径上的输入按反射属性名绑定,而 UMaterialExpressionNormalize 把它的 pin 命名为 VectorInput。这个差异在调用点是看不见的 —— normalize(N) —— 但会出现在 could not bind input / failed to access input 这两条诊断里。

sin、cos

两者都把节点的 Period 属性保持在默认值。要用非默认周期:

UE.Expression(Class = "Sine", OutputType = "float1", Input = x, Period = 2.0)

节点复用

结果会做公共子表达式缓存。两个文本相同、操作数值也相同的调用 —— 写了两次的 sin(X) —— 只产生一个 Sine 节点,而不是两个。缓存键覆盖内置名、节点类和每个参数值。

数学内置和通用的 UE.Expression 路径以及 Substrate.* 包装节点共享这一行为。已注册的 UE.* 内置节点是例外:它们每次调用都新建节点。

每个数学节点都创建在编辑器 X 坐标 360 上,Y 取自生成器的布局计数器。

这里没有的东西

这个调用面上没有矩阵、反三角、指数、对数、stepsmoothstepreflectrefractcrosslength 内置节点。有两条出路:

  • 直接触达对应的 UMaterialExpression,例如 UE.Expression(Class = "CrossProduct", OutputType = "float3", A = u, B = v)
  • 把运算写在 Function 的 HLSL 体里,那里可以用完整的 HLSL 内建函数集。

Function 的 HLSL 体内部,这些名字根本不由这个分派器处理 —— 函数体原样输出, 适用的是 HLSL 自己的内建函数。

反编译器导出已有材质时会输出这些写法:LinearInterpolatelerpClamp(当 ClampMode == CMODE_Clamp 时)→ clampPowerpowDotProductdotNormalizenormalizeMin/Maxmin/maxAbsabsSaturatesaturateFloor/Ceil/Frac/SquareRootfloor/ceil/frac/sqrt,以及 Sine/Cosine (当 Period 为 1.0 时)→ sin/cos

诊断

{Name} 是作者书写的写法,保留原始大小写。{Index} 从 1 开始。

消息触发原因处理
Math function '{Name}' expects exactly 1 argument.单参数内置节点的参数个数不对,或者任何一个参数带了名字。去掉参数名 —— 这个调用面只接受位置参数。
Math function '{Name}' expects exactly 2 arguments.同上,适用于 dot、pow、min、max、fmod 和 mod。
Math function '{Name}' expects exactly 3 arguments.同上,适用于 lerp、mix 和 clamp。
Math function '{Name}' is missing argument {Index}.内置节点要求的某个参数槽位不存在。
Math function '{Name}' argument {Index}: {Error}参数表达式求值失败;{Error} 是内层诊断。
Math function '{Name}' only accepts numeric scalar/vector arguments.某个参数是纹理对象、MaterialAttributes 值或 Substrate 值。先采样纹理,或先拆开 attributes,再做运算。
Failed to create math function '{Name}'.材质节点创建失败。
Math function '{Name}' could not bind input '{Input}'.节点类没有暴露预期的输入属性。仅一元内置节点。
Math function '{Name}' failed to access input '{Input}'.输入属性存在但无法访问其存储。仅一元内置节点。
Unknown Graph function '{Name}'.该名字不是内置节点、构造器、property 或用户函数 —— 由调用路径而非本路径发出。拼错的内置名会落到这里。详解

完整清单见错误速查

示例

Shader(Name="Docs/M_MathBuiltins")
{
    Properties = {
        float X = 0.5;
        vec3  A = vec3(1.0, 0.0, 0.0);
        vec3  B = vec3(0.0, 1.0, 0.0);
    }
    Settings = { Domain = "UI"; ShadingModel = "Unlit"; }
    Outputs  = { vec3 Color; Base.EmissiveColor = Color; }
    Graph = {
        float s     = sin(X);
        float c     = cos(X);
        float cl    = clamp(X, 0.0, 1.0);
        float sa    = saturate(X);
        vec3  mixed = lerp(A, B, sa);
        vec3  unit  = normalize(mixed);
        float d     = dot(unit, A);
        Color = mixed * (s + c + cl) + unit * d;
    }
}

生成的节点:

Sine(X)                       -> s
Cosine(X)                     -> c
Clamp(X, 0.0, 1.0)            -> cl
Saturate(X)                   -> sa
LinearInterpolate(A, B, sa)   -> mixed     (3 components: max(3, 3))
Normalize(mixed)              -> unit      (3 components)
DotProduct(unit, A)           -> d         (1 component, always)
Add / Multiply chain          -> Color

接下来

本页目录