In the previous Part Two installment I documented each component of the stylized material I made. By combining and layering together a lot of simple concepts you can easily make a more complex network that’s visually interesting.
In this section I want to expand upon that further and demonstrate the parameters, document the post process materials I developed to enhance the look, and finally bring everything together in a simple test level using the Third Person template, where we’ll run around a stylized woodland.
The models I used are from Quaternius • Free Game Assets. I could make some simple assets of course, but my feeling was it’s even more valuable to test a shader on assets that you didn’t model yourself, to ensure that it performs well regardless of the geometry you give it. I also like the style of the models he makes. They’re CC0 Creative Commons license, but I want to give credit as he was nice to give his permission for me to use them in this piece.
Post Process Shaders
In addition to the base stylized material, I wanted to experiment with some post process materials. In Part Two I showed a turn table of the shader where the outline width can vary slightly depending on the surface topology and the viewing angle using a fresnel effect. In addition to this I wanted to experiment with the following:
General outline shader
Desaturation based on player distance from an object
Desaturation of the ground base on player location, where the ground is desaturated after a certain falloff distance from the player
Alternative texturing of the shadows, where the shadows can have a hatched effect
For the alternative texturing of shadows, the goal was to not only have the object self shadowing be hatched, but also the ground shadows, or allow the user to feed in a custom texture. I got the idea for this from a post by Tom Looman: Textured Shadows Trick in Unreal Engine - Tom Looman. In this post he demonstrates how to access the Light Attenuation buffer and query an objects UVs to project a texture into the shadow regions. The Light Attenuation buffer is technically no longer accessible in UE5 to my knowledge, at least not easily. Render Bucket, an excellent YouTube channel details an alternative workflow for creating an equivalent ‘shadow catcher’ pass: Unreal Engine 5+ Shadow Catcher / Shadow Pass (youtube.com). Effectively
Desaturation of distant object and areas on the ground is easy to capture by sampling the depth buffer and applying an Overlay material to relevant objects. Overlay materials can be prohibitive in terms of performance, but in this case the operation is simple, where I’m just desaturating the primary color.
An outline material relies on basic edge detection. There are a number of algorithms that allow for this, generally all relying on kernel convolution. A notable operator is the Sobel filter: Finding the Edges (Sobel Operator) - Computerphile (youtube.com). Laplachian style filtering generates a more complex edge with more interior details. Edge Detection Using Laplacian | Edge Detection (youtube.com) In this case I really only wanted the external edge, so relied on a Sobel style operator.
A key source for ideas on how to implement these effects in UE5 came from a GDC 2024 talk that actually released while I was in the midst of working on this. Chris Murphy of Epic Games covered a huge amount of information in just 30 minutes: Simple Stylization Techniques in Unreal Engine | GDC 2024 - YouTube. This talk shed light on how to access the correct frame buffers to use in my post process shaders.
Outline Shader
As mentioned the main additional post process effect I wanted was an outline material, to compliment the edge detection in the base material. By creating a more uniform black outline my feeling was this would enhance the read of objects and improve visual clarity.
Creating an outline material is fairly simple. We can change the material assigned to the Post Process Volume to overlay an outline over the entire scene by querying the incoming color and implementing an edge detection algorithm. It sounds complicated, but it’s actually very easy to implement in UE’s shading network.