数学内置
Graph 块直接降级为算术材质节点的 29 个无前缀 HLSL 风格调用名。
有 29 个无前缀、按 HLSL 拼写的调用名会被直接降级为算术 UMaterialExpression 节点。它们覆盖 26 种
运算 —— 其中三个名字是别名。除 reflect 和 refract 外,每次调用生成一个节点;这两个 Unreal 没有对应
表达式,会展开成一小张子图。
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) 都能解析。
这 29 个名字是保留名,会静默遮蔽你自己的代码。 命名为 lerp、clamp、dot、min、max、
pow、abs 或目录中任何其他写法的 Function、GraphFunction、ShaderFunction、VirtualFunction
或 property,在 Graph 块中都无法被调用到。名称解析时内置节点胜出,而且不会产生任何诊断。
声明本身仍能编译、仍会生成对应资产;被改道的只是 Graph 调用点。要么给用户符号改名,要么从
Function 体内调用它,而不是从 Graph 块调用。
1.6.0 把这个命名空间从 19 个拓宽到 29 个 since 1.6.0。既有源码里声明了 length、step、
cross、atan 等同名符号的,需要改名。
构造器名(float3、vec4、int2…)也是同样的保留方式,而且检测得更早。
语法概要
| 记号 | 含义 | 示例 |
|---|---|---|
<x> | 占位符——替换成实际内容,尖括号本身不写出来。 | Name = <string> |
[ x ] | 可选——整段可以整体省略。 | [, Root = <string>] |
{ a | b } | 多选一——从竖线分隔的写法里取其中一个。 | { Node( … ) | Comment( … ) } |
… | 可重复——前一项可以出现任意多次。 | <property-declaration> … |
{ abs | acos | asin | atan | ceil | cos | floor | frac | fract | length
| normalize | saturate | sin | sqrt } ( <x> )
{ atan2 | cross | dot | fmod | max | min | mod | pow | reflect | step } ( <x> , <y> )
{ clamp | lerp | mix | refract | smoothstep } ( <x> , <y> , <z> )目录
每个可接受写法一行。返回宽度是生成器赋给结果的分量数;权威表示该宽度是否参与 运算符与转换中的加宽规则。
| 写法 | 参数个数 | 降级为 | 按序连接的输入 pin | 返回宽度 | 权威 |
|---|---|---|---|---|---|
abs | 1 | UMaterialExpressionAbs | Input | 参数的宽度 | 继承自参数 |
acos since 1.6.0 | 1 | UMaterialExpressionArccosine | Input | 参数的宽度 | 继承自参数 |
asin since 1.6.0 | 1 | UMaterialExpressionArcsine | Input | 参数的宽度 | 继承自参数 |
atan since 1.6.0 | 1 | UMaterialExpressionArctangent | Input | 参数的宽度 | 继承自参数 |
atan2 since 1.6.0 | 2 | UMaterialExpressionArctangent2 | Y ← 参数 1,X ← 参数 2 | 两个参数的 max | 任一参数为权威时设置 |
ceil | 1 | UMaterialExpressionCeil | Input | 参数的宽度 | 继承自参数 |
clamp | 3 | UMaterialExpressionClamp | Input、Min、Max | 参数 1 的宽度 | 取自参数 1 |
cos | 1 | UMaterialExpressionCosine | Input | 参数的宽度 | 继承自参数 |
cross since 1.6.0 | 2 | UMaterialExpressionCrossProduct | A、B | 恒为 3 | 恒为权威 |
dot | 2 | UMaterialExpressionDotProduct | A、B | 恒为 1 | 恒为权威 |
floor | 1 | UMaterialExpressionFloor | Input | 参数的宽度 | 继承自参数 |
fmod since 1.5.0 | 2 | UMaterialExpressionFmod | A ← 被除数,B ← 除数 | 参数 1 的宽度 | 取自参数 1 |
frac | 1 | UMaterialExpressionFrac | Input | 参数的宽度 | 继承自参数 |
fract since 1.5.0 | 1 | UMaterialExpressionFrac | Input | 参数的宽度 | 继承自参数 |
length since 1.6.0 | 1 | UMaterialExpressionLength | Input | 恒为 1 | 恒为权威 |
lerp | 3 | UMaterialExpressionLinearInterpolate | A、B、Alpha | 参数 1 和 2 的 max | 参数 1、2 中任一为权威时设置 |
max | 2 | UMaterialExpressionMax | A、B | 两个参数的 max | 任一参数为权威时设置 |
min | 2 | UMaterialExpressionMin | A、B | 两个参数的 max | 任一参数为权威时设置 |
mix | 3 | UMaterialExpressionLinearInterpolate | A、B、Alpha | 参数 1 和 2 的 max | 参数 1、2 中任一为权威时设置 |
mod since 1.5.0 | 2 | UMaterialExpressionFmod | A ← 被除数,B ← 除数 | 参数 1 的宽度 | 取自参数 1 |
normalize | 1 | UMaterialExpressionNormalize | VectorInput | 参数的宽度 | 继承自参数 |
pow | 2 | UMaterialExpressionPower | Base、Exponent | 参数 1 的宽度 | 取自参数 1 |
reflect since 1.6.0 | 2 | 一张 4 节点子图 —— 见下面的说明 | —— | 两个参数的 max | 任一参数为权威时设置 |
refract since 1.6.0 | 3 | 一张 14 节点子图 —— 见下面的说明 | —— | 参数 1 和 2 的 max | 参数 1、2 中任一为权威时设置 |
saturate | 1 | UMaterialExpressionSaturate | Input | 参数的宽度 | 继承自参数 |
sin | 1 | UMaterialExpressionSine | Input | 参数的宽度 | 继承自参数 |
smoothstep since 1.6.0 | 3 | UMaterialExpressionSmoothStep | Min、Max、Value | 三个参数的 max | 任一参数为权威时设置 |
sqrt | 1 | UMaterialExpressionSquareRoot | Input | 参数的宽度 | 继承自参数 |
step since 1.6.0 | 2 | UMaterialExpressionStep | Y ← 参数 1(edge),X ← 参数 2(value) | 两个参数的 max | 任一参数为权威时设置 |
别名
三对别名。每对中的两种写法可以互换,生成的节点完全相同 —— 都没有被弃用。
| 别名对 | 节点 | 说明 |
|---|---|---|
lerp / mix | LinearInterpolate | mix 是 GLSL 写法 |
frac / fract | Frac | fract 是 GLSL 写法 since 1.5.0 |
fmod / mod | Fmod | mod 是 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 | 向量/标量构造器名(float3、vec4、int2…) | 运算符与转换 |
| 2 | UE.SceneTexture | UE.* 节点 |
| 3 | 任何以 UE. 开头的被调用者 | UE.* 节点 |
| 4 | 任何以 Substrate. 开头的被调用者 | Substrate |
| 5 | 数学内置 —— 本页 | — |
| 6 | SampleTexture2D | UE.Expression |
| 7 | 已声明的 property,按参数 pin 调用形式 | Properties 类型 |
| 8 | Function、GraphFunction、ShaderFunction、VirtualFunction | 调用 |
拼错的内置名不会被报成数学错误。saturte(x) 会穿过全部八步,由调用路径报成
Unknown Graph function 'saturte'.
逐个内置节点的说明
clamp
clamp(Input, Min, Max) 会连接全部三个参数,并把节点的 ClampMode 保持在默认值 CMODE_Clamp。
要用 CMODE_ClampMin 或 CMODE_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 是除数,分别连到节点的 A 和 B 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)step
since 1.6.0step(edge, x) 返回 x >= edge ? 1 : 0,和 HLSL 一致。但 UMaterialExpressionStep 的 pin 命名是反过来的
—— Y 是 edge、X 是 value —— 所以参数 1 连到 Y、参数 2 连到 X。手写节点形式的等价调用是:
UE.Expression(Class = "Step", OutputType = "float1", Y = edge, X = x)smoothstep
since 1.6.0smoothstep(min, max, x),参数顺序同 HLSL,直接连到节点的 Min、Max 和 Value pin。
length、cross
since 1.6.0除 dot 之外仅有的两个返回宽度固定的内置:无论参数是什么,length 恒为 1 分量、cross 恒为 3 分量,
两者的宽度都是权威的。这与同样这两个类经 UE.Expression 触达时报告的
宽度一致。
asin、acos、atan、atan2
since 1.6.0四个反三角节点。atan2(y, x) 的参数顺序同 HLSL,而节点的 pin 本来就叫 Y 和 X,所以映射是直接的。
引擎还带了 ArcsineFast、ArccosineFast、ArctangentFast 和 Arctangent2Fast —— 在有限输入范围上
成立的更便宜的近似。它们没有内置写法,用
UE.Expression(Class = "ArcsineFast", OutputType = "float1", Input = x) 触达。
reflect、refract
since 1.6.0仅有的两个背后不是一个节点的内置。Unreal 既没有 Reflect 也没有 Refract 表达式,所以两者都被降级成
HLSL 对它们的定义,返回给调用方的是那张子图的最后一个节点。
reflect(i, n) 展开成 i - 2 * dot(i, n) * n —— 四个节点(DotProduct、两个 Multiply、Subtract)。
字面量 2 骑在 Multiply 的 ConstB 上,不额外花一个 Constant 节点。
refract(i, n, eta) 展开成完整的 HLSL 定义 —— 十四个节点:
k = 1 - eta*eta * (1 - dot(n, i)^2)
k < 0 ? 0 : eta*i - (eta*dot(n, i) + sqrt(k)) * n全内反射的判断是一个 If 节点,所以两侧都会被翻译、再选其一;对负 k 取 sqrt 只落在被丢弃的那一侧,
和 HLSL 完全一样。k == 0 仍然满足公式(sqrt(0) == 0)并走折射侧。零分支写成 i * 0 而不是常量,
是为了让它的类型永远等于 i 的类型 —— If 要求两个分支一致,而追踪到的分量数并不总能保证手挑的常量对得上。
节点复用
结果会做公共子表达式缓存。两个文本相同、操作数值也相同的调用 —— 写了两次的 sin(X) ——
只产生一个 Sine 节点,而不是两个。缓存键覆盖内置名、节点类和每个参数值。
数学内置和通用的 UE.Expression 路径以及
Substrate.* 包装节点共享这一行为。已注册的
UE.* 内置节点是例外:它们每次调用都新建节点。
每个数学节点都创建在编辑器 X 坐标 360 上,Y 取自生成器的布局计数器。
这里没有的东西
矩阵不在其中,而且加不了。 Unreal 材质图根本没有矩阵值类型,所以无论 DSL 怎么做,mul(M, v) 都不会有
Graph 写法。空间转换用 UE.Expression(Class = "Transform") 或 "TransformPosition",其余的写在
Function 的 HLSL 体里。
仍然缺席、但各自都有节点因而可以经 UE.Expression 触达的:指数、对数、
tan、sign、round、trunc 和 distance。例如:
UE.Expression(Class = "Distance", OutputType = "float1", A = u, B = v)在 Function 的 HLSL 体内部,这些名字根本不由这个分派器处理 —— 函数体原样输出,
适用的是 HLSL 自己的内建函数。
反编译器导出已有材质时会输出这些写法:LinearInterpolate → lerp、
Clamp(当 ClampMode == CMODE_Clamp 时)→ clamp、Power → pow、DotProduct → dot、
Normalize → normalize、Min/Max → min/max、Abs → abs、Saturate → saturate、
Floor/Ceil/Frac/SquareRoot → floor/ceil/frac/sqrt,以及 Sine/Cosine
(当 Period 为 1.0 时)→ sin/cos。
1.6.0 新增的十个写法没有加进导出侧:Step、SmoothStep、Length、CrossProduct 和四个反三角
节点导出成通用的 UE.Expression(Class="…", …) 调用,和 Fmod 是同一种不对称。reflect / refract
则根本无法 round-trip —— 它们留在图里的是普通算术节点,没有任何东西标记它们的来历。
诊断
{Name} 是作者书写的写法,保留原始大小写。{Index} 从 1 开始。
| 消息 | 触发原因 | 处理 |
|---|---|---|
| Math function '{Name}' expects exactly 1 argument. | 单参数内置节点的参数个数不对,或者任何一个参数带了名字。 | 去掉参数名 —— 这个调用面只接受位置参数。 |
| Math function '{Name}' expects exactly 2 arguments. | 同上,适用于 dot、pow、min、max、fmod、mod、step、cross、atan2 和 reflect。 | |
| Math function '{Name}' expects exactly 3 arguments. | 同上,适用于 lerp、mix、clamp、smoothstep 和 refract。 | |
| 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