Files
UShaderLab/README.md
2026-07-04 13:20:24 +08:00

12 KiB

UShaderLab Guide

English | 中文

ExampleImage

UShaderLab lets you write Unreal Engine materials in a text format that feels close to HLSL. You write .usl files, and the plugin parses them into material graphs with editable parameters exposed through ShaderLab Material Instances.

Basic Workflow

  1. Create a .usl file under <Project>/Shaders or a plugin's <Plugin>/Shaders directory.
  2. Add #include "/Plugin/ShaderLab/ShaderLab.ush" at the top so the IDE can recognize DSL macros and UE_ intrinsics.
  3. Use SL_SETTINGS for material settings, and SL_PROPERTY for instance parameters.
  4. Write the material body with entries such as SL_SURFACE, SL_UNLIT, or SL_POSTPROCESS.
  5. In the Content Browser, create a ShaderLab Material Instance and choose the corresponding ShaderLab base material.
  6. Adjust parameters on the generated instance, then assign it to meshes, UI, post process volumes, or other supported targets.

It is recommended to run this once in the editor console:

ShaderLab.IDE.Prepare

This prepares basic HLSL recognition and path mapping for .usl and .uslfunc, making IDE completion more useful.

VSCode users are recommended to install the shader-validator extension. UShaderLab completion is already supported.

Minimal Example

Here is a minimal Surface material:

#include "/Plugin/ShaderLab/ShaderLab.ush"

SL_SETTINGS(Domain = Surface, BlendMode = Opaque)

SL_PROPERTY(Category = "Surface")
float3 BaseColor = float3(0.8, 0.2, 0.1);

SL_PROPERTY(Category = "Surface", ClampMin = 0, ClampMax = 1)
float Roughness = 0.5;

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    S.DiffuseAlbedo = BaseColor;
    S.Roughness = Roughness;
}

The file name is used as the ShaderLab material name. Create an instance from Material > ShaderLab Material Instance in the Content Browser, choose this material in the picker, then edit BaseColor and Roughness in the instance details panel.

File Types

.usl is a ShaderLab file that generates a material. You can think of it as a text-authored UMaterial. One .usl corresponds to one instantiable ShaderLab material.

.uslfunc is a function library file. You can think of it as a text-authored UMaterialFunction. It does not generate a material by itself. It can only be included by .usl or other .uslfunc files to share functions and parameters.

#include "/Project/Lib/MyLibrary.uslfunc"

Paths usually use virtual shader paths:

/Project/...
/Plugin/<PluginName>/...
/Engine/...

Common Settings

SL_SETTINGS is written at the top level and describes the material domain, blend mode, and common material settings.

SL_SETTINGS(Domain = Surface, BlendMode = Masked, TwoSided = true, OpacityMaskClipValue = 0.4)

Common Domain values:

  • Surface: regular surface material.
  • PostProcess: post process material.
  • UI: UI material.
  • Decal: decal material.
  • Volume: volume material.
  • LightFunction: light function material.

Common BlendMode values:

  • Opaque
  • Masked
  • Translucent
  • Additive
  • Modulate
  • AlphaComposite
  • AlphaHoldout

Parameter Declarations

There are two main kinds of input parameters: SL_PROPERTY declares editable parameters on the material instance, while SL_COLLECTION declares global inputs from a Material Parameter Collection.

SL_PROPERTY(Category = "Surface")
float3 Tint = float3(1, 1, 1);

SL_PROPERTY(Category = "Surface", ClampMin = 0, ClampMax = 1)
float Metallic = 0.0;

SL_PROPERTY(Category = "Textures", DefaultTexture = "white")
Texture2D AlbedoTex;
SamplerState AlbedoTexSampler;

SL_PROPERTY(Category = "Switches")
#define UseTexture 1

SL_COLLECTION(Path = "/Game/YourFolder/MPC_Global", Parameter = "Wind")
float3 Wind;

Common type mapping:

  • float: Scalar parameter.
  • float3: Color parameter.
  • float4: Vector parameter.
  • Texture2D, TextureCube: texture parameter.
  • #define Name 0/1: Static Bool parameter, usable in #if.
  • SL_COLLECTION(...) floatN Name;: Material Parameter Collection input, not an instance knob.

