15 Jul 2021 18:07

Unreal Engine 4 Shader Exploration

Shader on-off example
Material Shader UV-flip Blueprint

These are some first steps in experimenting with using a UE4 material shader, and the 'custom node' to insert HLSL shader code.

The material Blueprint is configured to act as a post-process, and in the test is connected in the scene to a 'post-processing' volume. Moving in and out of the volume enables and disables the shader.

The HLSL code is entered in the 'code' section of the material's custom node, which flips the y value of the incoming UV values depending upon its value:

	float2 uvAdjusted = UV;
	if (UV.y < 0.5) uvAdjusted.y = (1.0-UV.y);
	return uvAdjusted;
Shader material post-process setting
Shader material custom node UV-flip HLSL
Shader output Texture2DSample
Blueprint using HLSL Texture2DSample of external texture

Now, moving to using the HLSL to sample the alpha channel of an external texture.

This uses Texture2DSample, and where the source texture is transparent, the pixels are not Y-flipped:

	float2 uvAdjusted = UV;
	float4 TexColour = Texture2DSample(Mask, MaskSampler, UV);
	if (TexColour.a < 0.5) return UV;

	uvAdjusted.y = (1.0-UV.y);
	return uvAdjusted;

Direct SceneTexture Access

This time, instead of altering the UV data going into the PostProcess SceneTexture, this code accesses the data internally. It then also outputs the result directly. (Note: The custom node's connection to the SceneTexture is only needed in order to provide access to its functions)

Scene Texture direct access and Material Parameters

MyTestOffset Custom Node

The primary HLSL node now has 3 inputs:

  • SceneTexPPI0 - connection to a SceneTexture:PostProcess0 node
  • Enabled - connection to the 'EnableShader' Material Property
  • EnableGrid - connection to the 'EnableGrid' Material Property

The two 'Material Property' connections respond to the values contained in a 'Material Parameter Collection' object added to the Content Browser. This in turn has its values set by the inputs in the main Level Blueprint's Event Graph, allowing the shader and overlay grid to be toggled on and off.

Main Custom Node code:

	if (!Enabled)
		return SceneTexPPI0;   

	// Obtain the pixel's UV directly from the PostProcess0 scene texture (index 14)
	float2 uvDirect = GetDefaultSceneTextureUV(Parameters, 14);

	// Get the pixel colours from the framebuffer at the original UV pos
	float4 TexColourDirect = SceneTextureLookup(uvDirect, 14, false);

	// Grid overlay
	if (EnableGrid)
		bool bGridX = (fmod(uvDirect.x, 0.1) < 0.0015);
		bool bGridY = (fmod(uvDirect.y, 0.1) < 0.0015);
		if (bGridX || bGridY)
			return 1-TexColourDirect;

	// Check the external mask alpha
	float4 MaskColour = Texture2DSample(Mask, MaskSampler, uvDirect);
	if (MaskColour.a < 0.1)
		return TexColourDirect;

	// Add an x-offset (wrapped) to the incoming UV co-ord, and get the colour at that location
	float2 newUV = uvDirect + float2(0.1, 0.0);
	newUV = frac(newUV);
	float4 NewColour = SceneTextureLookup(newUV, 14, false);

	// Return the offset colour
	return NewColour;

Material Parameter Collection

This object is added in the content browser (appearing as the icon shown) , and provides a method of accessing variables within a material. Clicking on it allows you to edit its details, and add new variables that can then be used as required.

In this instance, the parameters are controlled using keypresses via the main 'Level Blueprint'.

Material Parameter Collection object details and Content Browser icon
Level Blueprint - controlling Material Parameter values
Material output compare node

Switching and Comparing Material Outputs

An additional HLSL custom node has been added between the main node's output, and the Material's final Emissive input.

The node takes 3 inputs:

  • uvRAW - the unmodified colour output
  • uvMod - the modified colour output
  • EnableColourDiff - connection to the 'EnableColourDiff' Material Property

Pressing the 'C' key causes the node to toggle its comparison of the two colour outputs. When enabled, pixels with the same colour are dimmed out by 50%, differences are colour coded red (original) and green (changed), and then shown as a blend of the two.

Comparison Custom Node code:

	if (!EnableColourDiff)
		return uvMod;

	// If the original and modified pixel are the same, then dim it out
	if (all(uvRaw == uvMod))
		return uvRaw * float4(0.5, 0.5, 0.5, 1.0);

	// Mask the colour: Orig=Red, New=Green
	float4 TexColourRaw = uvRaw * float4(1.0, 0.5, 0.5, 1.0); // Dim down all except red channel
	float4 TexColourMod = uvMod * float4(0.5, 1.0, 0.5, 1.0); // Dim down all except green channel

	// Return a blend of the masked-original and the modified pixel
	return lerp(TexColourRaw, TexColourMod, 0.5);

The final outputs here show the shader with the grid enabled, as well as the output from the separate node which compares the raw and modified outputs.

SceneTexture direct access and comparison nodes