Table of Contents

🔳 CosterGraphics.Systems.OutlineSystem


🕶️ Shaders Overview

This OutlineSystem uses three types of shaders:

  1. 🎭 Mask Shaders
    The mask shaders are used by the renderer feature during the mask render passes to create different types of masks of the outline objects that are stored to be used later each frame by the Composite shaders that draw the final outlines to the camera texture.

  2. 🔁 Intermediate Buffer Shaders Intermediate buffer shaders are only used by the Pro tier outline shaders during render passes in between the regular mask passes and the final composite passes. To create the Jump Flood Algorithm distance field textures that are required by the JFA Composite shaders, the JFA Seeds Texture Mask created during the JFA Seed Mask pass is first copied to an intermediate texture that is then written to several times in a loop for as many render passes that are required to generate the field (which depends on the size of the screen). To create the JFA distance field outlines the system uses separate vertical and horizontal jump passes that both require a different intermediate material/shader. After the jump passes are done the final JFA UV field is stored in a JFA UV jump render texture which is used by the Composite JFA shaders via the _OutlineJFAUVJumpTextureMask property to calculate distance fields and signed distance fields with of the ouline geometry. The JFA seeds texture from which the UV field texture is originally created is also stored in a render texture for later use in the Composite shaders via the _OutlineJFAUVSeedTexture property. What is stored in the JFA UV seed textures during the mask passes are only the screen space UV positions of the masked outline objects and what is stored in the final JFA Jump texture after the intermediate horizontal and vertical jump passes are the distances from each pixel to the closes edges of the masked outline objects.

  3. 🖼️ Final Composite Shaders To explain this in terms of cooking in a restaurant kitchen, the outline composite shaders are where the outline ingredients that are created during the masking render and shader passes are used to cook the final outline dishes with. Each composite shader uses one or more different kinds of ingredients to create the different types of outlines with. In regular terms, the composite shaders uses the render texture masks, (of which most are drawn using unlit or lit shaders via command buffers in the renderer feature that render only the selected outline objects to the special render texture masks), to the scene view and game view cameras color textures via full-screen Shader Graph shaders. This setup makes it extremely easy to start creating your own outline shaders for this system because any Full-Screen Shader Graph shader used for the composite pass of the outline systems renderer feature has access to all the ingredients via the properties listed below. To keep things optimized and efficient the renderer feature will only execute the passes and create the mask textures if the composite actually has the right property for it!



🥚 The OutlineSystem Composite Mask Texture2D Shader Properties (The Outline Recipes Ingredients)

These are the property names of the mask textures that are available to the final Full-Screen Composite shaders of each Tier. The system's Renderer Features only run the required Render Passes for it if the final composite shader has one of these properties added to the Shader Graph's properties.

Type Property Name Simple Basic Advanced Pro
Texture2D _OutlineColorTextureMask ✔️ ✔️ ✔️ ✔️
Texture2D _OutlineColorTextureMaskDepthTested ✔️ ✔️ ✔️ ✔️
Texture2D _CameraOpaqueTexture ✔️ ✔️ ✔️ ✔️
Texture2D _OutlineDepthTextureMask ✔️ ✔️ ✔️
Texture2D _OutlineNormalsTextureMask ✔️ ✔️ ✔️
Texture2D _OutlineUVTextureMask ✔️ ✔️ ✔️
Texture2D _OutlineObjectIDTextureMask ✔️ ✔️
Texture2D _OutlineJFAUVSeedsTextureMask_Inside ✔️
Texture2D _OutlineJFAUVSeedsTextureMask_Outside ✔️
Texture2D _OutlineJFAUVJumpTextureMask_Inside ✔️
Texture2D _OutlineJFAUVJumpTextureMask_Outside ✔️

Choosing the right Outline Composite Shader

There is no real one-size-fits-all-type-of-games full-screen outline edge detection shader method so it is important to choose the right Composite shader that uses the right edge detection methods for your project's needs.

There are a whole bunch of different types of outlines to choose from and they all have their pro's and cons but they can loosely be grouped together based on their ability to draw thin or thick outlines and wether or not they can draw the lines on the actual outside of the geometry/masks or on the inside only.

Then there are also different methods for drawing only Silhouette outlines around the perimiters of the outline geometry mask and the ones for drawing the Contours as well. Here are some tips that may help you choose the right Composite Outline shader for your project...

Choosing Outlines based on desired thickness

Drawing very wide crisp silhouette outlines can be quite computationally expensive using the most straight forward traditional edge detection method which is to take multiple samples in a box or circle(-ish) shape around each sample pixel, to figure out if there is an edge at that location. With this method, when very thick outlines are desired, we can choose to either sample a lot of neighbor pixels (exponentionally increases sample size with thickness) or we can offset the samples taken by adding a large offset position (doesn't increase sample size but is basically a cheat for extra thickness that can only be used a little before it breaks the illusion of fake thickness).

For sampling only the direct neighbors of a pixel eight samples are required and when sampling the neighbors around the direct neighbors as well (this would be called a two pixel sampling radius) twenty five samples are required. So the amount of required samples increases dramatically with each increment of the sampling radius (remember it has to be done for each pixel of the screen). Increasing the sampling radius can only be done so-much before it becomes very expensive.

Another method for drawing outlines is using the Jump Flood Algorithm to 'flood fill' a texture map with the UVs of the closest edges to each pixel and to calculate a distance field or signed distance field from that which can then be used to simply draw outlines over the distance away from the masked geometry which is a very efficient way to drawy very wide outlines. Outlines generated with the JFA have the added advantage that colors can be easily blended across the distance as well, making it really easy to generate all kinds of special multi-color outline effects.

So depending on wether or not you want thin basic outlines or thick special effect outlines you can pick between the system's 'regular' Simple, Basic or Advanced Composite shaders, which all use the sampling neighboring pixels methods and different edge detection kernels, or you can choose the Pro JFA Distance and Signed Distance field outlines for thick to ultra thick outlines.

Keep in mind that all outline edge detection and thickness methods used for the Composite shaders have their own pros and cons. Since all the outlines are rendered in screen-space as a post-processing effect, it is always an aproximation of the 'real' data and the input used for the edge detection algorithms is never absolutely mathematically perfect, since texture masks with limited band with are used to store data like normal vectors and depth values.

Because this outline system works in the context of the Unity game engine we do have access to (almost) perfect, low-noise, image data to perform the edge detection on because we can use normal and depth data to perform edge-detection on, which is ofcourse far less noisy than real digital camera images are. Even when performing edge detection on color data coming from the URP scene's camera (the camera opaque texture) it is still much less noisy than images taking in the real world.

This has the advantage that even simple edge-detection methods can produce really nice, crisp, sharp and stable outlines when using game engine graphics as input!

Choosing Outlines based on capability

Choosing Outlines based on sharpness/quality

Choosing Outlines based on rendering efficiency