Common specifiers:

  • Category: category in the instance details panel.
  • SortPriority: sorting priority.
  • ClampMin, ClampMax: Scalar slider range.
  • DefaultTexture: default texture, such as white, black, grey, normal, or an asset path.
  • CustomPrimitiveData: reads a Scalar or Vector from component Custom Primitive Data.

Common SL_COLLECTION usage:

SL_COLLECTION(Path = "/Game/YourFolder/MPC_Global", Parameter = "Wind")
float3 Wind;

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    S.EmissiveColor = abs(Wind);
}

Static Bool parameters can directly control preprocessor branches:

SL_PROPERTY(Category = "Switches")
#define UseTexture 1

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
#if UseTexture
    S.DiffuseAlbedo = float3(1, 1, 1);
#else
    S.DiffuseAlbedo = float3(0.2, 0.2, 0.2);
#endif
}

When a Static Bool is overridden in a material instance, the corresponding permutation is recompiled.

Surface Materials

Most regular surface materials use SL_SURFACE. The function takes inout FShaderLabSurface; fill only the fields you care about, and unspecified fields keep their defaults.

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    float2 uv = UE_TextureCoordinate(0);
    S.DiffuseAlbedo = Texture2DSample(AlbedoTex, AlbedoTexSampler, uv).rgb * Tint;
    S.Roughness = Roughness;
    S.EmissiveColor = float3(0, 0, 0);
}

Common fields:

  • DiffuseAlbedo
  • F0
  • Roughness
  • Normal
  • EmissiveColor
  • Opacity
  • OpacityMask
  • Refraction
  • PixelDepthOffset

If a body needs transform helpers that depend on the material context, declare Parameters explicitly:

SL_SURFACE()
void Surface(FMaterialPixelParameters Parameters, inout FShaderLabSurface S)
{
    float3 n = UE_TransformTangentVectorToWorld(Parameters, float3(0, 0, 1));
    S.Normal = n;
}

Other Material Entries

In addition to SL_SURFACE, other entries can express different material types.

SL_UNLIT()
void Unlit(inout FShaderLabUnlit U)
{
    U.EmissiveColor = float3(1, 0.5, 0.1);
}

Common entries:

  • SL_UNLIT: unlit material.
  • SL_HAIR: hair material.
  • SL_EYE: eye material.
  • SL_WATER: Single Layer Water.
  • SL_CLEARCOAT: clear coat material.
  • SL_TOON: toon material.
  • SL_VOLUME: volume material, requires Domain = Volume.
  • SL_LIGHTFUNCTION: light function material, requires Domain = LightFunction.
  • SL_POSTPROCESS: post process material, requires Domain = PostProcess.
  • SL_UI: UI material, requires Domain = UI.

Layered Materials

Multiple SL_SLAB blocks can be combined into one Substrate material with SL_FRONTMATERIAL.

SL_PROPERTY(Category = "Layer", ClampMin = 0, ClampMax = 1)
float Mix = 0.5;

SL_SLAB()
void Base(inout FShaderLabSurface S)
{
    S.DiffuseAlbedo = float3(0.1, 0.1, 0.1);
    S.Roughness = 0.8;
}

SL_SLAB()
void Coat(inout FShaderLabSurface S)
{
    S.DiffuseAlbedo = float3(0.8, 0.2, 0.1);
    S.Roughness = 0.2;
}

SL_FRONTMATERIAL(HorizontalMix(Base, Coat, Mix))

Common composition operators:

  • VerticalLayer(Top, Base, Thickness): layer one material over another.
  • HorizontalMix(Background, Foreground, Mix): horizontal mix.
  • Add(A, B): add.
  • Weight(A, Weight): weight.
  • Select(A, B, Threshold): select.

A mix factor can be a float literal, a Scalar parameter, or an SL_VALUE.

SL_VALUE()
float EdgeMask()
{
    return saturate(UE_TextureCoordinate(0).x);
}

SL_FRONTMATERIAL(HorizontalMix(Base, Coat, EdgeMask))

Vertex And Interpolator

SL_VERTEX outputs vertex-stage data, such as world position offset.

