DreamShaderLang
Language

Sections

Properties, Inputs, Outputs, Results, Settings, Options, Graph and Layout — which block accepts which, how repeats behave, and Group scopes.

The body of an attribute-taking block is a list of sections. A section is a name, an optional =, and a braced list of ;-separated statements.

<section> := <section-name> [=] { <statement>… } [;]
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> …

Section names are matched case-insensitively, may appear in any order, and may be repeated. The = is optional sugar since 1.5.0, and so is the ; after the closing }. All four of these are the same section:

Properties = { float A = 1.0; }
Properties   { float A = 1.0; }
properties = { float A = 1.0; };
PROPERTIES   { float A = 1.0 }

Support matrix

SectionShaderShaderFunctionShaderLayer / BlendVirtualFunction
Propertiesparameter nodesparameter nodesparameter nodesalias for Inputs
Inputsunknown sectionpin declarationspin declarations, arity-constrainedpin declarations
Outputsdeclarations + bindingspin declarationspin declarations, arity-constrainedpin declarations
Resultsunknown sectionalias for Outputsalias for Outputsalias for Outputs
Settingsmaterial settingsfour function keysfour function keysalias for Options
Optionsunknown sectionunknown sectionunknown sectionthe Asset reference
Graphrequired¹requiredrequiredhard error
Layoutyesyesyesunknown section
Codehard errorhard errorhard errorhard error
  1. Unless at least one output declaration carries an initializer since 1.3.4.

Function and GraphFunction have no sections at all — their { … } is raw HLSL.

Repeat behaviour

A repeated section does not always append. Getting this wrong silently loses statements.

SectionOn repeat
Properties, Inputs, Outputs, Resultsappends to the previous list
Settings, Optionsmerges; the later key wins
Graphoverwrites the previous body
Layoutresets — a second Layout discards the first entirely

Layout is the only section that throws its predecessor away. Only the last Layout block in a body has any effect. See Layout and #Region.

Properties

Declares the parameter nodes, constant nodes and UE.* builtin nodes a block generates into its own graph.

property-declaration := [ const ] <type-token> <name> [ = <default> ] [ [ <metadata> ] ] ;

group-scope          := Group( "<group-name>" ) { { <property-declaration> | <group-scope> }… } [ ; ]

The innermost [ … ] pair around <metadata> is literal DreamShaderLang punctuation; the outer pair is the meta-syntax for "optional". A declaration with metadata therefore reads float Roughness = 0.5 [Group="Surface"];.

Properties = {
    const float DebugScale = 1.0;                 // UMaterialExpressionConstant
    float Strength         = 1.0;                 // UMaterialExpressionScalarParameter
    vec3  Tint             = vec3(1.0, 1.0, 1.0); // UMaterialExpressionVectorParameter
    UE.TexCoord(Index = 0) UV;                    // a builtin node, not a parameter
}

A statement is decomposed in a fixed order, and that order is what makes type tokens containing spaces and parentheses work:

StepOperationConsequence
1A trailing [ … ] block is peeled off the endthe statement must end with ] for metadata to be seen at all
2Split at the first = outside (), [] and "…"that = is the only thing that sets "has a default value"
3Split the left side at the last top-level whitespacetype is everything before, name is everything after
4const is stripped from the front of the type tokenconst is detected after the type/name split

Because step 3 is parenthesis-aware and accepts any whitespace, UE.TexCoord(Index = 0) UV; splits into the type UE.TexCoord(Index = 0) and the name UV.

A property name is only checked for being non-empty — it is not validated as an identifier. Properties { float 1Bad = 0; } parses without a diagnostic; the declaration is simply unreachable from Graph. Inputs / Outputs names, by contrast, must match [A-Za-z_][A-Za-z0-9_]*.

Property nodes are created lazily, on first reference from Graph since 1.3.2. A property the Graph never mentions produces no node at all, so declaration order does not affect name resolution — only the vertical order of the generated nodes and the automatic sort counter below.

Type tokens, const, defaults and the metadata block are covered on Property Types and Metadata and Groups.

Group("Name") { … } scopes

since 1.5.0

A Group scope stamps its name onto every declaration inside it, so a shared group need not be repeated in each declaration's metadata.

Properties {
    Group("Surface") {
        ScalarParameter Roughness = 0.5 [Slider(0, 1)];
        VectorParameter BaseColor = float4(1, 1, 1, 1);
    }
}
RuleDetail
KeywordGroup, matched case-insensitively
Argumenta balanced ( … ) whose trimmed inner text must start with a "; the name is then unquoted and must be non-empty
Body{ … }; the walker is brace-, paren-, bracket- and string-aware
Terminatora single ; immediately after the closing } is consumed silently

