DreamShaderLang
HLSL / GLSL Background

GLSL Overview

GLSL for DreamShaderLang authors — which GLSL spellings are first-class tokens in a 1.x source, which are rewritten inside Function bodies, which carry a warning, why a .dss takes HLSL only, 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. The 1.x syntax accommodates GLSL spellings deliberately, in three different ways; a .dss takes HLSL only. Telling these apart is the whole point of this page.

MechanismWhereWhat happens
First-class token1.x Properties, Inputs, Outputs, Graph declarations, Graph constructorsvec3 is a type token, read as float3; no diagnostic
Identifier rewrite1.x Function / GraphFunction signatures and bodieswhole identifiers are substituted before the HLSL is emitted, so vec3 becomes float3 and mix becomes lerp; no diagnostic
Legacy rule with a warning since 2.0.0mix, fract, mod, inversesqrt in a 1.x Graphread as lerp, frac, fmod, rsqrt, with the warning DSH5277
HLSL only.dssno GLSL spellings: a function name such as mix is the error DSH4250, and vec3 is no type (DSH4201)
// 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

In a 1.x source these are real tokens, accepted everywhere the corresponding float / int / uint / bool spelling is accepted, and read as that spelling. A .dss does not take them — write the HLSL spelling; dsc migrate respells them for you.

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 gives no diagnostic; DSH5277 applies to Graph text only.

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 a graph has no matrix values. The declaration parses and the call fails: a Graph call that passes a matrix to a pin is DSH4361. See Matrices.

GLSL function names in a Graph block

Math builtins are matched by their exact HLSL name since 2.0.0. Four GLSL spellings still work in a 1.x Graph, as a legacy rule that says so:

GLSL nameIn a 1.x Graph blockIn a .dssIn a 1.x Function body
mixread as lerp, warning DSH5277the error DSH4250 — write lerprewritten to lerp
fractread as frac, warning DSH5277the error DSH4250 — write fracrewritten to frac
modread as fmod, warning DSH5277the error DSH4250 — write fmodrewritten to fmod
inversesqrtread as rsqrt, warning DSH5277the error DSH4250 — write rsqrtno such HLSL name
clamp, dot, min, max, pow, sqrt, abs, floor, ceil, sin, cos, normalizespelled the same in both languages — real builtinsthe sameHLSL intrinsics
smoothstep, step, length, cross, reflect, refractreal builtins since 1.6.0 — spelled the same in both languagesthe sameHLSL intrinsics
distance, exp, exp2, log, log2real builtins since 2.0.0the sameHLSL intrinsics

Note that the Sine / Cosine nodes sin / cos build take cycles (Period = 1), not radians — only HLSL sin in a body takes radians. The complete builtin catalogue is Math Builtins.

Constructors and swizzle

In a 1.x source both survive the trip as written; in a .dss spell the type the HLSL way (float3(…)), and the swizzle rules are the same.

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. Reordering and repeated reads are both allowed; mixing the two sets in one swizzle (.xg) is DSH4230 since 2.0.0. There is no stpq set and no uv set — .uv is DSH4230.
  • A swizzle can only narrow or rearrange. Roughness.yz on a scalar is DSH4230, not a broadcast. Repeat channel 0 (.xx) or use a constructor instead.

Details: Expressions and Conversions.

What has no equivalent

GLSL habitIn DreamShaderLang
void main() { … }a .dss entry, export void M(inout material m); in 1.x a Shader block with Outputs bindings
uniform float x;in a .dss it is uniform float x;, with /// tags; in 1.x a Properties declaration. Either becomes a real material parameter
in / out / varying / attribute at file scopenone — inputs come from UE.* nodes and parameters
sampler2D + texture(s, uv)in a .dss, uniform Texture2D Tex; and Tex.Sample(uv); in 1.x a texture property plus SampleTexture2D(tex, uv), or a TextureSampleParameter2D called as Tex(Coordinates = uv)
layout(…), lowp / mediump / highpnone
mat3 m; m * vno matrix values in a graph (DSH4361) — use UE.TransformVector / UE.TransformPosition, or compute it in a /// @custom / Function body
#version, #extensionnone; the preprocessor has just the #if / #define family
for, whilea .dss has them (a loop whose count is known only at run time goes in a /// @custom function); a 1.x Graph does not (DSH2208)
discardvalid inside a /// @custom / Function HLSL body only

Porting a snippet

A typical GLSL fragment, and the 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 .dss code, respelled the HLSL way, where every call is a real node:

float Ring(float2 uv, float t)
{
    float2 p = uv - 0.5;
    float  d = length(p);
    return frac(sin(t) * 0.5 + d * 4.0);    // note: sin in the graph takes cycles
}

Put a /// @custom line above the function and its body becomes the HLSL of one Custom node instead (sin in radians).

As a 1.x 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 1.x Graph statements, where length and frac are builtins; fract compiles too, with the warning DSH5277:

Graph = {
    vec2  uv = UE.TexCoord(Index = 0);
    vec2  p  = uv - 0.5;
    float d  = length(p);                          // a builtin since 1.6.0
    float r  = frac(sin(UE.Time()) * 0.5 + d * 4.0);
}

Before 1.6.0, length was not a builtin and this line had to be spelled out as sqrt(dot(p, p)). Older sources written that way are still perfectly valid — just no longer necessary.

The /// @custom / Function version is one Custom node; the graph-code version produces inspectable material nodes that can be wired to parameters. Pick per case — see Functions and DreamShaderLang 2.0.

Common GLSL keywords

For reference. Nothing in this table is part of the DreamShaderLang grammar except where noted above (in a .dss, if, for, while, return, in / out / inout, const and uniform are the HLSL keywords of the same name).

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