SL_PROPERTY(Category = "Vertex")
float Offset = 10.0;

SL_VERTEX()
void Vertex(inout FShaderLabVertex V)
{
    V.WorldPositionOffset = float3(0, 0, Offset);
}

SL_INTERPOLATOR computes a value in the vertex stage and reads it in the pixel stage.

SL_INTERPOLATOR()
float3 VertexNormalColor()
{
    return abs(UE_VertexNormalWS());
}

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    S.DiffuseAlbedo = UE_Interpolator(VertexNormalColor);
}

Interpolators are useful for passing vertex-frequency results to the pixel stage. Do not put pixel-only sampling logic inside SL_INTERPOLATOR.

Function Libraries .uslfunc

.uslfunc files are used to share material functions. A library can declare SL_FUNCTION and its own SL_PROPERTY. When a .usl includes the library, the library parameters are promoted onto that material instance.

Library file:

#include "/Plugin/ShaderLab/ShaderLab.ush"

SL_PROPERTY(Category = "Detail", ClampMin = 0, ClampMax = 1)
float DetailStrength = 0.5;

SL_FUNCTION()
float3 ApplyDetail(float3 color, float2 uv)
{
    float stripes = frac(uv.x * 12.0);
    return lerp(color, color * stripes, DetailStrength);
}

Material file:

#include "/Plugin/ShaderLab/ShaderLab.ush"
#include "/Project/Lib/Detail.uslfunc"

SL_PROPERTY(Category = "Surface")
float3 BaseColor = float3(0.8, 0.8, 0.8);

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    S.DiffuseAlbedo = ApplyDetail(BaseColor, UE_TextureCoordinate(0));
}

If multiple libraries contain parameters with the same name, use SL_IMPORT to add a namespace prefix to promoted parameters.

SL_IMPORT(Namespace = "Detail")
#include "/Project/Lib/Detail.uslfunc"

Common UE_ Intrinsics

ShaderLab exposes common UE material nodes through the UE_ prefix. They can be called like regular functions inside a .usl body.

Common categories:

  • UV: UE_TextureCoordinate(0).
  • Time: UE_Time().
  • Geometry data: UE_VertexNormalWS(), UE_PixelNormalWS(), UE_CameraVectorWS().
  • Object and instance data: object position, object radius, actor position, Custom Primitive Data.
  • Coordinate transforms: UE_TransformVector..., UE_TransformPosition....
  • Noise and rotation: UE_Noise(...), UE_RotateAboutAxis(...).
  • Scene textures: UE_SceneTexture(...), UE_SceneDepth(...), UE_CustomDepth(...), UE_CustomStencil(...).
  • Distance fields: UE_DistanceToNearestSurface(...), UE_DistanceFieldGradient(...).

Example:

SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
    float t = UE_Time();
    float2 uv = UE_TextureCoordinate(0);
    float3 normalColor = abs(UE_VertexNormalWS());
    S.EmissiveColor = normalColor * (0.5 + 0.5 * sin(t + uv.x * 6.28318));
}

Material Instances And Usage Notes

ShaderLab materials should be used through ShaderLab Material Instance.

Usage is not declared in .usl. When assigning an instance to static meshes, skeletal meshes, particles, UI, or other targets, use the normal UE material instance rules to set or record the corresponding usage. Before packaging, make sure the usages you actually need have been determined in the editor.

.usl and .uslfunc files under project or plugin Shaders directories participate in packaging. Textures, MPCs, Profiles, and other assets referenced by material instances should still exist as normal UE content assets.

Implementation Idea

As a user, you can think of UShaderLab as three layers:

  • .usl is the material source. It stays close to HLSL, with SL_ macros for material settings, parameters, and entries.
  • In the editor, the plugin parses .usl and builds a UE material graph from parameters, Custom HLSL, Substrate BSDFs, and required helper nodes.
  • What you use in the scene is a ShaderLab Material Instance. The instance stores parameter overrides and owns the compiled result needed for rendering.

Static switches declared with #define are exposed as Static Bool parameters on the material instance. After an instance override, related #if branches are evaluated for that permutation, so different branches can be written as real compile-time branches.

The base material is mainly a template. In daily use, you only need to create and save ShaderLab Material Instances.