Keywords: UE4, Shader Development, Material Optimization, Shader Debugging

Common Case

Environment Setup

1, Set LoadingPhase as PostConfigInit, default is Default.
e.g. TestProj.uproject:

{
    "FileVersion": 3,
    "EngineAssociation": "4.25",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "TestProj",
            "Type": "Runtime",
            "LoadingPhase": "PostConfigInit"
        }
    ]
}

Otherwise there’s error on building:

Shader type was loaded after engine init, use ELoadingPhase::PostConfigInit on your module to cause it to load earlier.

2, Turn on r.ShaderDevelopmentMode in \Engine\Config\ConsoleVariables.ini.
ConsoleVariables.ini:

[Startup]
; Uncomment to get detailed logs on shader compiles and the opportunity to retry on errors
r.ShaderDevelopmentMode=1

Otherwise there’s error on building:

Failed to compile global shader XXXX. Enable `r.ShaderDevelopmentMode` in ConsoleVariables.ini for retries.

3, Add RenderCore, Renderer in your .Build.cs. Maybe RHI is also need to be added.

PublicDependencyModuleNames.AddRange(new string[] { "RenderCore", "Renderer", "RHI" });

4, Now you can setup your shader source in your game source, and these shader would be compiled on editor starting.
MyActor.cpp (quoted from Engine\Source\Runtime\Renderer\Private\PostProcess\PostProcessVisualizeBuffer.cpp):

namespace
{
    class FVisualizeBufferPS : public FGlobalShader
    {
    public:
        DECLARE_GLOBAL_SHADER(FVisualizeBufferPS);
        SHADER_USE_PARAMETER_STRUCT(FVisualizeBufferPS, FGlobalShader);

        BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
            SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output)
            SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture)
            SHADER_PARAMETER_SAMPLER(SamplerState, InputSampler)
            SHADER_PARAMETER(FLinearColor, SelectionColor)
            RENDER_TARGET_BINDING_SLOTS()
        END_SHADER_PARAMETER_STRUCT()

        static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
        {
            return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::ES3_1);
        }
    };

    IMPLEMENT_GLOBAL_SHADER(FVisualizeBufferPS, "/Engine/Private/PostProcessVisualizeBuffer.usf", "MainPS", SF_Pixel);

    FScreenPassTexture AddVisualizeBufferPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FVisualizeBufferInputs& Inputs)
    {
        ...
        FVisualizeBufferPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVisualizeBufferPS::FParameters>();
        PassParameters->Output = GetScreenPassTextureViewportParameters(OutputViewport);
        PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
        PassParameters->InputTexture = Tile.Input.Texture;
        PassParameters->InputSampler = BilinearClampSampler;
        PassParameters->SelectionColor = SelectionColor;
        
        TShaderMapRef<FVisualizeBufferPS> PixelShader(View.ShaderMap);
        ...
    }
    
    void Test_RenderThread(FRHICommandListImmediate& RHICmdList)
    {
        ...
        TShaderMapRef<FDownsamplePS> PixelShader(ShaderMap, PermutationVector);
        ...
    }
}

AMyActor::TestFun()
{
    ENQUEUE_RENDER_COMMAND(FDownsampleTestCommand)(
    [](FRHICommandListImmediate& RHICmdList)
    {
        Test_RenderThread(RHICmdList);
    });
}

5, If want to specify the Shader Type (Global, Material, MeshMaterial, Niagara etc.), use IMPLEMENT_SHADER_TYPE instead of IMPLEMENT_GLOBAL_SHADER, see the example of PostProcessMaterial.cpp in engine.
quoted from Engine\Source\Runtime\Renderer\Private\PostProcess\PostProcessMaterial.cpp:

IMPLEMENT_SHADER_TYPE(,FPostProcessMaterialVS, TEXT("/Engine/Private/PostProcessMaterialShaders.usf"), TEXT("MainVS"), SF_Vertex);
IMPLEMENT_SHADER_TYPE(,FPostProcessMaterialPS, TEXT("/Engine/Private/PostProcessMaterialShaders.usf"), TEXT("MainPS"), SF_Pixel);
How to dump shader statistics of all materials

UE5 solution:

"D:/UE_5.2/Engine/Binaries/Win64/UnrealEditor-Cmd.exe" "D:/TestProj/TestProj.uproject" -run=DumpMaterialShaderTypes  -targetplatform=Windows

Then results output to [MyProj]/Saved/MaterialStats/.txt.

DumpMaterialShaderTypes only works for UE5.

UE4 solution:
Turn on follows switch in Engine\Config\ConsoleVariables.ini and restart editor (if want to build package from editor):

r.DumpShaderDebugInfo=1
r.DumpShaderDebugShortNames=1

Then execute cook command:

D:/UE4/Engine/Binaries/Win64/UE4Editor-Cmd.exe D:/TestTP/TestTP.uproject -run=Cook -TargetPlatform=WindowsNoEditor -fileopenlog -ddc=DerivedDataBackendGraph -unversioned -abslog=D:/UE4/Engine/Programs/AutomationTool/Saved/Cook-2023.06.09-17.05.16.txt -stdout -CrashForUAT -unattended -NoLogTimes -UTF8Output

This cook command also will be executed automatically while building Development package.

