DreamShaderLang
Builtins

Math Builtins

Unprefixed, HLSL-spelled call names — 44 HLSL names lowered straight to arithmetic material nodes, plus the four GLSL spellings a 1.x source may still use.

Math builtins

DreamShader » Builtins » Math builtins

Unprefixed, HLSL-spelled call names that lower directly to arithmetic UMaterialExpression nodes.

Declared in.dsm, .dsf — inside a Graph { … } body, an Outputs binding expression, or an Outputs declaration initializer; a .dss takes the same names anywhere an expression stands
Kindbuiltin call surface — the compiler's core operations
Generatesone UMaterialExpression per call, chosen per name — see the catalogue. Five names — fwidth, rcp, rsqrt, reflect, refract — have no node behind them and generate a small subgraph
Spellings44 HLSL names, plus four GLSL spellings a 1.x source may still use (since 2.0.0; 29 spellings through 1.9.x)
Namespacenone — these are called bare, saturate(x), not UE.saturate(x)

Synopsis

{ abs | acos | asin | atan | ceil | cos | ddx | ddy | exp | exp2 | floor | frac | fwidth | length
| log | log2 | log10 | normalize | rcp | round | rsqrt | saturate | sign | sin | sqrt | tan | trunc } ( <x> )
{ atan2 | cross | distance | dot | fmod | max | min | pow | reflect | step } ( <x> , <y> )
{ clamp | lerp | refract | smoothstep } ( <x> , <y> , <z> )

( ) and , are literal DreamShaderLang punctuation; { a | b } is meta-notation and is never typed. Each <x> / <y> / <z> is any Graph expression. sinh, cosh and tanh are names too, and refused — see Notes.

Names are matched exactly (since 2.0.0). A 1.x source still gets the 1.x leniencies, each with a diagnostic that says so — see Name resolution. An argument is positional, or named after the node's pin — see Named arguments.

Catalogue

One row per HLSL name. Result is the type the compiler gives the call; widest operand means the widest of all the arguments, scalars broadcasting to it.

NameArityLowers toInput pins, in argument orderResult
abs1UMaterialExpressionAbsInputthe argument's
acos (since 1.6.0)1UMaterialExpressionArccosineInputthe argument's
asin (since 1.6.0)1UMaterialExpressionArcsineInputthe argument's
atan (since 1.6.0)1UMaterialExpressionArctangentInputthe argument's
atan2 (since 1.6.0)2UMaterialExpressionArctangent2Y, Xwidest operand
ceil1UMaterialExpressionCeilInputthe argument's
clamp3UMaterialExpressionClampInput, Min, Maxwidest operand
cos1UMaterialExpressionCosineInputthe argument's
cross (since 1.6.0)2UMaterialExpressionCrossProductA, Balways float3
ddx (since 2.0.0)1UMaterialExpressionDDXValuethe argument's
ddy (since 2.0.0)1UMaterialExpressionDDYValuethe argument's
distance (since 2.0.0)2UMaterialExpressionDistanceA, Balways float
dot2UMaterialExpressionDotProductA, Balways float
exp (since 2.0.0)1UMaterialExpressionExponentialInputthe argument's
exp2 (since 2.0.0)1UMaterialExpressionExponential2Inputthe argument's
floor1UMaterialExpressionFloorInputthe argument's
fmod (since 1.5.0)2UMaterialExpressionFmodA ← dividend, B ← divisorwidest operand
frac1UMaterialExpressionFracInputthe argument's
fwidth (since 2.0.0)1a subgraph: abs(ddx(x)) + abs(ddy(x))—the argument's
length (since 1.6.0)1UMaterialExpressionLengthInputalways float
lerp3UMaterialExpressionLinearInterpolateA, B, Alphawidest operand
log (since 2.0.0)1UMaterialExpressionLogarithmInputthe argument's
log2 (since 2.0.0)1UMaterialExpressionLogarithm2Xthe argument's
log10 (since 2.0.0)1UMaterialExpressionLogarithm10Xthe argument's
max2UMaterialExpressionMaxA, Bwidest operand
min2UMaterialExpressionMinA, Bwidest operand
normalize1UMaterialExpressionNormalizeVectorInputthe argument's
pow2UMaterialExpressionPowerBase, Exponentwidest operand
rcp (since 2.0.0)1a subgraph: Divide with ConstA = 1—the argument's
reflect (since 1.6.0)2a 4-node subgraph — see below—always float3
refract (since 1.6.0)3a 14-node subgraph — see below—always float3
round (since 2.0.0)1generated UMaterialExpressionCustom with HLSL roundInputthe argument's
rsqrt (since 2.0.0)1a subgraph: SquareRoot, then Divide with ConstA = 1—the argument's
saturate1UMaterialExpressionSaturateInputthe argument's
sign (since 2.0.0)1UMaterialExpressionSignInputthe argument's
sin1UMaterialExpressionSineInputthe argument's
smoothstep (since 1.6.0)3UMaterialExpressionSmoothStepMin, Max, Valuewidest operand
sqrt1UMaterialExpressionSquareRootInputthe argument's
step (since 1.6.0)2UMaterialExpressionStepY ← argument 1 (edge), X ← argument 2 (value)widest operand
tan (since 2.0.0)1UMaterialExpressionTangentInputthe argument's
trunc (since 2.0.0)1UMaterialExpressionTruncateInputthe argument's

