Works in Unreal Engine 5.0 to 5.8. Updated October 2026.
Quick answer: put your HLSL functions in a .ush file, give its folder a virtual shader path, and include it from the Custom node.
- Create
Shaders/MyFunctions.ushin your project and write your functions there. - Map the folder to a virtual path such as
/MyShaders, once, withAddShaderSourceDirectoryMappingin a C++ module that loads at PostConfigInit. - In the material’s Custom node, add
/MyShaders/MyFunctions.ushto Include File Paths. - In the node’s Code, call your function:
return TintColor(Color, Tint, Amount);
Why move code out of the Custom node
The Custom node’s code box has no syntax highlighting and no error squiggles, and you can’t share a function between materials without copying it. A .ush file gives you Visual Studio’s editor, readable version-control diffs, and one copy of each function that every material can include.
Step 1: Write the functions in a .ush file
Create a Shaders folder next to your .uproject and add MyFunctions.ush:
#pragma once
float3 TintColor(float3 Color, float3 Tint, float Amount)
{
return lerp(Color, Color * Tint, Amount);
}
Open it in Visual Studio. The free HLSL Tools for Visual Studio extension adds highlighting and error squiggles for .ush files.
Step 2: Give the folder a virtual path
Materials can only include files through a virtual shader path, so the folder has to be registered once at startup. The mapping must happen early, so do it in a small module with "LoadingPhase": "PostConfigInit" in your .uproject (or .uplugin), and add RenderCore to that module’s Build.cs dependencies.
#include "Modules/ModuleManager.h"
#include "Misc/Paths.h"
#include "ShaderCore.h"
class FMyShadersModule : public IModuleInterface
{
public:
virtual void StartupModule() override
{
FString ShaderDirectory = FPaths::Combine(FPaths::ProjectDir(), TEXT("Shaders"));
if (!AllShaderSourceDirectoryMappings().Contains(TEXT("/MyShaders")))
{
AddShaderSourceDirectoryMapping(TEXT("/MyShaders"), ShaderDirectory);
}
}
};
IMPLEMENT_MODULE(FMyShadersModule, MyShaders)
The virtual path must start with /, must not end with /, and must be unique. A made-up name like /MyShaders avoids clashing with the engine’s own paths.
Working in a plugin? Epic’s docs let you include files from an enabled plugin’s Shaders folder as /Plugin/<PluginName>/<File>.ush, so you can skip the mapping code there.
Step 3: Include the file in the Custom node
- In the Material Editor, add a Custom node.
- In Details, expand Include File Paths, click + and enter
/MyShaders/MyFunctions.ush. - Add Inputs named
Color,TintandAmount, and set Output Type to CMOT Float 3. - Set Code to a single line:
return TintColor(Color, Tint, Amount);
Click Apply. The node now runs the function from your file.
Step 4: Reload after editing the file
Saving the .ush in Visual Studio doesn’t recompile materials by itself. After an edit, run recompileshaders changed in the editor console (Ctrl + Shift + . does the same), or make any change in the material and click Apply.
Common problems
| Problem | Fix |
|---|---|
| “Can’t map virtual shader source path” or an include error | Check the virtual path in Include File Paths matches the one you mapped, including the file name. |
| Editor crashes at startup after adding the mapping | The path is already mapped or the folder doesn’t exist. Keep the Contains check and create the Shaders folder first. |
| Function not found | The Custom node input names must match what you pass in, and the function must be in the included file. |
| Changes don’t show up | Run recompileshaders changed or re-apply the material. |
FAQ
.usf or .ush?
Use .ush for files you include. .usf is for shader entry points, which you don’t need for material functions.
Does this work in a packaged game?
Yes. The code is compiled into the material’s shaders when you cook. The mapping is only needed in the editor and during cooking.