DreamShaderLang
Parameters

Metadata and Groups

The trailing [ … ] block — recognized keys and aliases, Slider(min,max), Group scopes and the SortPriority counter, and the reflected-property passthrough.

A declaration can carry a trailing [ … ] block: a list of Key = Value entries that set the generated node's organization fields and — for any key the parser does not recognize — write directly to a reflected UPROPERTY of that node's class.

That last part is what makes the block useful. Only five entry forms are special-cased; everything else is looked up on the Unreal class by reflection, so any property the engine exposes on the node you generated is reachable from source.

Writing a block

<declaration> [ <entry> ; <entry> ; … ] ;
<declaration> [ <entry> , <entry> , … ] ;

<entry> := <key> = <value>
         | Slider( <min> , <max> )

Every [, ], ;, ,, = and ( ) above is literal punctuation. ; and , may be mixed in one block, and a trailing separator before ] is accepted. Slider(…) is the only entry form without an =.

ScalarParameter Roughness = 0.5 [Group="Surface"; Slider(0, 1); Desc="Micro-surface roughness"];
RuleBehaviour
The block must be the last thing in the statementa statement that does not end with ] has no metadata at all — the [ … ] stays in the declaration text and fails elsewhere
The opening [ is found at paren-depth 0 and bracket-depth 0brackets inside ( … ) and inside string literals are safe
Text before the block must be non-emptyotherwise Metadata must follow a declaration.
Entries split on ; or , at top levelmixing both in one block is accepted
A trailing ; or , before ]accepted; the empty entry is dropped
Keys are trimmed and lower-cased before comparison[ Group = "X" ], [group="X"] and [GROUP="X"] are the same entry
Values are unquoted, then trimmed" X " becomes X; an unquoted value is trimmed as written
A duplicate key (after lower-casing)Metadata key '{Key}' is declared more than once.

Semicolon-separated entries arrived in since 1.2.4; the block itself in since 1.2.3.

Recognized keys

KeyAliasesValueEffect
GroupCategorystringThe node's parameter group; also written to the Group UPROPERTY (an FName)
DescriptionDesc, TooltipstringWritten to the node's Desc UPROPERTY
SortPrioritySortintegerThe node's SortPriority; a non-integer is an error
ParameterNamestringOverrides the material parameter name; the declared identifier is used when absent
Slider(min, max)two numbers, no =Expands to the reflected SliderMin and SliderMax properties
anything elsesee Reflected passthroughWritten to the same-named reflected UPROPERTY of the generated node's class

All key comparisons are case-insensitive. Every entry — including the recognized ones — is also stored under its lower-cased key and pushed through the reflected-property writer, so the recognized keys are the ones with an extra typed effect, not a separate namespace.

SortPriority defaults to 32 when nothing sets it, matching Unreal's own node default.

Slider(min, max)

since 1.5.0
ScalarParameter Roughness = 0.5 [Slider(0, 1)];

Matched case-insensitively, must end with ), and the inner text must split on a top-level , into exactly two numeric values. Both are written as reflected SliderMin / SliderMax, so it is exactly equivalent to [SliderMin = 0; SliderMax = 1] — and combining the two forms is a duplicate.

WrittenResult
[Slider(0, 1)]SliderMin = 0, SliderMax = 1
[SliderMin = 0; SliderMax = 1]identical
[Slider(0, 1); SliderMin = 0]Metadata SliderMin/SliderMax is declared more than once (entry '{Entry}').
[Slider(0)] / [Slider(0, 1, 2)] / [Slider(a, b)]Metadata 'Slider(min, max)' requires exactly two numeric bounds: '{Entry}'.

SliderMin / SliderMax exist on UMaterialExpressionScalarParameter. On a class without them the entry fails as an unreflected property.

ParameterName

ScalarParameter Rough [ParameterName = "Surface Roughness"];

The declared identifier stays the name Graph uses; ParameterName changes only the name the material exposes to instances and Blueprints. An empty value falls back to the declared name.

ParameterName is also left in the reflected-property list, so it is written twice — once as the node's parameter name and once through reflection. Harmless on a UMaterialExpressionParameter subclass. On a class with no ParameterName UPROPERTY — DynamicParameter is the one in the token set — [ParameterName="…"] is a hard error, not a warning.

Alias rewriting and auto-injection

Before anything is written, the entry list is completed and its keys are rewritten:

StepBehaviour
1Group is injected as key group unless Group or Category was typed literally
2SortPriority is injected unless SortPriority or Sort was typed literally
3Description is injected as key desc unless Description, Desc or Tooltip was typed literally
4keys are rewritten to their real UPROPERTY names
Typed keyReflected UPROPERTY
DescriptionDesc
TooltipDesc
CategoryGroup
SortSortPriority
anything elsepasses through as the lower-cased key

