In the previous tutorial, we discussed the optimization of our game by packing textures. Now, we will dive into how we can unpack these textures and use them in Unreal Engine 5.
Launch your project, import your textures into the engine, and create a new material. The very first thing we’re going to do is change our compression method and some other values of our normal texture so that Unreal can work with this texture as we want it to.
Double-click on your normal texture to open it in the editor, then change the compression method from ‘Default’ to ‘BC7 (DX11, optional A)’.
Compression method should be set to: BC7 (DX11, optional A)
Scroll down to locate the ‘Texture’ category and uncheck the box next to the ‘sRGB’ value to set it to false. Remember to click on the save button before closing this editor.
After performing these actions, you will notice that Unreal displays an error sign on our normal map texture:
This is because we changed the sampler type of our texture from ‘Normal’ to ‘Linear Color’. The fix is very simple. Now, all we need to do is tell Unreal that this map is not a color or normal map, but a linear color map by changing the sampler type:
Set the sampler type to ‘Linear Color’.
(The reason why we are not using the sampler type ‘Normal’ is because we manipulated this texture in Photoshop to store additional data inside of it. Therefore, we need to set its space to linear to be able to access this additional data.)
Now, we can begin unpacking our textures.
Let’s start with the easiest one. We already know that our albedo values are stored in the RGB channels, and the alpha channel contains the roughness information.
Unpacking albedo and roughness
Unreal Engine’s master shader’s normal input pin requires all of the X, Y, and Z values of the normal map to process the normal data properly. However, since we packed our ambient occlusion (AO) and cavity maps into the blue channel of our normal map, we cannot access to the Z values, but only the X and Y coordinates (red and green channels). Fortunately, Unreal Engine comes with a built-in node specifically for this purpose, which calculates Z values from X and Y. This node is called the ‘DeriveNormalZ_Function’.
Accessing the normal values
This setup here separates the red and green channels (where we store our normal map values) from the blue channel (where we store our cavity and ambient occlusion textures) and generates the Z value required for our normal map.
Alternatively, we can use the following setup as well to access our normal values, which is cheaper and less resource-intensive than using ‘DeriveNormalZ_Function’.
Now, we can concentrate on working on our cavity and ambient occlusion (AO) maps.
The Final Setup
Let’s explain the setup above.
The Max node (Please pay attention to the values, they are not random.) eliminates the black values and brings the white values forward, thus returning our ambient occlusion (AO) map.
To get our specular from our cavity map, we are using the ‘CheapContrast’ node to increase the contrast of the map. This brings us our specular. If you need to use your displacement, remember that we packed it into the normal texture’s alpha channel.
Some additional questions you might have:
Your advertisement can be placed here for $200 permanently. Suggested resolutions: 1800x663 or 1400x1400. Contact us.
I am using Quixel Megascans’ textures most of the time in my Unreal projects, and Megascans’ specular and cavity maps are essentially the same, except for one difference: Quixel multiplies cavity maps by 0.5.
So, what we do in our setup is take the cavity texture and convert it into a specular map in Unreal’s ‘Material Graph’ editor. In other words, a cavity map multiplied by 0.5 gives us a specular map. (For most non-metal objects, the specular they have is typically just at a medium level, like a value of 0.5, unless there are parts of the object that block light, like in cracks or crevices.) And that’s why we’re multiplying our cavity texture in the setup above by 0.5.
Since packing the actual specular map and unpacking it inside Unreal’s ‘Material Graph’ requires a much more complicated workflow, thus increasing the instruction count (every node we use in the ‘Material Graph’ comes with a cost), I prefer converting cavity to specular.
Due to the reasons I listed above, I prefer not to use specular textures, but if you want to opt in for that option, you can store your specular texture in the blue channel of your normal map, and ignore AO. Remember that, in that case you wouldn’t multiply your specular by 0.5.
I talked about this subject in my post titled ‘Is Ambient Occlusion Still Relevant?’ but to say a few things about this subject, yes, I think so. If you go to your ‘Preview Scene Settings’:
And activate ‘Rotate Sky and Direction’:
You can see the effects of these textures on our object better. One can argue that it is a personal choice to use them, so I prefer using them.
In the first tutorial, I talked about bump maps a little bit, so I’ll just copy and paste what I said there here:
"If you are using a bump map, one would typically pack a bump map as a separate texture from other maps since this allows for better organization and management of textures within the material workflow."
I created a material function in Unreal Engine 5 to streamline this process. All you need to do inside that function is connect your albedo map to the albedo input of the function, and the normal map output to the input of the same function. You can download it here (UE5 Material Function for Extracting Textures from Packed Formats) if you wish.