GLSL spellings, read in a 1.x source as the HLSL name, with the warning DSH5277: mix → lerp, fract → frac, mod → fmod, inversesqrt → rsqrt. In a .dss they are DSH4250. dsc migrate respells them.

Argument rules

These apply identically to every builtin above, in a 1.x source and a .dss alike.

#RuleConsequence when violated
1Arity is exact — no defaults, no optional arguments, no varargsDSH4224
2An argument is positional, or named after one of the node's input pins; a positional argument fills the first operand not filled yet. In a .dss a positional argument after a named one is DSH2158an unknown name is DSH4216, a pin given twice DSH4215, a pin left out between given ones DSH4217
3Each argument is evaluated as a full Graph expression, including nested builtin callsthe inner expression reports its own error, at its own position
4Every argument is a number (or a bool, read as 0 / 1) — a texture object, a MaterialAttributes value or a Substrate value is notDSH4226
5Component counts are checked (since 2.0.0): the widest operand sets the width, a scalar broadcasts to it, a narrower vector does not widenDSH4226
6cross, reflect and refract take float3 operandsa narrower vector is DSH4226; a wider one is cut to three in a 1.x source with the note DSH5289 — what 1.x did — and is DSH4226 in a .dss
7Two integer constructor calls as the operands of / are refused — the graph has no integer division; any other / is a float division, 7 / 2 includedDSH4243

Through 1.9.x none of rules 4 – 6 was a compile check: dot(vec3Value, vec2Value) was accepted without a word and failed later, in Unreal's own material translation.

Name resolution

A call to a bare name is resolved in this order. The full lookup is on Name resolution.

#CandidateReference
1in a 1.x Graph, the spellings the legacy front end rewrites while it reads the call: SampleTexture2D, a declared property's pin-call form, UE.SceneTexture and the other 1.x call shapesUE.* catalogue · Using parameters in Graph
2type names — constructors such as float3(…), vec4(…)Constructors
3UE.- and Substrate.-prefixed calleesUE.* catalogue · Substrate.*
4Texture2DSample, Texture2DSampleLevel
5math builtins — this page, by the exact HLSL name; then a GLSL spelling (DSH5277 in 1.x, DSH4250 in a .dss)—
6functions this file declares or imports — Function, GraphFunction, ShaderFunction, VirtualFunction, a .dss functionCalls
7in a 1.x source only: a builtin or a function whose name matches ignoring case, with the warning DSH5275—

