Just as a short preface for shader time, I noticed that many toon shader tutorials used different methods in Unreal Engine to achieve a stylized look, so I looked for and found this artist’s stylized shader theory series which helped me understand the underlying logic.
Stylized Rendering Basics
The series is by
Ikwoo Lee (이득우). I recommend it heavily for getting an initial grasp on NPR. Before I could get started, I needed to make a few finicky adjustments to the UE mesh in preparation.
First, I created an eye shadow mesh by duplicating the top half of the eye socket boundary's edges, then extruded downwards and adjusted manually with proportional editing.
Then, I created an eye fresnel mesh by creating a UV sphere in Blender, applied a subdivide modifier with 3 levels, then scaled down to squash it closer to a square to lay on top of the eye, since this eye has more of a flatter profile than a realistic eye.
This mesh is to act as a container for the eye specular I create later on.
Finally, I assigned the face to its own material/UDIM and centered it, so that flipping the SDF wouldn't need manual adjustments after. Aligning the vector to the face orientation without using bones is also easier, since I don't have a rig in Unreal Engine.
Afterwards, I found this tutorial by
Songji which compiles several components needed for the character’s look, oftentimes scattered into what I felt were potentially conflicting solutions. The video explanation is thorough, so to save some time, I'll skip to the gotchas and extra things I did.
UE5 Toon Shader Breakdown by Songji
While setting up the DirectionalLight which is used to set a single direction for the shader's light and shadow to be based on, I realised that the light was not returning a rotation value.
It turned out that I had set the Light Intensity to 0 to prevent it from directly affecting the scene, but it also caused this issue.
To get around this, I disabled the lighting channels for the actor instead.
Onto the next hitch, the head joint was used as a reference for the head's forward vector, but I had no rig present.
As such, I created an empty actor and manually positioned it to the character's head.
Before I set up the face SDF map that I created previously, I also needed to flatten the normals on the face.
I used this video for guidance on flattening normals.
I found that the export settings in the video didn't work for me, so I experimented until finding that Smoothing (Face) and enabling Tangent Space worked.
In Unreal Engine, I reimported the mesh, then disabled Recompute Normals and Recompute Tangents such that UE5 would not overwrite my custom normals. I also made sure to Apply Changes at the bottom.
With that settled, I wanted to use the Ambient Occlusion I baked from Substance Painter for the face, but it was another contributor (aside from the original normals) which exposed the real contouring around the nose.
As such, I went back to Painter to manually paint away the AO around the nose area. To accomplish this, I added the original AO bake in a new layer, then a paintover layer over it in the Basecolor channel. Implementing the SDF map was straightforward from there.

before
after
face ao paintover
From here, I did most of the things mentioned in the Toon Shader Breakdown, then moved on to set up other things I had in mind, like this
Eye Specular effect using Inverted Fresnel I saw from
Cherylynn Lima. This is what the eye fresnel mesh was for.
Tutorial by Cherylynn Lima
As the effect was made in Blender, I had to translate it into Unreal Engine, which was surprisingly straightforward. I just used a Fresnel node, guesstimated the Exponent and BaseFraction scalar values, and assigned a white Emissive colour.
The troublesome part was figuring out how to add more specular highlights. Using an Add node didn't work, and I needed to research how fresnel was calculated to realise that I needed a Max node instead.
I added X and Z offset scalars to a VectorNormalWS, which then gets plugged into the normal input of each fresnel node.
After getting the base setup going, I added more scalar parameters for X and Z offsets to control the specular positions individually, as well as one more set of offsets in the Material Parameter Collection (MPC) to adjust them as a whole.
I wanted this overall offset value since the speculars don't adapt well to vertical camera angle changes.
After this was done, I also wanted to try implementing a
light falloff effect for the toon shader. The artsy basics of this effect are best explained in a video by
Marco Bucci.
Light Falloff explanation by Marco Bucci
To make my intent clear, my goal for this effect is to isolate and have colour control over just the area where light meets shadow, since I already have control over the latter two.
After some brainstorming, my plan looked like this:
My plan was to get individual masks of the left and right areas of the boundary, then subtract the masks from each other to get the boundary area itself.
As part of the Toon Shader video, I obtained a shadow mask through a smoothstep function. This involved calculating a min and max range for the smoothstep, with the directional light’s dot product to the character as the input.
To start, I treated the shadow mask’s min and max range as the boundary of two new masks. Let’s call these the ShadowMax and ShadowMin masks for simplicity’s sake.
I added another scalar to control the deviation of the light falloff mask. This is used for calculation of the min and max range for the ShadowMax and ShadowMin masks, giving us the two masks.
Finally, we subtract the ShadowMax mask from the ShadowMin mask to obtain the light falloff mask. I also multiplied in a scalar at the end to control the opacity of the light falloff mask.
I'm quite happy with the final result. It can be used to accentuate more dramatic lighting, but I'm going to keep it subtle this time.
After the light falloff effect was complete, I multiplied an extra layer of ambient occlusion and fresnel-driven shadow onto the final emissive colour to get a bit closer to the Endfield look.