Injection in steps 1–3 is what applies a value that came from somewhere other than this block — most often the enclosing Group("Name") { … } scope.

Reflected property passthrough

Any key that is not Slider(…) is resolved to an FProperty on the generated node's class:

  1. the key is trimmed, lower-cased and compared against each UPROPERTY name, case-insensitively;
  2. if that fails, a second pass strips a leading b from every FBoolProperty name and compares again.

So [FractionalPart = true] binds bFractionalPart, and [UseCustomPrimitiveData = true] binds bUseCustomPrimitiveData. Writing the b explicitly also works.

Value grammar by property type

Property typeAccepted textError on failure
booltrue / false, case-insensitive'{Value}' is not a valid boolean value for '{Property}'.
int32a signed 32-bit integer'{Value}' is not a valid integer value for '{Property}'.
uint32an integer in [0, 4294967295]'{Value}' is not a valid unsigned integer value for '{Property}'.
floata number, or true / false (1.0 / 0.0)'{Value}' is not a valid numeric value for '{Property}'.
doubleas float'{Value}' is not a valid numeric value for '{Property}'.
FStringanything, verbatim after trimming
FNameanything, converted to an FName
object referencePath(…), an absolute /… path, or a bare pathsee Object properties
enuman enum literal, four spellings'{Value}' is not a valid enum value for '{Property}'.
uint8 backed by an enuman enum literal'{Value}' is not a valid enum value for '{Property}'.
plain uint8an integer in [0, 255]'{Value}' is not a valid byte value for '{Property}'.
anything else (structs, arrays, …)Unreal's own import text, e.g. (R=1,G=0,B=0,A=1)Property '{Property}' on '{Class}' is not a supported literal type yet.

Enum literals

An enum value is normalized by trimming, lower-casing and then removing every space, _, -, :, . and /. Each enumerator that is not tagged UMETA(Hidden) is tried against four spellings:

#SpellingExample for SAMPLERTYPE_LinearColor
1short nameSAMPLERTYPE_LinearColor
2fully qualified nameEMaterialSamplerType::SAMPLERTYPE_LinearColor
3DisplayName metadataLinear Color
4short name with everything up to the first _ removedLinearColor

Because of the normalization, "LinearColor", "linear color", "linear-color", "SAMPLERTYPE_LinearColor" and "linear.color" all select the same value.

_MAX sentinels of engine enums usually carry no Hidden metadata, so a value such as "SAMPLERTYPE_MAX" resolves rather than erroring — and writes something that is not a real value. Do not use them.

Object properties

An object-valued UPROPERTY takes an asset reference resolved by the metadata resolver described on Asset References.

SituationResult
Value starts with Path( or / and does not resolvethe resolver's own message is reported
Value is neither and does not resolveObject property '{Property}' expects Path(...) or an absolute Unreal object path.
Asset loads but is the wrong classAsset '{Path}' is not compatible with '{Property}'. Expected '{Class}'.
Asset fails to loadFailed to load asset '{Path}' for '{Property}'.

A texture that fails to load is silently accepted. When the property is a UTexture subclass and is named exactly Texture or TextureObject, a failed load writes nullptr and the write is reported as successful. [Texture = Path(Game, "Typo")] therefore generates with no diagnostic and leaves an unbound sampler, which fails later at shader compile. Check the asset path when a sampler comes out empty.

Organization fields that are not reflected

Group, SortPriority and Desc exist on UMaterialExpressionParameter subclasses — but not every parameter node is one. UMaterialExpressionDynamicParameter is not. When one of those three fields is missing from the class it is skipped with a warning, not an error:

'{Class}' does not expose the '{Field}' organization field; ignoring it for this parameter.

Any other unresolved key is a hard error:

Metadata property '{Key}' is not a reflected property on '{Class}'.

The practical consequence: DynamicParameter Dyn = float4(0,0,0,0) [Group="S"; SortPriority=10]; compiles, logs two warnings, and the node ends up in no group. Its default value is applied, and its parameter name is written to ParamNames[0].

Group(…) scopes and the SortPriority counter

since 1.5.0 A Group("Name") { … } scope inside a Properties section stamps its members. How it interacts with an explicit entry:

SituationResult
Member typed neither Group nor Categorythe enclosing group is injected
Member typed Group or Categorythe typed value wins; the scope is ignored for that member
Nested scopescomposed with |Group("Outer") { Group("Inner") { … } } yields Outer|Inner
A literal Group("Manual|Literal")passes through unchanged
Member typed neither SortPriority nor Sortit receives the next value from an auto counter
Member typed SortPriority or Sortthe typed value wins and does not consume a counter slot
Declaration outside any scopeuntouched — no group, and no auto sort priority

The counter starts at 0 and steps by 10. It is shared across every scope in the block, not reset per 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]; // 99 — does not consume a slot
        ScalarParameter D = 2.0;                   // SortPriority = 20
    }
    ScalarParameter Loose = 3.0;                   // no group, no auto sort
}

