Adding SMAA T2x to Unreal Engine 5

Recently, I took an interest in Enhanced Subpixel Morphological Anti-Aliasing (SMAA) [1]. SMAA is a post-process anti-aliasing technique built to be lightweight and performant.

Without getting too far into rendering, the majority of shipping games currently use a Temporal Anti-Aliasing (TAA) solution, at least in the Triple-A space. There's a few reasons for this, but the main one is deferred rendering significantly hampering "older" techniques such as Multi-Sample Anti-Aliasing (MSAA).

Temporal solutions also open the door to rendering helpers such as DitheredTemporalAA in Unreal.

Unreal Engine already supports a few types of anti-aliasing. It defaults to one of its two temporal upscaling solutions depending on version, but also supports Fast Approximate Anti-Aliasing (FXAA) and a native resolution TAA. When the engine is using forward rendering, MSAA is also available.

I chose to implement SMAA T2x, a temporal variant of SMAA, as keeping DitherTemporalAA functional is relatively important for some UE features to continue to function.

Below is a screen capture from Epic's City Sample using Temporal Super Resolution (TSR) at 100% resolution scale and an early SMAA T2x implementation as a temporal plugin. This approach ended up not panning out, and I resorted to modifying the engine.

Unreal Engine's Temporal Super Resolution

Personally, I like TSR's presentation. It's a little softer, but it's retaining a lot of detail and, unlikely SMAA T2x, it supports non-native render resolutions.

SMAA T2x in Unreal Engine 5

SMAA T2x's advantage is the sharper image it creates. In the above image, there's more aliasing than the final images created with the version directly integrated into the engine. This is because all temporal solutions cause Unreal 5 to use a pseudorandom subpixel jitter pattern; however, for SMAA T2x, we need an MSAA 2x subpixel pattern (North-West, then South-East).

Comparison in UE5

Additionally, while all of TAAs that Unreal supports do a really good job at preventing ghosting in animated textures, SMAA T2x requires a bit of tuning from the defaults to clean up the in-texture ghosting. In particular, the weight of the current frame is the greatest factor.

Changing the current frame weight upwards does reduce ghosting in textures, but it also reduces the effective anti-aliasing gain from T2x as more data is coming from the present frame.

The engine implementation, available as a Pull Request on GitHub, breaks the reference implementation in a way that I feel improves overall quality. In [1], SMAA uses the scene depth, scene luminosity, or scene colour to detect edges. Each of these options comes with benefits and drawbacks and are listed in order of GPU intensity. In the debug view that the engine version implements, it's obvious that both luminosity and colour detect far too many edges within an object, whilst often failing to detect the border. Ultimately, this issue leads to visible aliasing on the edges of objects. The depth information fares far better, but loses precision, in my opinion, unacceptably close to the camera.

In Unreal, we can pass most buffers into our post-process shaders though, and some of the data in the geometry buffer proved quite helpful. In my testing, I found that running edge detection on the world-space normal provided high quality object edges similar to the result of the depth information without the distance limitations.

References

[1] Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, and Diego Gutierrez. 2012. SMAA: Enhanced Subpixel Morphological Antialiasing. Comput. Graph. Forum 31, 2pt1 (May 2012), 355–364. https://doi.org/10.1111/j.1467-8659.2012.03014.x