DreamShaderLang
Builtins

Math Builtins

The 19 unprefixed HLSL-spelled call names a Graph block lowers to arithmetic material nodes.

Nineteen unprefixed, HLSL-spelled call names are lowered directly to arithmetic UMaterialExpression nodes. They cover 16 operations — three of the names are aliases.

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

These are called bare — saturate(x), not UE.saturate(x) — and they are the one builtin surface whose arguments are positional. They may be written inside a Graph body, in an Outputs binding expression, or in an Outputs declaration initializer.

Every name is matched case-insensitively: SATURATE(x), Lerp(a, b, t) and Sin(x) all resolve.

These 19 names are reserved and shadow your own code silently. A Function, GraphFunction, ShaderFunction, VirtualFunction or property named lerp, clamp, dot, min, max, pow, abs — or any other spelling in the catalogue — is unreachable from a Graph block. The builtin wins during name resolution and no diagnostic is emitted.

The declaration still compiles and still generates its asset; only the Graph call site is redirected. Rename the user symbol, or call it from a Function body instead of a Graph block.

Constructor names (float3, vec4, int2, …) are reserved the same way, and are tested even earlier.

Synopsis

NotationMeaningExample
<x>Placeholder — substitute a real value; the angle brackets are not typed.Name = <string>
[ x ]Optional — the whole group may be left out.[, Root = <string>]
{ a | b }Choice — take exactly one of the alternatives separated by |.{ Node( … ) | Comment( … ) }
Repetition — the preceding item may appear any number of times.<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> )

Catalogue

One row per accepted spelling. Return width is the component count the generator assigns to the result; Authoritative is whether that width counts for the widening rules in Expressions and conversions.

SpellingArityLowers toInput pins wired, in orderReturn widthAuthoritative
abs1UMaterialExpressionAbsInputwidth of the argumentinherited from the argument
ceil1UMaterialExpressionCeilInputwidth of the argumentinherited from the argument
clamp3UMaterialExpressionClampInput, Min, Maxwidth of argument 1from argument 1
cos1UMaterialExpressionCosineInputwidth of the argumentinherited from the argument
dot2UMaterialExpressionDotProductA, Balways 1always set
floor1UMaterialExpressionFloorInputwidth of the argumentinherited from the argument
fmod since 1.5.02UMaterialExpressionFmodA ← dividend, B ← divisorwidth of argument 1from argument 1
frac1UMaterialExpressionFracInputwidth of the argumentinherited from the argument
fract since 1.5.01UMaterialExpressionFracInputwidth of the argumentinherited from the argument
lerp3UMaterialExpressionLinearInterpolateA, B, Alphamax of arguments 1 and 2set when either of arguments 1, 2 had it
max2UMaterialExpressionMaxA, Bmax of both argumentsset when either argument had it
min2UMaterialExpressionMinA, Bmax of both argumentsset when either argument had it
mix3UMaterialExpressionLinearInterpolateA, B, Alphamax of arguments 1 and 2set when either of arguments 1, 2 had it
mod since 1.5.02UMaterialExpressionFmodA ← dividend, B ← divisorwidth of argument 1from argument 1
normalize1UMaterialExpressionNormalizeVectorInputwidth of the argumentinherited from the argument
pow2UMaterialExpressionPowerBase, Exponentwidth of argument 1from argument 1
saturate1UMaterialExpressionSaturateInputwidth of the argumentinherited from the argument
sin1UMaterialExpressionSineInputwidth of the argumentinherited from the argument
sqrt1UMaterialExpressionSquareRootInputwidth of the argumentinherited from the argument

Aliases

Three alias pairs. The two spellings in each pair are interchangeable and produce identical nodes — neither is deprecated.

PairNodeNote
lerp / mixLinearInterpolatemix is the GLSL spelling
frac / fractFracfract is the GLSL spelling since 1.5.0
fmod / modFmodmod is the GLSL spelling since 1.5.0

Inside a Function HLSL body the identifier mod is rewritten to fmod by the GLSL-alias pass. In a Graph block both spellings are accepted directly, with no rewrite.

Argument rules

These apply identically to every builtin above.

#RuleConsequence when violated
1Arity is exact — no defaults, no optional arguments, no varargsMath function '{Name}' expects exactly {N} argument(s).
2Every argument is positional; a named argument is not acceptedreported as an arity error, see below
3Each argument is evaluated as a full Graph expression, nested builtin calls includedthe inner error is wrapped as Math function '{Name}' argument {Index}: {Error}
4Texture-object values are rejectedMath function '{Name}' only accepts numeric scalar/vector arguments.
5MaterialAttributes values are rejectedsame message
6Substrate values are rejectedsame message
7Argument component counts are not checked, widened or broadcastnothing here; the mismatch surfaces later as an Unreal material-translation error

Rule 7 is the one to watch. dot(vec3Value, floatValue) is accepted by DreamShader without a diagnostic and then fails during Unreal's own shader compile. Unlike the arithmetic operators, this path has no scalar/vector compatibility test — compare Expressions and conversions.

Named arguments

Every arity guard is evaluated as "argument count is wrong or an argument is named", and both outcomes emit the arity message.