[!IMPORTANT] A function cannot be named like a builtin (since 2.0.0). A Function, GraphFunction, ShaderFunction, VirtualFunction or .dss function named lerp, clamp, dot, min, max, pow, abs — any HLSL name or GLSL spelling on this page — is DSH6206 at its declaration. Through 1.9.x such a function compiled and was silently unreachable from a Graph block, because the builtin won.

A misspelled builtin — saturte(x) — is DSH4208, with a did you mean when a declared name differs only in case.

Named arguments

(since 2.0.0) A named argument names one of the node's input pins — the Input pins column of the catalogue — and fills that operand, wherever it stands in the list:

float t = lerp(A, B, Alpha = Mask);     // A, B by position; Alpha by name
float c = clamp(X, Max = 1.0, Min = 0.0);

The pin names are the engine's, so they are not always HLSL's: pow is Base / Exponent, step is Y (the edge) / X (the value), normalize is VectorInput. A name the node does not have is DSH4216, which lists the pins. The builtins with no node (fwidth, rcp, rsqrt, reflect, refract) take their arguments in order only.

[!NOTE] Through 1.9.x a named argument on a math builtin was reported as an arity error — saturate(Input = X) failed with an "expects exactly 1 argument" message. It selects the pin now.

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, call the node itself: UE.Clamp(Input = x, Min = a, ClampMode = CMODE_ClampMin). See UE.Expression.

dot, length, distance, cross

The builtins with a fixed result: dot, length and distance are always one component, cross always three, whatever the arguments were — so float d = dot(A, B); needs no swizzle.

fmod

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

mod is the GLSL spelling. In a Graph block a 1.x source may still write it, with the warning DSH5277. Inside a Function HLSL body the identifier mod is rewritten to fmod — silently, ignoring case — together with the other GLSL names.

lerp

The result is as wide as the widest of the three arguments, Alpha included: a scalar Alpha blending two float3 values yields a float3, and is broadcast to it. Over two Substrate values, lerp(A, B, t) is a Substrate.HorizontalMixing instead — see Substrate sugar.

min, max

The two names share one typing rule and differ only in the node class selected.

normalize

The only builtin whose single input pin is not named Input: UMaterialExpressionNormalize names it VectorInput. The difference is invisible at a positional call site (normalize(N)), and it is the name a named argument has to use.

sin, cos

Both leave the node's Period property at its default. For a non-default period call the node itself: UE.Sine(Input = x, Period = 2.0).

The default period is 1, so these graph builtins take cycles: sin(0.25) is 1 and cos(0.25) is 0 (within floating-point precision). Constant expressions use the same period as graph nodes. Inside an @custom HLSL body, sin and cos take radians as usual.

round

The graph builtin and constant folding follow HLSL's nearest-even rule at a halfway value: round(0.5) is 0, round(2.5) is 2, and round(-1.5) is -2.

The generated Custom node evaluates HLSL round for both uniform and varying inputs. This avoids the native UE.Round(Input = x) node's preshader, which rounds halfway values upward in UE 5.8 (0.5 becomes 1). Use the reflected spelling when that native behavior is required. Decompilation recovers an unchanged generated node as round; a native Round node remains UE.Round. dump-ir shows the language operation and dump-graph shows the generated Custom node.

What that costs: the engine never evaluates a Custom node ahead of the shader, so a round of a uniform value -- a parameter, say -- runs per pixel, one instruction, and math downstream of it does too instead of being folded into the material's uniforms once per draw. A constant argument still folds at compile time. Where that matters more than the halfway case, UE.Round(Input = x) keeps the native node and its preshader.

step

step(edge, x) returns x >= edge ? 1 : 0, as in HLSL. UMaterialExpressionStep names its pins the other way round — Y is the edge and X is the value — so argument 1 wires to Y and argument 2 to X.

smoothstep

smoothstep(min, max, x), argument order as in HLSL, wired straight to the node's Min, Max and Value pins.

asin, acos, atan, atan2

The four inverse-trigonometric nodes. atan2(y, x) takes its arguments in HLSL order and the node's pins are already named Y and X, so the mapping is direct.

