How the Unreal Engine Translates a Material Graph to HLSL
- After Apply in Material Editor: (1) translate material graph to HLSL; (2) compile HLSL to GPU bytecode. Article focuses on (1). Shader permutations: not one shader per material — several depending on features/platforms/usages; see Understanding Shader Permutations (linked from article; separate fetch redirected to docs home).
- View generated shader: Window → Shader Code → HLSL Code (read-only). Copy to Notepad++/Rider/VS with HLSL highlighting.
- Template: Engine/Shaders/Private/MaterialTemplate.ush — structures/functions with "%s" placeholders filled by text replacement. Called from pixel shader BasePassPixelShader.usf.
- Filled by FMaterialCompiler; standard path uses FHLSLMaterialTranslator (inherits FMaterialCompiler). Also Lightmass / Proxy implementations.
Call path
FMaterial::CacheShaders
-
FMaterial::BeginCompileShaderMap
- FHLSLMaterialTranslator::Translate
- FHLSLMaterialTranslator::GetMaterialEnvironment
- FHLSLMaterialTranslator::GetMaterialShaderCode
- FPartialShaderMap::Compile
-
Translate: prepare each material input pin's graph; ensure vertex interpolators, texture coordinates, material parameter collections, virtual texture samples, etc.; error reporting (e.g. per-pixel shading models with SingleLayerWater).
-
GetMaterialEnvironment: sets defines used in final HLSL (#ifdefs for shading models / features).
-
GetMaterialShaderCode: prints compiled data into template string.
UMaterialExpression::Compile
- Nodes inherit UMaterialExpression; key methods Compile, GetCaption, GetKeywords. Declarations in headers; definitions in Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp. Each can define constants and FExpressionInput graph inputs.
- Compile returns int32 = array index of compiled result. Takes FMaterialCompiler*.
- Example UMaterialExpressionAdd::Compile: if pin connected compile input else Compiler->Constant(ConstA/ConstB); then Compiler->Add(...).
Constant Folding (FHLSLMaterialTranslator::Add)
- Both constant → precompute on CPU, write literal result.
- One constant + parameter → hidden parameter combining them on CPU so GPU sees a fixed number.
- Both non-constant → emit "A + B".
- Example graph outcomes in CalcPixelMaterialInputs:
- Roughness: PixelMaterialInputs.Roughness = (1.00000000 + 2.00000000);
- Metallic: Material.ScalarExpressions[0].z (packed float4 uniform expressions)
- Base Color: MaterialFloat Local1 = (Parameters.TexCoords[0].xy.r + Material.ScalarExpressions[0].y); then PixelMaterialInputs.BaseColor = Local1;
- Non-constant results stored as LocalN variables; hashed so duplicate subgraphs reuse LocalN.
EmissiveColor editor quirk
- Editor-compiled materials lerp user emissive with a VectorExpression controlled by engine value for selection highlighting; optimized away in cooked shaders.
References cited in article
- Tim Jones Shader Playground; Rider HLSL; Notepad++ HLSL UDL; UE4 Rendering Part 4 Deferred; HLSL Tools for VS (add .usf/.ush extensions).