Group(…) is the only construct that may open a { inside Properties. Any other { fails with Unexpected '{' in Properties near '{Statement}'. Only Group("Name") { ... } may open a brace here.

Scopes nest to any depth, and a nested scope's effective name is the enclosing name, a |, and the inner name — Unreal's own sub-category syntax:

Properties {
    Group("Surface") {
        float A = 0;                    // Group = "Surface"
        Group("Detail") {
            float B = 0;                // Group = "Surface|Detail"
            Group("Micro") {
                float C = 0;            // Group = "Surface|Detail|Micro"
            }
        }
    }
}

An explicit key on the declaration always wins. The inherited group is applied only when the member's metadata typed neither Group nor Category:

Group("Surface") {
    float A = 0;                        // Group = "Surface"    (inherited)
    float B = 0 [Group="Override"];     // Group = "Override"   (explicit wins)
    float C = 0 [Category="Other"];     // Group = "Other"      (Category is the alias)
}

Automatic SortPriority

Members of a group scope are auto-numbered by declaration order.

RuleValue
Counter start0
Counter step10
Counter scopeone counter shared by every group in the same Properties section, not one per group
Explicit SortPriority / Sortwins, and does not consume a slot
Ungrouped (top-level) declarationsnever auto-numbered, and never given a group
Properties {
    Group("Surface") {
        ScalarParameter A = 0.5;                    // SortPriority = 0
        VectorParameter B = float4(1, 1, 1, 1);     // SortPriority = 10
    }
    Group("Detail") {
        ScalarParameter C = 1.0 [SortPriority=99];  // SortPriority = 99, no slot consumed
        ScalarParameter D = 2.0;                    // SortPriority = 20  (counter continued)
    }
    ScalarParameter Loose = 3.0;                    // no group, no auto sort
}

The counter is seeded once per Properties section. A block that declares Properties twice gets a fresh counter starting at 0 in the second section, so two groups in two sections can end up with overlapping sort priorities.

When neither a group scope nor explicit metadata supplies a value, no SortPriority is written at all and the generated node keeps its class default.

Inputs, Outputs and Results

The typed-parameter sections. They declare the pins of a generated material function, or the interface a VirtualFunction describes.

parameter-declaration := [ opt ] <type> <name> [ = <default-expression> ] [ [ <metadata> ] ] ;

Results is a pure synonym for Outputs — it appends into the same list, with no warning.

MemberRequiredDescription
opt since 1.2.3noMarks the input optional in Unreal (bUsePreviewValueAsDefault).
<type>yesSee Types and Values.
<name>yesMust match [A-Za-z_][A-Za-z0-9_]*. Becomes the pin name.
= <default>noPreview value or preview graph. Meaningless on a function's outputs.
[ <metadata> ]noOnly Description / Desc / Tooltip and SortPriority / Sort have an effect.

The whitespace rule differs from Properties

PropertiesInputs / Outputs / Results
Type/name split pointlast top-level whitespace, parenthesis-awarelast literal space
Tab as the separatoracceptednot accepted
Type token may contain ( … )yesno
Name validated as an identifiernoyes

opt is recognised as the literal three letters followed by a space. opt<TAB>float Strength; parses without a diagnostic and produces a required input whose type token is opt plus the tab plus the real type; generation then fails with {Kind} '{Function}' input '{Name}' uses unsupported type '{Type}'.

in and out are not qualifiers in these sections. They exist only on the Function / GraphFunction signature form. Writing Inputs = { in float X; } splits into the type in float and the name X, and generation reports uses unsupported type 'in float'.

Input defaults

CaseBehaviour
Type is a plain scalar/vector and the default parses as a numeric literalwritten straight into PreviewValue
Anything elseevaluated as a graph expression and connected to the input's Preview pin

The graph-expression path is what lets a preview default reference a node the block itself generates since 1.2.6:

ShaderFunction(Name="Functions/F_Sample")
{
    Properties = {
        const Texture2D PreviewTex = Path(Engine, "EngineResources/DefaultTexture");
    }
    Inputs = {
        opt Texture2D Tex = PreviewTex;      // preview graph, not a literal
        opt float     Mix = 0.5;             // literal → PreviewValue
    }
    Outputs = { vec4 OutColor; }
    Graph   = { OutColor = Tex(Coordinates = UE.TexCoord(Index = 0)) * Mix; }
}

opt — and only opt — is what marks the pin optional. A default on a non-opt input still builds the preview graph but leaves the pin required.

= <expression> on a material function's Outputs / Results entry is parsed and then ignored. A function output is only ever driven by the Graph. This does not apply to a Shader's Outputs, where an initializer is meaningful since 1.3.4.

Properties versus Inputs

Both put something into a generated function, but they are different grammars producing different nodes.

PropertiesInputs
Generatesa parameter, constant or UE.* node inside the function grapha FunctionInput pin on the function's interface
Visible to callersas a material parameter on any material that uses the functionas a wired input pin
Name validationnon-empty onlymust match the identifier rule
Type/name separatorany whitespacea literal space only

Property names must be unique within the block and must not collide with an input name. Both checks are case-insensitive and share one diagnostic: {Kind} '{Function}' property '{Name}' conflicts with another property or input name.

Outputs in a Shader

A Shader's Outputs fills two lists from one body, classifying each statement independently.

Statement shapeClassified as
no top-level =bare output-variable declaration
top-level =, left side is a valid typed declarationinitialized output declaration since 1.3.4
top-level =, left side is not a valid typed declarationoutput binding
Outputs = {
    vec3  Color;
    float Alpha;

    Base.EmissiveColor = Color;
    Base.Opacity       = Alpha;
}
  • Declarations and bindings may be interleaved freely; a binding may reference a variable declared later in the same section.
  • The name return is reserved. It may not be declared, and as a binding source it may only feed Base.* targets — never in a Shader that has a Graph block.
  • No [ … ] metadata block is accepted on any Shader Outputs statement. The metadata parser is never invoked there, so a bracketed block is left inside the statement text and produces an Invalid typed declaration or Invalid output binding error.

The full Base.* catalogue and the Expression( … ).Pin[i] form are on Output Bindings.

Settings and Options

Both use one statement grammar: <Key> = <Value> ;, split at the first = outside (), [] and "…". Keys are trimmed and lower-cased; values have one surrounding "…" pair stripped; a duplicate key silently overwrites the earlier one.

BlockWhat Settings means
Shadermaterial settings — special keys plus reflected UMaterial properties. See Material Settings.
ShaderFunction, ShaderLayer, ShaderLayerBlendexactly four honoured keys: Description, UserExposedCaption, ExposeToLibrary, LibraryCategories. Every other key is parsed, stored and silently ignored.
VirtualFunctionan alias for Options, whose only consumed key is Asset.

The four material-function keys are reset when absent, so removing a key from the source removes it from the asset. LibraryCategories is comma-split, each entry trimmed, empty entries dropped.

Graph

The node-graph body. The declaration parser does not look inside it — it stores the text between Graph = { and its matching } verbatim and hands it to a separate expression grammar at generation time. Comments are not stripped from it, and #Region directives are recognised only here.

Graph = {
    vec2 UV  = UE.TexCoord(Index = 0);
    vec4 Tex = BaseTex(Coordinates = UV);
    Color = Tex.rgb * Tint;
}

The statement and expression language is documented in The Graph Language.

Layout

Pins generated node positions and declares comment boxes. See Layout and #Region.

Diagnostics

MessageCauseFix
Invalid property declaration '{Statement}'.No top-level whitespace separating the Properties type token from the name.
Missing property name in declaration '{Statement}'.The name side of the split is empty.
Missing property type after const in declaration '{Statement}'.const with nothing after it.
Unsupported property type '{Type}'.The token matches no compact token, no parameter-node token and no UE. prefix.Details
Metadata must follow a declaration.The statement is nothing but a [ … ] block.
Unexpected '{' in Properties near '{Statement}'. Only Group("Name") { ... } may open a brace here.A { inside Properties that is not a Group scope head.
Group(...) requires a non-empty name.Group(""), or an argument that does not start with a quote.
Unterminated Group("{Name}") { ... } block.The scope's { is never closed.
Invalid typed declaration '{Statement}'.No literal space between type and name in Inputs / Outputs / Results, an empty side, or a name that is not an identifier.Use a space, not a tab. Details
Invalid setting declaration '{Statement}'.A Settings or Options statement with no top-level =.
Invalid empty setting key in '{Statement}'.A Settings or Options statement whose key side is empty.
Metadata entry '{Entry}' must use Key=Value syntax.A metadata entry with no top-level = that is not Slider(…).Details
Metadata key '{Key}' is declared more than once.Duplicate metadata key after normalisation.
Metadata SortPriority value '{Value}' is not an integer.A non-integer sort priority.
{Kind} '{Function}' property '{Name}' conflicts with another property or input name.A duplicate property name, or a property that shadows an input. Both compared ignoring case.
{File}: Property '{Name}' is declared more than once. Property names must be unique.Two Shader properties whose names are equal ignoring case.

Where to next

On this page