[!NOTE] The engine also ships ArcsineFast, ArccosineFast, ArctangentFast and Arctangent2Fast — cheaper approximations valid over a limited input range. They have no builtin spelling; call the node itself, UE.ArcsineFast(Input = x).

fwidth, rcp, rsqrt

No engine node does any of the three. fwidth(x) is built as abs(ddx(x)) + abs(ddy(x)), which is what the HLSL intrinsic is defined as; rcp(x) as a Divide with ConstA = 1; rsqrt(x) as a SquareRoot feeding such a Divide.

reflect, refract

Unreal has no Reflect or Refract UMaterialExpression, so both are lowered to the arithmetic HLSL defines them as, and the value returned to the caller is the final node of that subgraph.

reflect(i, n) becomes i - 2 * dot(i, n) * n — four nodes (DotProduct, two Multiply, Subtract). The literal 2 rides Multiply's ConstB rather than costing a Constant node.

refract(i, n, eta) becomes the full HLSL definition — fourteen nodes:

k = 1 - eta*eta * (1 - dot(n, i)^2)
k < 0 ? 0 : eta*i - (eta*dot(n, i) + sqrt(k)) * n

The total-internal-reflection test is an If node, so both sides are translated and one is selected; sqrt of a negative k lands only on the discarded side, exactly as in HLSL. k == 0 still satisfies the formula (sqrt(0) == 0) and takes the refracted side. The zero branch is built as i * 0 rather than a constant so that its width always equals i's, which If requires of its two branches.

[!NOTE] Both are node-count-expensive by construction. Where the surrounding code is already HLSL, write them in a Function body instead and let the intrinsic do it in one node.

Notes

  • Two identical calls over identical operands — sin(X) written twice — become one node: the IR's structural de-duplication merges them before anything is emitted. See Node reuse.
  • Node positions come from the graph layout, like every other node.
  • sinh, cosh and tanh are refused with DSH4246: the material graph has no hyperbolic node. Write them in a Function body (or a /// @custom function in a .dss), where the shader compiler has them.
  • There is no matrix on this surface. The Unreal material graph has no matrix value type — no float3x3 value, no mul(M, v) — and a matrix that survives constant folding is DSH4361. The space-conversion nodes are the Transform builtins. For genuine matrix math, write a Function HLSL body, where float4x4 and mul are just HLSL.
  • Inside a Function HLSL body these names are not handled by the compiler at all; the body is emitted verbatim and HLSL's own intrinsics apply.
  • The decompiler writes these spellings back. Its 2.0 writer (.dss, the default) prints the builtin for any node of a class in the catalogue whose input pins are all connected and whose other properties are at their defaults — a Sine with a non-default Period stays UE.Sine(...) — and recognises the subgraphs fwidth, rcp, rsqrt, reflect and refract lower to when nothing else reads into them. The legacy writer (-Format Legacy, the Export Legacy entries) keeps its 1.x table.

Diagnostics

CodeRaised when
DSH4224the wrong number of arguments
DSH4216a named argument names no input pin of the node
DSH4215one pin is given twice
DSH4217an argument is missing between the ones given
DSH2158(.dss) a positional argument after a named one
DSH4226an argument is not a number, or its width does not fit the call's
DSH5289(info, 1.x only) a wider vector cut down to the width the call takes
DSH4243an integer division
DSH4246sinh, cosh or tanh
DSH5277(warning, 1.x only) a GLSL spelling
DSH4250a GLSL spelling in a .dss
DSH5275(warning, 1.x only) a name that matches a builtin only ignoring case
DSH4208the name is no builtin, type, node or function
DSH6206a function declared with a builtin's name
DSH8214the emitter could not create a node of the subgraph a builtin lowers to

Every code: diagnostics.

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, Alpha = 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     (float3: the widest operand)
Normalize(mixed)              -> unit      (float3)
DotProduct(unit, A)           -> d         (float, always)
Add / Multiply chain          -> Color

See also

On this page