Passing a named argument to a math builtin reports an arity error, not a namedness error. saturate(Input = X) — one argument, correctly named after the node's pin — fails with Math function 'saturate' expects exactly 1 argument.

The fix is to drop the name: saturate(X). Named arguments are a UE.* / Substrate.* feature, not a math-builtin feature.

Name resolution

Math-builtin names are resolved before any user-declared name. The Graph call dispatcher tests, in order:

#CandidateReference
1vector/scalar constructor names (float3, vec4, int2, …)Expressions
2UE.SceneTextureUE.* nodes
3any UE.-prefixed calleeUE.* nodes
4any Substrate.-prefixed calleeSubstrate
5math builtins — this page
6SampleTexture2DUE.Expression
7declared properties, in the parameter pin-call formProperty types
8Function, GraphFunction, ShaderFunction, VirtualFunctionCalls

A misspelled builtin is not reported as a math error. saturte(x) falls through all eight steps and is reported by the call path as Unknown Graph function 'saturte'.

Per-builtin notes

clamp

clamp(Input, Min, Max) wires all three arguments and leaves the node's ClampMode at its default, CMODE_Clamp. For CMODE_ClampMin or CMODE_ClampMax, go through the generic form:

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

dot

The only builtin with a fixed return width. dot always produces a 1-component, authoritative result regardless of the argument widths, so float d = dot(A, B); needs no swizzle.

fmod, mod

Argument 1 is the dividend and argument 2 the divisor; they are wired to the node's A and B pins respectively. The result takes the dividend's width.

The decompiler has no case for UMaterialExpressionFmod. An existing Fmod node exports as a generic UE.Expression(Class="Fmod", …) call rather than as fmod(…). The exported source is equivalent; it simply does not round-trip to the builtin spelling.

lerp, mix

The result width is max of arguments 1 and 2 — the Alpha argument does not participate. A scalar Alpha blending two vec3 values yields a vec3.

min, max

The two names share one implementation and differ only in the node class selected. Both take the max of the two argument widths.

normalize

The only builtin whose input pin is not named Input. Inputs on this path are bound by reflected property name, and UMaterialExpressionNormalize names its pin VectorInput. The difference is invisible at the call site — normalize(N) — but it does appear in the two could not bind input / failed to access input diagnostics.

sin, cos

Both leave the node's Period property at its default. For a non-default period:

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

Node reuse

Results are common-subexpression cached. Two textually identical calls over identical operand values — sin(X) written twice — produce one Sine node, not two. The cache key covers the builtin name, the node class and every argument value.

Math builtins share this behaviour with the generic UE.Expression path and the Substrate.* wrappers. The registered UE.* builtins are the exception: they create a fresh node per call.

Every math node is created at editor X coordinate 360, with Y taken from the generator's running layout counter.

What is not here

There is no matrix, inverse-trigonometric, exponential, logarithmic, step, smoothstep, reflect, refract, cross or length builtin on this surface. Two ways out:

  • reach the UMaterialExpression directly, for example UE.Expression(Class = "CrossProduct", OutputType = "float3", A = u, B = v);
  • write the operation in a Function HLSL body, where the full HLSL intrinsic set is available.

Inside a Function HLSL body these names are not handled by this dispatcher at all — the body is emitted verbatim and HLSL's own intrinsics apply.

The decompiler emits these spellings when exporting an existing material: LinearInterpolatelerp, Clamp (when ClampMode == CMODE_Clamp) → clamp, Powerpow, DotProductdot, Normalizenormalize, Min/Maxmin/max, Absabs, Saturatesaturate, Floor/Ceil/Frac/SquareRootfloor/ceil/frac/sqrt, and Sine/Cosine (when Period is 1.0) → sin/cos.

Diagnostics

{Name} is the spelling as the author wrote it, so its casing is preserved. {Index} is 1-based.

MessageCauseFix
Math function '{Name}' expects exactly 1 argument.Wrong argument count for a 1-argument builtin, or any argument was named.Drop the argument names — this surface is positional only.
Math function '{Name}' expects exactly 2 arguments.The same, for dot, pow, min, max, fmod and mod.
Math function '{Name}' expects exactly 3 arguments.The same, for lerp, mix and clamp.
Math function '{Name}' is missing argument {Index}.An argument slot the builtin asked for does not exist.
Math function '{Name}' argument {Index}: {Error}Evaluating the argument expression failed; {Error} is the inner diagnostic.
Math function '{Name}' only accepts numeric scalar/vector arguments.An argument is a texture object, a MaterialAttributes value or a Substrate value.Sample the texture, or break the attributes, before doing arithmetic.
Failed to create math function '{Name}'.The material node could not be created.
Math function '{Name}' could not bind input '{Input}'.The node class does not expose the expected input property. Unary builtins only.
Math function '{Name}' failed to access input '{Input}'.The input property exists but its storage could not be reached. Unary builtins only.
Unknown Graph function '{Name}'.The name is not a builtin, constructor, property or user function — emitted by the call path, not by this one. A misspelled builtin lands here.Details

The complete list lives in the diagnostics index.

Example

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;
    }
}

Generated nodes:

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

Where to next

On this page