Material graph node

Making a custom Advanced Output material expression accessible from Compute Shaders in Unreal Engine 5

That title is a mouthful, but in short, this recipe provides ways to:


1. Setup

1.1 Scaffold a material compute shader:

$ shadeup-unreal
 > [COMPUTE] Compute Shader
 > Base with material
OR

1.2 Scaffold an output-only material node:

$ shadeup-unreal
 > [MATERIAL] Custom Material Nodes
 > Base Final Output
OR

If you don’t have the shadeup command installed follow this to get it.


2. Reduce permutations

First we’ll need to change a few parts of our compute shader, we can start by reducing the number of permutations that are compiled with:

AdvancedOutputComputeShader.cpp:70
const bool bIsCompatible =
	Parameters.MaterialParameters.MaterialDomain == MD_Surface
	&& Parameters.MaterialParameters.BlendMode == BLEND_Opaque
	&& Parameters.MaterialParameters.ShadingModels == MSM_DefaultLit
	&& Parameters.MaterialParameters.bIsUsedWithLidarPointCloud; // Add this line here

By adding a check for bIsUsedWithLidarPointCloud we can greatly reduce the number of materials that need to be compiled against our compute shader. Of course, this means that any shaders you do want to use will need to have the UseWithLidarPointCloud check box ticked off in the material editor details panel.

Unfortunately, this is a hack and makes this a bit unintuitive because you now need to remember to check that box off. (We’ll fix this next)

You can find a list of all the available bIsUsedWithXYZ flags here


3. Permutation error message

We should add an error message when the user attempts to call a material that doesn’t have the bIsUsedWithLidarPointCloud.

Add this block right after line 130:

AdvancedOutputComputeShader.cpp:130
...
bool bIsShaderValid = ComputeShader.IsValid();

// START Add this block
if (MaterialRenderProxy && !MaterialRenderProxy->GetMaterialInterface()->CheckMaterialUsage_Concurrent(MATUSAGE_LidarPointCloud)) {
	#if WITH_EDITOR
	GEngine->AddOnScreenDebugMessage((uint64) 5643264352356, 6.f, FColor::Red, FString(TEXT("Can't use the specified material because it has not been compiled with bUsedWithLidarPointCloud.")));
	#endif

	bIsShaderValid = false;
}
// END Add this block

if (bIsShaderValid) {
...

4. Get values in the shader

Next we’ll add a call to GetShadeupAdvancedMaterialOutput to the shader side.

Replace the line:

AdvancedOutputComputeShader.usf:30
OutputColor[0] = float4(GetMaterialBaseColor(PixelMaterialInputs), 0);

with:

AdvancedOutputComputeShader.usf:30
float num1 = 0;
float num2 = 0;

#ifdef NUM_MATERIAL_OUTPUTS_GETSHADEUPADVANCEDMATERIALOUTPUT
	#if NUM_MATERIAL_OUTPUTS_GETSHADEUPADVANCEDMATERIALOUTPUT > 0
		num1 = GetShadeupAdvancedMaterialOutput0(MaterialParameters);
		num2 = GetShadeupAdvancedMaterialOutput1(MaterialParameters);
	#endif
#endif

OutputColor[0] = float4(num1, num2, 0, 0);

This will make sure the output node exists and that we don’t have a compilation error when it doesn’t.


5. Usage

Now we can build and launch the editor to begin playing with our new custom node!

5.1 Create a material

Add the Shadeup Advanced Output Node and hook up some inputs:

bpue/54-fvbg1
UE5 Material graph

5.2 Enable the permutation

Tick off the Used with Lidar Point Cloud checkbox in the material details panel

UE5 Material details panel

5.3 Call from BP

Open up a level blueprint or create a new actor BP and insert the following nodes:

bpue/zlvmtoyq
UE5 blueprint editor

6. All done!

You should now have values coming from a material graph, into a compute shader, over to BP, and finally landing on your screen.

Video of results