Since my game requires tiles of different types to be rendered with clear separation between them, I need a way to render these sprites without tiles of different types overlapping each other and ruining the separation. This is where the shader graph comes in.

The way this works is that the reference sprite of each tile will have a specific color that is unique to it. It does not need to be very visually distinct (though that definitely helps while debugging), but the color’s hex code should be unique. This is because we’re essentially going to chroma key out this reference sprite and replace it with the rendered sprite using a shader.

The green areas in this image can be replaced with any other computer generated or filmed elements, while keeping the subject and its motions intact.

Chroma key is a concept used primarily in film — it is the green screen effect you see in movies and videos, where the original shot is recorded with a (most commonly) green background, so that during post-production, all the green in the shot can be replaced with a scene of one’s preference.

Go into Edit -> Project Settings -> Tags and Layers and under Layers, make two new layers called Mask and Ground.

Create a new Camera in the hierarchy and rename it to Mask Camera. In its Camera component, first set the Culling Mask to “Nothing” and then set it to the Mask layer.

Now go into your Project window and make a new RenderTexture called MaskRT. Set the Mask Camera’s output target field to Texture and the Texture field to MaskRT. If you are using an older version of Unity, simply set the camera’s Render Target field to MaskRT.

Set the masked camera’s render target to MaskRT

What we’ve just done is created a new render texture onto which the Mask Camera will render the scene to. However, it will only render the reference sprite of each tile, because we have only set the camera to be able to see the Mask layer. This is the same layer on which all the reference sprite renderers are set to.

Under the Main Camera’s Culling Mask dropdown field, click on the Mask layer to remove it. This layer should only be rendered onto the MaskRT render texture and should not be rendered directly onto the main camera.

The Main Camera with the Mask layer removed from its Culling Mask.

This is when we start the process of chroma keying. Create a new script and call it TileData. Add this script to the prefab of the tiles that you have been instantiating (or pooling) while generating your procedural terrain. Open up the script and create the Start() method and within it, cache a reference to the reference sprite renderer (spriteMask) as well as the rendered sprite renderer (sRenderer).

Then create a new method called SetData():

What we’re using here is a Material Property Block. We can use these to pass in values to the shader that is assigned to this sprite renderer. But if you try to run the code, it won’t work yet, because we haven’t created the shader that is responsible for replacing the chroma keyed mask sprite with the rendered sprites.

NOTE: The variable tileOnFile is a reference to a ScriptableObject that I am using to store instances of tile types. Ideally, you would have a storage system for each tile type which holds a reference to a unique data (such as mask color, size, sorting layer, sorting order, etc) for that tiletype. I have used ScriptableObjects to create instances for each different tile types and that is where I retrieve these properties from, however, you are free to use any storage method that suits your project, as long as you can get the unique chroma key color for a given tile type during its instantiation.

Different Scriptable Objects that hold data for different tile types.

An example of data held by one of the tile type scriptable objects (Grass tile). Notice the Mask Color field.

Go back to Unity, and in the Project Window, create a new Sprite Unlit Graph (This is an experimental feature at the time of writing this article), and call it TerrainMaskedTile.

In the Properties window of the shader graph, add the following properties:

Important Note: Make sure that the Reference names of each of those properties are EXACTLY the same as shown in the above image. These are what we use in the code to refer to these variables using the Material Property Block. The reference name of the Render Texture can be the auto generated one since we do not need a reference to it using the Material Property Block.

Set the default value of the render texture to MaskRT, the render texture that we created earlier on in the tutorial.

Given below is what the shader looks like. For the purposes of this tutorial, I will not explain it step by step. However, the way it works is that the render texture is sampled at the position of the tile, and if the given pixel is the same color as the chroma key passed in for this tile, then that pixel is replaced with the value of the pixel on the Main Texture.