Texture keys

The keys you will write most often are reflected ones on a texture node. They have no dedicated code path — SamplerType is an ordinary reflected key that happens to land on a TEnumAsByte<EMaterialSamplerType> UPROPERTY — but they are worth knowing by name.

KeyTypeDefault
SamplerTypeEMaterialSamplerTypederived from the asset
SamplerSourceESamplerSourceModeFromTextureAsset
MipValueModeETextureMipValueModeNone
GatherMode since UE 5.6enumNone
AutomaticViewMipBiasbooltrue
ConstCoordinateuint80
ConstMipValueint32-1
IsDefaultMeshpaintTextureboolfalse

MipValueMode is the one key that changes the node's pin set, and therefore what the call form can wire.

SamplerType values

17 enumerators. All four spellings of a row select it, and the comparison ignores case and strips , _, -, :, . and /.

ValueEnum constantDisplayName spelling
ColorSAMPLERTYPE_ColorColor
GrayscaleSAMPLERTYPE_GrayscaleGrayscale
AlphaSAMPLERTYPE_AlphaAlpha
NormalSAMPLERTYPE_NormalNormal
MasksSAMPLERTYPE_MasksMasks
DistanceFieldFontSAMPLERTYPE_DistanceFieldFontDistance Field Font
LinearColorSAMPLERTYPE_LinearColorLinear Color
LinearGrayscaleSAMPLERTYPE_LinearGrayscaleLinear Grayscale
DataSAMPLERTYPE_DataData
ExternalSAMPLERTYPE_ExternalExternal
VirtualColorSAMPLERTYPE_VirtualColorVirtual Color
VirtualGrayscaleSAMPLERTYPE_VirtualGrayscaleVirtual Grayscale
VirtualAlphaSAMPLERTYPE_VirtualAlphaVirtual Alpha
VirtualNormalSAMPLERTYPE_VirtualNormalVirtual Normal
VirtualMasksSAMPLERTYPE_VirtualMasksVirtual Mask
VirtualLinearColorSAMPLERTYPE_VirtualLinearColorVirtual Linear Color
VirtualLinearGrayscaleSAMPLERTYPE_VirtualLinearGrayscaleVirtual Linear Grayscale

There is no virtual counterpart for DistanceFieldFont or External.

SamplerSource values

ValueEnum constantDisplayNameMeaning
FromTextureAssetSSM_FromTextureAssetFrom texture assetTake the sampler from the texture; consumes one of the shader's limited sampler slots
Wrap_WorldGroupSettingsSSM_Wrap_WorldGroupSettingsShared: WrapShared sampler, wrap addressing, filter from the world texture group; consumes no slot
Clamp_WorldGroupSettingsSSM_Clamp_WorldGroupSettingsShared: ClampShared sampler, clamp addressing; consumes no slot

SSM_TerrainWeightmapGroupSettings is tagged UMETA(Hidden) and is therefore not selectable. Because Shared: Wrap normalizes to sharedwrap, both [SamplerSource="Shared: Wrap"] and [SamplerSource="SharedWrap"] select the same value.

Application order

StepWhat happens
1The node is created and its texture asset assigned — from = Path(…), from the engine fallback for the declared dimension, or by SetDefaultTexture()
2AutoSetSampleType() runs, deriving SamplerType from the asset's compression settings and sRGB flag
3The [ … ] block is applied

Because metadata is applied last, an explicit [SamplerType=…] always wins over the inferred value. Omitting the key means the value follows the asset — and changes if the asset is later recompressed. The decompiler therefore emits SamplerType and the texture-sample keys explicitly on every export, so a decompile → recompile round trip is stable.

Where else the block is accepted

  • On Inputs / Outputs / Results typed parameters of a material function, but there only Description / Desc / Tooltip and SortPriority / Sort have any effect. Group is parsed and retained but never applied — Unreal's function input/output nodes have no group field — and every other key is ignored rather than reflected. An input's SortPriority defaults to its declaration index.
  • Not on a Shader's Outputs statements, nor on Settings, Options or Layout entries.
  • On a const declaration: [Desc="…"] on a Constant node works.

Diagnostics

Parse time

