DreamShaderLang
HLSL / GLSL Background

GLSL Overview

GLSL for DreamShaderLang authors — which GLSL spellings are first-class tokens, which are rewritten inside Function bodies, and what has no equivalent at all.

GLSL is what most shader examples on the internet are written in. DreamShaderLang is not a GLSL compiler and never emits GLSL — the target is Unreal's material graph and HLSL. But GLSL spellings are deliberately accommodated, in two quite different ways, and telling the two apart is the whole point of this page.

MechanismWhereWhat happens
First-class tokenProperties, Inputs, Outputs, Graph declarations, Graph constructorsvec3 is a type token. Nothing is rewritten; it is stored and reported as you wrote it
Identifier rewriteFunction / GraphFunction signatures and bodieswhole identifiers are substituted before the HLSL is emitted, so vec3 becomes float3 and mix becomes lerp
// GLSL
vec3 applyTint(vec3 color, vec3 tint)
{
    return color * tint;
}
// DreamShaderLang — the same helper. vec3 survives in the declaration,
// and becomes float3 in the generated HLSL.
Function vec3 ApplyTint(in vec3 color, in vec3 tint) {
    return color * tint;
}

GLSL type aliases

These are real tokens, accepted everywhere the corresponding float / int / uint / bool spelling is accepted.

GLSL spellingEquivalent
vec2 vec3 vec4float2 float3 float4
ivec2 ivec3 ivec4int2 int3 int4
uvec2 uvec3 uvec4uint2 uint3 uint4
bvec2 bvec3 bvec4bool2 bool3 bool4

There is no vec1, ivec1, uvec1 or bvec1, and no GLSL vector spelling for half. The single-component GLSL-style forms simply do not exist — use float or float1.

Because int, uint, bool and half all collapse to the same float component counts, choosing between int3, ivec3, bool3 and float3 is a documentation decision, not a semantic one. The single observable difference is the integer marker set by an integer constructor call in a Graph block, which exists solely to reject integer division. See Types and Values.

Rewrites inside function bodies

A Function / GraphFunction signature runs every type token through a normalizer, and the body runs every identifier through a superset of the same map. Both match on the lower-cased whole identifier, so the rewrite is case-insensitive.

Rewritten identifierBecomesIn signature typesIn body text
vec2 vec3 vec4float2 float3 float4
ivec2 ivec3 ivec4int2 int3 int4
uvec2 uvec3 uvec4uint2 uint3 uint4
bvec2 bvec3 bvec4bool2 bool3 bool4
mat2float2x2
mat3float3x3
mat4float4x4
mixlerp
fractfrac
modfmod

15 signature aliases; 18 body aliases. The rewrite is comment- and string-aware, and it applies to Function and GraphFunction bodies only — a Graph block, a Shader body and a ShaderFunction body are not normalized.

The body rewrite matches the whole identifier, ignoring case. A helper, local variable or struct member named Mix, Mod, Fract, Vec3, Mat4 … inside a Function or GraphFunction body is silently renamed to lerp, fmod, frac, float3, float4x4. There is no diagnostic; the failure surfaces as an HLSL compile error, or as silently different math. Rename the identifier.

Note that mat2 / mat3 / mat4 rewrite cleanly but resolve to types the language does not support. The declaration parses and the call fails — … input 'basis' uses unsupported type 'float3x3'. See Matrices.

GLSL function names in a Graph block

Inside a Graph block nothing is rewritten, so the GLSL spellings have to be real builtins — and three of them are:

GLSL nameIn a Graph blockIn a Function body
mixa real builtin, an alias of lerprewritten to lerp
fracta real builtin since 1.5.0, same node as fracrewritten to frac
moda real builtin since 1.5.0, same node as fmodrewritten to fmod
clamp, dot, min, max, pow, sqrt, abs, floor, ceil, sin, cos, normalizespelled the same in both languages — real builtinsHLSL intrinsics
smoothstep, step, length, distance, cross, reflect, refract, exp, lognot builtins — use UE.Expression(Class = …) or a Function bodyHLSL intrinsics, available directly

The complete builtin catalogue is Math Builtins.

Constructors and swizzle

Both survive the trip, with GLSL spelling intact.

Graph = {
    vec3  color = vec3(1.0, 0.2, 0.1);
    float r     = color.r;
    vec2  rg    = color.rg;
    vec3  bgr   = color.bgr;
}
  • Constructor names follow the type tokens, so vec2(…), vec3(…), vec4(…), ivec3(…) and so on all work in a Graph block.
  • Swizzles accept between one and four channel characters, from either the xyzw or the rgba set, matched case-insensitively. Reordering and repeating are both allowed. There is no stpq set and no uv set — .uv fails.
  • A swizzle can only narrow or rearrange. Roughness.yz on a scalar is a hard error, not a broadcast: Swizzle 'yz' is invalid for a value with 1 components. Repeat channel 0 (.xx) or use a constructor instead.

Details: Expressions and Conversions.

What has no equivalent

GLSL habitIn DreamShaderLang
void main() { … }none — a Shader block with Outputs bindings describes the material
uniform float x;a Properties declaration; the node becomes a real material parameter
in / out / varying / attribute at file scopenone — inputs come from UE.* nodes and parameters
sampler2D + texture(s, uv)a texture property plus SampleTexture2D(tex, uv), or a TextureSampleParameter2D called as Tex(Coordinates = uv)
layout(…), lowp / mediump / highpnone
mat3 m; m * vno matrix types — use UE.TransformVector / UE.TransformPosition
#version, #extensionnone
discardvalid inside a Function HLSL body only

Porting a snippet

A typical GLSL fragment, and the two ways to land it.

float ring(vec2 uv, float t)
{
    vec2  p = uv - 0.5;
    float d = length(p);
    return fract(sin(t) * 0.5 + d * 4.0);
}

As a Function, where the body is HLSL and the aliases are rewritten for you:

Function float Ring(in vec2 uv, in float t)
{
    vec2  p = uv - 0.5;          // vec2 -> float2 on emission
    float d = length(p);         // HLSL intrinsic, fine inside a body
    return fract(sin(t) * 0.5 + d * 4.0);   // fract -> frac
}

As Graph statements, where nothing is rewritten and length does not exist as a builtin:

Graph = {
    vec2  uv = UE.TexCoord(Index = 0);
    vec2  p  = uv - 0.5;
    float d  = sqrt(dot(p, p));                    // length, spelled out
    float r  = fract(sin(UE.Time()) * 0.5 + d * 4.0);
}

The Function version is shorter; the Graph version produces inspectable material nodes that can be wired to parameters. Pick per case — see Functions.

Common GLSL keywords

For reference. Nothing in this table is part of the DreamShaderLang grammar except where noted above.

CategoryKeywords
Typesvoid, bool, int, uint, float, double, vec2, vec3, vec4, mat2, mat3, mat4
Control flowif, else, for, while, do, switch, case, default
Jumpsreturn, break, continue, discard
Parametersin, out, inout
Modifiersconst, uniform, layout, centroid, flat, smooth, attribute, varying
Precisionlowp, mediump, highp

Where to next

On this page