Then statistics data output to directory: [MyProj]/Saved/MaterialStats/*.csv.
UE4’s cook command only output sum of shader count, UE5’s command output shader count and shader type.

How to recompile shaders after .usf was modifed

Shaders will not take effect directly when changed code in .usf source file, need to run console command to fire shader recompilation manually.
The prerequisite is to turn on r.ShaderDevelopmentMode=1 in ConsoleVariables.ini.

Console command:

RecompileShaders changed
Shader Compilation

Incredibuild may struggle with remote connections and builds may end up slower with it enabled.
You can disable Incredibuild in Engine/Config/ConsoleVariables.ini:

; r.XGEShaderCompile is now enabled by default in source. Uncomment to disable XGE shader compilation.
r.XGEShaderCompile = 0

Origin: https://forums.unrealengine.com/t/work-from-home-disable-xge-incredibuild/141160

Shader Debugging (UE5)

Edit Engine/Config/ConsoleVariables.ini and uncomment these lines:

; Uncomment to get detailed logs on shader compiles and the opportunity to retry on errors
r.ShaderDevelopmentMode=1

; Uncomment when running with a graphical debugger (but not when profiling)
r.Shaders.Optimize=0
; When this is enabled, shaders will have extra debugging info. This could change patch sizes, uniqueness, etc and will recompile the shaders
r.Shaders.Symbols=1
; When this is enabled, ShaderName field of FRHIShader will be populated (Development and Debug builds only)
r.Shaders.ExtraData=1
; Uncomment to skip shader compression. Can save a significant time when using debug shaders.
r.Shaders.SkipCompression=1

For packaged builds, disable IoStore (as it doesn’t support shader symbol files) and EOS overlay (conflicts with RenderDoc’s DirectX hooks).

For more details, see: Shader Debugging in PIX

Issue(UE5): Virtual shader source file name should be absolute from the virtual root directory

Error on building:

UATHelper: Packaging (Windows): LogShaders: Error: Virtual shader source file name "D:\Engine\Private\BasePassPixelShader.usf.intermediate.hlsl" should be absolute from the virtual root directory "/".
UATHelper: Packaging (Windows): LogShaders: Error: Backslashes are not permitted in virtual shader source file name "D:\Engine\Private\BasePassPixelShader.usf.intermediate.hlsl"
UATHelper: Packaging (Windows): LogShaders: Error: Extension on virtual shader source file name "D:\Engine\Private\BasePassPixelShader.usf.intermediate.hlsl" is wrong. Only .usf or .ush allowed.

Solution:
Uncheck SM5 (DirectX 11) in Project Settings, using SM6 (DirectX 12) instead.

References

Official Documents

Shader Development
Adding Global Shaders to Unreal Engine
Shaders In Plugins

Render Dependency Graph. An immediate-mode API which records render commands into a graph data structure to be compiled and executed.
https://docs.unrealengine.com/5.3/en-US/render-dependency-graph-in-unreal-engine/

How to Create a Custom Ray Tracing Shader as a Plugin

Creating a New Global Shader as a Plugin

Blogs

Extend the UE4 Shading Model
https://qiita.com/dgtanaka/items/41f96ef2090820035609

Unreal Engine 4 Rendering Part 1: Introduction
Unreal Engine 4 Rendering Part 2: Shaders and Vertex Data
Unreal Engine 4 Rendering Part 3: Drawing Policies
Unreal Engine 4 Rendering Part 4: The Deferred Shading Pipeline
Unreal Engine 4 Rendering Part 5: Shader Permutations
Unreal Engine 4 Rendering Part 6: Adding a new Shading Model

剖析虚幻渲染体系(08)- Shader体系
https://www.cnblogs.com/timlly/p/15092257.html

Add custom render pass in UE5 (Recommended)
https://tianc377.github.io/shaderposts/2023/AddCustomRenderPassInUE5.html

Material Optimization

Material Optimization
https://forums.unrealengine.com/t/material-optimization/59111

How the Unreal Engine Translates a Material Graph to HLSL
https://forums.unrealengine.com/docs?topic=498490

Shader Permutation

Understanding Shader Permutations
https://forums.unrealengine.com/t/understanding-shader-permutations/264928

The Shader Permutation Problem - Part 1: How Did We Get Here?
https://therealmjp.github.io/posts/shader-permutations-part1/
The Shader Permutation Problem - Part 2: How Do We Fix It?
https://therealmjp.github.io/posts/shader-permutations-part2/

Vertex Shader

Rotating meshes using Vertex Shaders
https://www.tomlooman.com/unreal-engine-material-vertex-shaders/

The “Normal”-pin runs on the pixel-shader while the “World Position Offset”-pin runs on the vertex shader.

Customized UVs: Feature that allows running calculations in the vertex shader to increase performance over running them per-pixel.

https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/CustomizedUVs/

Compiling

Compiling Shaders Manually
http://mpolaczyk.pl/compiling-shaders-manually/

Examples

Unreal Official Shaders
https://github.com/EpicGames/UnrealEngine/tree/release/Engine/Shaders/Private

A tutorial project that shows how to implement HLSL Pixel and Compute shaders in UE4
https://github.com/Temaran/UE4ShaderPluginDemo

A compute shader plugin that is capable of sorting positional data in parallel directly on the GPU.
https://github.com/ValentinKraft/UE4_SortingComputeShader

The minimal source code for adding and using a custom compute shader in Unreal Engine 4
https://github.com/AyoubKhammassi/CustomComputeShaders


Business has only two functions — marketing and innovation. ― Peter Drucker