11 Steps to Post Processing Heaven
We here at Draconus take post processing very seriously! We think that post processing should be something that contributes to the feeling and readability of the game, the gameplay and it might help you to stand out in the crowd – it definitely should not be just for visual candy. And like mastering in audio realm, it’s quite easy to overcook your image, so proceed with caution when slapping more stuff on your post processing chain.
In Hexters, the post processing is actually very important part of the looks. Because our game is rather minimalistic in the art style (low poly, no normal maps or many texture maps), a bit of help is needed to make the image more polished and modern. We also have a proprietary gameplay specific image effect for Fog of War (FOW). The current chain is work in progress, but I decided to share this as coming up with a solid post process chain is not trivial, and hopefully this might help somebody struggling with image effects.
As we’re using Unity 5 (currently 5.3.2p1), it was quite easy to get started with the post processing. I just imported the standard Effects package and started experimenting the image effects. Soon enough though, I realized that many of the effects were not as good as they could, and started looking for alternatives.
I’ve documented the whole post processing chain here with before-and-after images, along with the plugins/packages used and the choices I made when adding these effects. One thing that is particularly important to note is the order of the effects – you can’t just slap the effects on in some random order as end result can be drastically different. For example, if you have noise and sharpen steps (as we do), you need to make sure that sharpen is done BEFORE the noise, otherwise you end up sharpening the noise and it will make noise more prominent and harder to control. Or if you have bloom and tonemapping (which converts HDR to LDR), having bloom after tonemapping will look drastically different (unnatural). Also, I’m not an expert on post processing, this is just something I’ve learnt while developing Hexters. If you see grave errors or If things I’ve said are just plain stupid, please comment or send me a message!
Few technical facts about rendering in Hexters. We’re using deferred rendering with linear color space, and the rendering is done in HDR. The final image is of course LDR, but it’s not trivial thing when the HDR->LDR conversion is done (tonemapping), and what effects are done before and after the conversion.
The steps in the chain we have are:
- [Opaque images are rendered using deferred rendering path]
- 1 Anti-aliasing (SMAA)
- 2 Ambient Occlusion (SSAO)
- 3 Fog (Screen space)
- 4 Fog of War
- [Transparent images are rendered using forward rendering path]
- 5 Depth of Field (DOF)
- 6 Bloom
- 7 Vignette
- 8 Tonemapping
- 9 Color Correction
- 10 Sharpen
- 11 Noise
Please use the slider to switch between before/after images!
Step 1 – Antialiasing
Anti-aliasing is the first step we do. The purpose of this step is to get rid of jagged edges around polygons. As we’re using deferred rendering, using hardware anti-aliasing (MSAA) is out of the question.
Package used: We were originally using the built-in anti-aliasing effect in the Effects package, but ended up using SMAA (Subpixel Morphological Anti-Aliasing) as it seemed to better job. You can get it from here: [Unity forums for SMAA]
Order: I have the anti-aliasing as the first effect as per recommendation on the SMAA Github page:
The effect should be the first in the post-processing chain (before
SSAOetc) or it will miss some edges.
Things to look for in the preview image: smoother edges on sharp corners such as cyan panel in the building and the resource boxes on the ground.
Step 2 – Ambient Occlusion
The purpose of ambient occlusion is to enhance the feeling of objects being in 3D space together instead of single, independently rendered objects. AO works best with complex geometry which we don’t really have, but in Hexters this step is yet one of the most important, as it appears to glue the objects to the ground and adds much needed depth.
I think SSAO is one of the most important post effects a modern game needs to have. But you have to be careful not to overdo it, as it will make image look like it was processed with unsharp mask 🙂
Package used: Initially I was using the built-in SSAO, but I found it quite unnatural looking and noisy. I ended up getting Thomas Hourdel’s wonderful SSAO PRO from the Unity AssetStore.
Order: As SSAO uses normals and depth infromation to darken the image, it should be used before any image effects that are masking or blurring the image (fog, FOW, DOF, bloom etc)
Things to look for in the preview image: Leg of the wireframe statue, boxes, and base of the building seem to be resting against a surface instead of being in the air.
Step 3 – Fog
The fog is not that obvious in this screenshot, but it’s purpose is to add dimension to the image. We vary the density and height of the fog during the day night cycle, so that for example just before the “dawn” there is more fog than normally.
Package used: The built-in screen space fog (Global Fog) from the Effects package. Both distance and height fog are enabled.
Order: Fog needs to be done after SSAO.
Things to look for in the preview image: fog pushes the background a bit further by darkening it.
Step 4 – Fog of War
Hexters is basically a strategy game, and we needed a fog of war (FOW) for it. FOW can be revealed by buildings or certain characters, and it’s proprietary shader based effect. Look out for a blog post about it in the future!
The FOW we use is basically a screen space fog that masks the image using a render texture as mask. Transparent object do not affect the depth buffer, so FOW would not have any idea if the image contained transparent objects in front of the FOW and that’s why the FOW needs to come before the transparent pass. Transparent object need to have special rendering/mechanism to support FOW.
Package used: Our own implementation.
Order: It needs to be done after SSAO, but might be needed to done before Fog, too (need to experiment with that). As with the fog, the effect needs the depth buffer, and needs to be rendered before the transparent pass.
Things to look for in the preview image: mountains that are in the FOW are completely masked out with a black color.
Step 5 – Depth of Field
The depth of field (DOF) simulates the camera lens, and really adds depth to your image. In our game the DOF doesn’t really play that much of a role as it’s mostly noticeable during cinematic sequences and not during the gameplay.
A very unfortunate property of DOF is that it won’t work correctly with transparent objects as they’re not rendered into the depth buffer. This will make things like particle effects seem out of focus if they’re rendered against far-away background or they might look sharp if they’re rendered against objects in the focal plane. If somebody has a good solution to this, I’m all ears!
Package used: Depth of Field from the built-in Effect package
Order: Should be done after all “solid” steps (SSAO, fog, FOW)
Things to look for in the preview image: both mountains and the front of the ground are wire on the front seem to be out of focus. The particle effect (blue hex circles) is partly out of focus and partly sharp.
Step 6 – Bloom
The bloom is probably one of the most obvious and prominent post processing effect first introduced to the masses by Epic in Unreal Engine 3 (Gears of War). At least that is when I started paying attention to it 🙂
For Hexters, this is the most influential post processing effect in the chain. The look wouldn’t really be the same without it. The bloom used does two things: it smoothens the image by adding a sheen over it, causing the high-intensity surfaces to bleed to the space around them. Secondly, a dirty lens effect causes the screen look a bit more like it was captured using a real, not so perfect camera. The dirty lens effect is so realistic that Markku has many times thought that my screen looks really dirty and that I should clean it up 😀 Maybe I haven’t noticed as my glasses usually look like this:
Package used: I tried the built-in package first, but it was looking really cheesy and unrealistic. Found out Sonic Ether’s Natural Bloom and Dirty Lens, and haven’t had to look anywhere else since. If you’re using anything else, please stop doing it and get the SENBDL instead.
Order: Needs to be done on a HDR framebuffer, so keep it before tonemapping.
Things to look for in the preview image: overall sheen over the image, cyan light from the building looks much more intense.
Step 7 – Vignette
I use vignette to round the image a bit and to make it more dramatic and focused. It also serves a secondary purpose by making the image less bright towards the edges of the screen, giving better contrast to the UI elements.
I’m not super happy with the vignette effect as it seems to darken the image too much, also in the middle. Perhaps more tweaking.
Package used: Vignette and Chromatic Aberration from the Effects package. Only Vignette is used.
Order: No particular place, could be later perhaps.
Things to look for in the preview image: More focused image, darker edges of the screen
Step 8 – Tonemapping
The purpose of tonemapping is to cramping the high dynamic range (HDR) values into the limited or low dynamic range values (LDR). The display can only show certain range of pixel intensities, so this phase is needed to make sure everything fits in the range. Without tonemapping, the overly intense values (over 1.0f) would be crudely clamped to 1.0f.
Currently we do a static tonemapping, which slightly makes the image brighter (partly undoing the overal darkening caused by vignetting). I’m considering doing some automation to the tonemapping exposure depending on the situation (day/night, huge bright explosions etc).
Package used: Tonemapping from the Effects package. Using the “Photographic” technique.
Order: Converts HDR to LDR, so it should be after all the effects that work better on HDR than LDR. Should be before any non-linear pixel processing such as color correction.
Things to look for in the preview image: High-intensity pixels being mapped LDR range around the building paneling, overall image brightness.
Step 9 – Color Correction
Color correction can be used to dramatically alter the feel of the image. It’s common to use it to reduce the colors in the image to achieve more cohesive feel. Gears of War is a classic example of this processing.
For this shot I’ve chosen the cliche orange-teal processing to get that larger-than-life summer blockbuster feel! Ironically the orange-teal process works great for film as it preserves or enhances human skin color – and we don’t have any human characters in the game 😉
We look forward to using different color corrections to emphasize the unique atmosphere of each level. Together with Markku’s awesome music it will work wonders!
One particular problem we have with the color correction is that it can alter the color hue quite a bit. In Hexters, we have a “trademark” cyan color which easily gets lost in translation – as can be seen in the screenshot. Looking forward to do something about it.
Package used: First I used the Color Correction (3D Lookup Texture) found the built-in effects package, but it was extremely painful to use as all the tweaking needed to be done in Photoshop for a screenshot, and then save-exported to Unity where it needed to be converted to a LUT in the actual effect. I soon switched to Thomas Hourdel’s Chromatica Studio, and haven’t looked back. Highly recommended!
Order: Must be done on LDR image, result is LDR. So put it after tonemapping.
Things to look for in the preview image: Less neutral and clinical image
Step 10 – Sharpen
Sharpen is something I recently decided to add to the chain after wondering what in earth was going on in the post processing chain for Mirza Beig’s Ultimate VFX package. It was sharpen. I wanted something similar but not that pronounced. I think the sharpen brings back some of the crispiness of the image that was lost in the chain.
Package used: Sharpen in MIR-2II Shader Pack found in Mirza Beig’s Ultimate VFX package.
Order: Should be one of the last effects in the chain. Preferable before noise.
Things to look for in the preview image: Overall sharpness of the image, the boxes in the background in particular
Step 11 – Noise
The final step in our chain is the noise. I think a subtle noise adds grit and makes the image more alive. One another important and practical reason to add noise is to hide any possible banding issues caused by lack of precision in some parts of the rendering. Even with HDR rendering there can be loss of precision in the image, and ugly banding can appear. Slight dose of noise can make that go away.
Package used: Built-in Noise and Grain effect, not using film-grain.
Order: Should be the very last effect in the chain. Any filter-based effect will affect the noise considerably, making it hard to tweak the noise parameters.
Things to look for in the preview image: Slight, much needed grit.
Before and After and Conclusions
Here is the comparison between the raw and processed image. Quite a difference.
So, there we go. That’s what we have in the menu right now. I’m looking forward tweaking the effects and making the level/situation specific changes to the parameters to make the game feel more dynamic and alive.
I haven’t touched the subject of performance at all. Currently the post processing seems to run just fine on our computers, but we need to test the game also on some lower end computers and maybe do some optimization such as merging some post processing steps into one. But later about that.
Any comments/questions? Feel free to comment here, or send me tweet at @snlehton.