UE5 HLSL Code in Visual Studio Instead of the Custom Node

Table of Contents

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.

  1. Create Shaders/MyFunctions.ush in your project and write your functions there.
  2. Map the folder to a virtual path such as /MyShaders, once, with AddShaderSourceDirectoryMapping in a C++ module that loads at PostConfigInit.
  3. In the material’s Custom node, add /MyShaders/MyFunctions.ush to Include File Paths.
  4. 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

  1. In the Material Editor, add a Custom node.
  2. In Details, expand Include File Paths, click + and enter /MyShaders/MyFunctions.ush.
  3. Add Inputs named Color, Tint and Amount, and set Output Type to CMOT Float 3.
  4. 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

ProblemFix
“Can’t map virtual shader source path” or an include errorCheck the virtual path in Include File Paths matches the one you mapped, including the file name.
Editor crashes at startup after adding the mappingThe path is already mapped or the folder doesn’t exist. Keep the Contains check and create the Shaders folder first.
Function not foundThe Custom node input names must match what you pass in, and the function must be in the included file.
Changes don’t show upRun 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.

Related tutorials