Keywords: UE4, Shader Development, Material Optimization

Environment Setup

Steps
  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.
    
  1. 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.
    
  1. Add RenderCore, Renderer in your .Build.cs. Maybe RHI is also need to be added.

     PublicDependencyModuleNames.AddRange(new string[] { "RenderCore", "Renderer", "RHI" });
    
  2. 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);
         });
     }
    
  3. 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);
    

References

Official Documents

Shader Development

Adding Global Shaders to Unreal Engine

Shaders In Plugins

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

Material Optimization

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

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

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