How to use bump/height textures in Unreal Engine 5?

In this tutorial, I’ll share two methods in Unreal Engine for creating fake depth. In other words, we’ll learn how to simulate geometry without sacrificing performance and prevent our textures from looking flat. If you’re not familiar with setting up textures in Unreal, I also wrote a tutorial on that topic here.

1. Some Additional Information on More Believable Bump Textures

Before we begin, let me share some additional performance tips regarding bump maps. In this tutorial, I won’t be using Quixel textures, but the methods we discuss today will work with textures downloaded from any source. The reason I mention Quixel is that, by default, the lowest resolution you can download from Megascan is 2K, which is excessive for a bump map. You can reduce the resolution significantly while still achieving great-looking bump maps to:

  • 256px*256px

In fact, using a low-resolution bump texture produces better results. This is due to limitations in the nodes that display bump maps in Unreal Engine 5, where low-resolution or blurry images can create smudged/etc. effects and reduce sharp transitions when viewed from different angles. If the last sentence doesn’t make sense, unfortunately, in Unreal Engine 5, bump textures lose their effectiveness from certain angles, but we’ll discuss that further shortly. So, if you wish, you can apply a very small amount of ‘Gaussian Blur’ to your texture in Photoshop or similar software.

Lazy Loaded Image

Your advertisement can be placed here for $200 permanently. Suggested resolutions: 1800x663 or 1400x1400. Contact us.

2. Preparing Our Bump Texture In Unreal Engine

After implementing ‘Bump Offset’ or ‘Parallax Occlusion Mapping’, you might notice that as you move your camera around, the mesh with the bump texture applied shifts its texture coordinates based on the camera’s view angle. To prevent or minimize this issue, when you import your bump texture into the engine, open your bump texture in UE5, and ensure that the ‘sRGB’ option is unchecked to prevent gamma correction:

Lazy Loaded Image

After doing so, you might encounter an error message within the ‘Material Graph’. To resolve this, you’ll need to change the ‘Sampler Type’ from ‘Color’ to ‘Linear Color’:

Lazy Loaded Image

3. How does a bump map work? What techniques are available to implement a bump map in UE5, and what are the differences between these methods?

Bump textures are grayscale images that contain black, white, and grey areas. These colors represent:

  • Black (Low areas)
  • White (Elevated areas)
  • Gray (Midpoints)

One More Tip About Quixel Textures

I’ve noticed that many of the bump textures provided by Quixel are a bit too gray for my liking. If you are aiming for archviz, you probably should not play with these values, but if you are a gamedev and want a little bit more punch in your texture, I recommend opening your image in Photoshop or similar software to adjust the white areas to be just a bit whiter and the black areas to be just a bit blacker. This will help you better visualize the results of the bump map on our mesh.

4. Now The Fun Part!

Lazy Loaded Image

Left to Right: Regular Texture Setup > Bumpoffset Method > Parallax Occlusion Mapping Method

Which one looks like it has more depth to you?

Important Note: One thing you need to be aware of with bump maps is that the way they are handled in Unreal Engine can cause this effect to break apart from certain camera angles due to a limitation of Unreal Engine. This is why using blurry and low-resolution bump textures can help, as the transition from one viewing angle to another won’t appear as sharp. Sharp transitions occur when the contrast between the white and black values in the texture is too pronounced.

4.1 Using Bump Offset Node

  • Advantages:
  • Cheapest method to create ‘fake depth’, as we are only sampling our bump texture once.
  • Needs very few instruction counts, making it suitable for implementation with almost any asset.
  • Disadvantages:
  • Details can be lost easily from certain angles.
  • The closer the camera is to the mesh, the easier it is to tell whether the geometry is an actual displacement or fake.

4.1.2 Bump Offset Node Setup

Lazy Loaded Image

Bump Offset Node Setup

Lazy Loaded Image