MessageCause
Metadata must follow a declaration.The statement is only a [ … ] block.
Metadata entry '{Entry}' must use Key=Value syntax.An entry with no top-level = that is not Slider(…).
Invalid metadata entry '{Entry}'.The key is empty after normalization.
Metadata key '{Key}' is declared more than once.A duplicate key; the message quotes the spelling as typed.
Metadata 'Slider(min, max)' requires exactly two numeric bounds: '{Entry}'.Wrong arity or a non-numeric bound.
Metadata SliderMin/SliderMax is declared more than once (entry '{Entry}').Slider(…) combined with an explicit SliderMin / SliderMax.
Metadata SortPriority value '{Value}' is not an integer.A non-integer SortPriority / Sort.

Generation time

MessageCauseFix
Metadata property '{Key}' is not a reflected property on '{Class}'.No UPROPERTY matched, and the key is not one of the three organization fields.Check the property name on the Unreal class the token generates. Details
Metadata property '{Key}' on '{Class}': {Inner}The property was found but the value could not be converted.
'{Class}' does not expose the '{Field}' organization field; ignoring it for this parameter.Warning — Group, SortPriority or Desc is missing from the class, as on DynamicParameter.
'{Class}' does not expose a ParameterName property.ParameterName written on a class without that UPROPERTY.
'{Value}' is not a valid boolean value for '{Property}'.Boolean UPROPERTY, non-boolean text.
'{Value}' is not a valid integer value for '{Property}'.int32 UPROPERTY.
'{Value}' is not a valid unsigned integer value for '{Property}'.uint32 UPROPERTY, or out of range.
'{Value}' is not a valid numeric value for '{Property}'.float or double UPROPERTY.
'{Value}' is not a valid byte value for '{Property}'.Plain uint8 UPROPERTY, or out of [0, 255].
'{Value}' is not a valid enum value for '{Property}'.No enumerator matched under any of the four spellings.
Object property '{Property}' expects Path(...) or an absolute Unreal object path.An object UPROPERTY given text that is neither.Details
Asset '{Path}' is not compatible with '{Property}'. Expected '{Class}'.The asset loaded but is the wrong class.
Failed to load asset '{Path}' for '{Property}'.The asset could not be loaded — except the silent-null texture case above.
Property '{Property}' on '{Class}' is not a supported literal type yet.A struct or container UPROPERTY whose import text was rejected.
property '{Name}': {Inner}Wrapper applied to every metadata failure, naming the declaration.

Example

Shader(Name="Docs/M_Metadata")
{
    Properties = {
        Group("11 - Specular") {
            TextureSampleParameter2D MetallicMap = Path(Game, "Textures/T_White_Linear") [
                SamplerType          = "LinearColor";
                SamplerSource        = "FromTextureAsset";
                MipValueMode         = "None";
                AutomaticViewMipBias = true;
                ConstCoordinate      = 0;
                ConstMipValue        = -1;
                Description          = "Packed metallic / roughness";
            ];

            ScalarParameter Metallic = 0.0 [Slider(0, 1); SortPriority = 51];
        }

        VectorParameter Tint = float4(1, 1, 1, 1) [
            Category               = "Look",
            Tooltip                = "Multiplied over base colour",
            UseCustomPrimitiveData = false,
            ParameterName          = "Base Tint"
        ];
    }

    Settings = { Domain = "Surface"; ShadingModel = "DefaultLit"; BlendMode = "Opaque"; }
    Outputs  = { vec3 Color; float M; Base.BaseColor = Color; Base.Metallic = M; }

    Graph = {
        vec4 S = MetallicMap(Coordinates = UE.TexCoord(Index = 0));
        Color = S.rgb * Tint.rgb;
        M     = S.b * Metallic;
    }
}

Applied properties:

MetallicMap  Group="11 - Specular"  SortPriority=0   Desc="Packed metallic / roughness"
             SamplerType=SAMPLERTYPE_LinearColor     SamplerSource=SSM_FromTextureAsset
             MipValueMode=TMVM_None  bAutomaticViewMipBias=true
             ConstCoordinate=0       ConstMipValue=-1
Metallic     Group="11 - Specular"  SortPriority=51  SliderMin=0  SliderMax=1
Tint         Group="Look"           Desc="Multiplied over base colour"
             bUseCustomPrimitiveData=false           ParameterName="Base Tint"

Metallic takes SortPriority = 51 from its own entry, so it does not consume a counter slot; MetallicMap took slot 0.

Next

  • Property Types — the tokens a metadata block can follow, and the class-specific keys each one exposes
  • Asset References — the grammar object-valued entries accept
  • Material Settings — the same reflection idea, applied to UMaterial
  • Decompiler — which keys a round trip always emits

On this page