Bump Offset Node Options

  • Coordinate: Connect a ‘TextureCoord’ node to this input to adjust the UVs for albedo, roughness, etc.
  • Height: Connect the bump map (Note: Use the Red channel.) to this pin.
  • HeightRatioInput: This setting determines the strength of the bump offset. By default, it is set to 0.05, which is often considered too strong. It’s suggested to use lower values, such as 0.005 or 0.01 or 0.1, for the best results, but the suggested value is not set in stone.
  • (VERY IMPORTANT) Reference Plane: (This option is revealed when the node is selected.) The reference plane value determines what the shader uses as its zero point. For example, with the plane measurement value set at 0.5, a value of 1 (white value in bump textures) is pushed forward. By 0.5, and anything below 0.5 along the grayscale will be pushed inwards. If you think you don’t get enough fake depth, instead of increasing the ‘HeightRatioInput’ value, try adjusting the values (Minimum: 0, Maximum: 1) here first.

4.2 Using Parallax Occlusion Mapping Node

  • Advantages:
  • Details are not easily lost from certain camera angles compared to the ‘Bumpoffset’ method.
  • Generates very believable fake depth. The fake depth illusion is not as noticeable as it is with the ‘Bumpoffset’ method.
  • Disadvantages:
  • Remarkably more taxing.
  • Not a good idea to use in many materials when low-end systems are targeted.

4.2.1 Parallax Occlusion Mapping Setup

Lazy Loaded Image

The ‘Pixel Depth Offset’ feature is optional, you don’t need to plug that in for POM to work.

(Optional Read) What does ‘Pixel Depth Offset’ do then?

The Pixel Depth Offset feature moves pixels up and down in the depth buffer. This means that this feature can be used to adjust the fake depth length or to modify the calculated depth value of a pixel, allowing for more control over how two or more objects are composited together. This can be particularly useful in scenarios where we want to prevent z-fighting — a visual artifact where two objects at different depths appear to flicker or fight for visibility. In some cases, Pixel Depth Offset can be used to reduce unnecessary overdraw in a scene, optimizing rendering performance by ensuring that only the visible portions of objects are rendered.

One thing to pay attention to when using this feature is that higher values can create unwanted shadows, as some pixels are moved further away from the mesh itself. To prevent this from happening, you can click on the asset, go to the details panel, search for ‘Cast Shadow’, and set it to false.”

Lazy Loaded Image

‘Parallax Occlusion Mapping’ Node Options

  • Heightmap Texture (T2d): Only accepts texture objects. (You can right-click on your bump texture and select ‘Convert to Texture Object’). Texture objects do not sample anything; they are simply texture pickers. In contrast, the ‘Texture Sampler’ node combines texture picking and sampling in one package. The main reason why the ‘Parallax Occlusion Mapping’ node is very demanding is that it first needs to sample the texture from the provided ‘Texture Object’ and then starts shooting ray traces to calculate where the light is hitting the object, in order to apply the best fake depth across the surface of the mesh. This calculation requires multiple samplings of the same texture to achieve the most accurate result, hence it is taxing.
  • Height Ratio (S): Determines the strength.
  • Min Steps/Max Steps (S): This setting allows you to control how many raycasts the POM node can shoot. If you can achieve satisfactory results with fewer raytraces, it’s better since using fewer steps improves performance. If the parallax occlusion depth breaks apart easily, you can always increase the values here.
  • UVs: Connect a ‘TextureCoord’ node to this input to adjust the UVs for albedo, roughness, etc.
  • Heightmap Channel (V4): Accepts a ‘Constant4Vector’ expression. We use this expression to tell to ‘Parallax Occlusion Mapping’ node that we want to use the red channel of our bump texture. A ‘Constant4Vector’ can be quickly created by pressing ‘4’ on the keyboard and the left mouse button simultaneously.
  • (VERY IMPORTANT) Reference Plane: The reference plane value determines what the shader uses as its zero point. For example, with the plane measurement value set at 0.5, a value of 1 (white value in bump textures) is pushed forward. By 0.5, and anything below 0.5 along the grayscale will be pushed inwards. If you think you don’t get enough fake depth, instead of increasing the ‘Height Ratio (S)’ value, try adjusting the values (Minimum: 0, Maximum: 1) here first.

Final Words

You can use both of these methods mentioned here simultaneously. For example; for less important assets, you can utilize ‘Bump Offset’ to conserve performance, and ‘Parallax Occlusion Mapping’ for assets that players will see from very close distances. You can implement a function that identifies the hardware where your game is being run and sets materials to use bump or parallax based on that information.

And as the last thing, if you enjoyed this tutorial and would like to see more written tutorials from me, you can donate as little as $10 to me on Patreon. This support will enable me to allocate more time to creating these resources.