Season system with interactive snow shader

By: Leon Lauran

Vast open landscapes with small details in videogames have always fascinated me. Walking around an artificial world that feels alive is a special feeling that can stick with a person for a lifetime. To me worlds like the ones featured in titles like Red Dead Redemption 2, The Witness and the Horizon series feel meaningful and alive. I took inspiration from those games where different weather/season scenario’s feel so different from one another. Another big inspiration for me was the season system currently being developed for the upcoming MMO-RPG Ashes of Creation. Their system can be seen in the video below.

So what did I manage to achieve? I managed to make a flexible system that allows for different object based actions on season change. Think of disabling objects, triggering different animations, starting the transitions between seasons, and toggling different particle effects. In the image below a small demo scene of the seasons can be seen. The demo showcases the above-mentioned effects. The demo however does not showcase the interactive part of the snow shader I made. I will further show and explain the shader and the season system in this blog.

Table of contents

  1. Season System
    1. Framework
    2. Object placement
    3. Leaves shader
  2. Tessellation
    1. Fixed amount
    2. Distance-based
    3. Edge length based
    4. Phong
    5. Comparison between methods
  3. Mesh manipulation
    1. Particle system
    2. Camera set-up
    3. Snow displacement
  4. Results
  5. References

1. Season system

I started working on a flexible system managing season change that would be easy expand on. The system should be compatible with different kinds of actions when the season changes.

1.1 Framework

A “SeasonManager” script keeps track of the current time, day, and season, it is also responsible for the transition between days and seasons. When the time reaches the specified day duration, the next day starts. When the number of passed days are equal to the specified number of days in a season, the next season starts. To get the next season in the enum without a switch case I made a custom enum helper class with static methods that can be used on any enum variable. When a season change occurs (days passed exceeds the specified days in a season), all I have to do is set the current season to the result of the “Next” method seen below. In that same moment I also invoke an Action variable with the new season. Actions can be listened to by other scripts and execute a method when the action is invoked.

For the easy creation of behavior scripts that trigger on season change without having a lot of duplicate code in different scripts I made a base class for season change behaviors. The base class listens to the Action that is invoked in the season manager script and keeps track of what seasons are applicable to the behavior. In the derived classes I only have to specify what happens to the object if a season change occurs, allowing for very clean and to the point code. In the two images below, a simplified version of the base class can be seen on the left. On the right, a derived class can be seen that is used for disabling an object when the season changes. The “ActiveSeasons” variable is a derived variable specifying what seasons should trigger the behavior.

The “ActiveSeasons” variable is manipulated by a custom editor script so it is possible to select multiple seasons. The editor script creates a mask field with the seasons enum states so I have complete freedom in choosing what seasons I want to be applicable. Because the editor script gets the enum itself, it automatically updates if different seasons are added or taken out of the “Seasons” enum.

1.2 Object placement

To fill up the scene I wanted to add some trees, flowers, mushrooms and bushes. As I wanted to animate and disable the objects on season change, I couldn’t use the terrain tool as I normally would. Therefore I created a script that randomly instantiates a specified amount of specified objects on the terrain in a specified area. For the amount of objects to be placed the script chooses a random position within the given boundaries, the script shoots a raycast downwards from the random position until hitting the ground or exceeding the travel distance. If a raycast hits the ground an object is chosen from the object list based on the random number and placed on the spot where the raycast hit the ground. Because this instantiates quite a few GameObjects it can be performance heavy when increasing the number of objects, but for this demo it posed no problem.

1.3 Leaves shader

A season system would not be complete without the beautiful autumn colors found in the forest. I wrote a simple shader that lerps (linearly interpolates) between two colors based on a specified value that ranges from zero to one. The shader therefore has four properties, a texture, two colors and one float. The sub-shader uses the predefined surf method for easy surface manipulation. In the surf method I have the possibility to sample the main texture at the given UV value of a vertex for a more realistic look. I don’t use a texture for the leaves in order to keep the simplistic looks of the trees. The value resulting from the texture sampling is multiplied by the result of the lerp between the two leave colors. As seen in the demo video, the leaves start green and slowly transition to an autumn color when fall starts.

The leaves shader is not the only shader I created, there is a bigger more complex shader in this project that was even more fun to figure out, a snow shader! Snow shaders are in my opinion very underrated. How accurate developers have managed to make snow in modern day games absolutely baffles me. Every character leaves accurate footsteps in the snow and I got curious how they did it. In the next chapters I will explain what I learned about snow shaders and how I created one.

2. Tessellation

In order to get snow in greater detail without making a custom mesh with more vertices, developers can use tessellation to create more vertices in runtime without permanently modifying the mesh. Tessellation adds extra triangles to a mesh with math! In the images below, the same quad can be seen in wireframe view. The only difference between the images is that the detail of the quad is drastically different, this is the result of tessellation. There are different methods of calculating what parts of the mesh need to be effected by tessellation, I will explain these methods further in this chapter.

No tessellation
Tessellation value of 3
Tessellation value of 5
Tessellation value of 64

2.1 Fixed amount

Tessellation seems very daunting at first, but I was surprised by how easy it was to apply. In the pragma statement it is possible to supply a method that returns a tessellation value. In the image below, I supply the tessDistance method into the tessellate parameter in the pragma line. This method returns a fixed value I have set as a property. By changing the property in the editor, the detail of the meshes that have the tessellation shader attached will increase in detail. This method is a good start if developers need a certain mesh on a small object to have more detail based on a fixed value.

2.2 Distance-based

A fixed amount of tessellation is not always the right answer as the extra detail is not needed in every scenario and it takes more computing resources to add the the extra detail to a mesh. Distance-based tessellation is a good solution for adding more detail based on the distance between the camera and a specific vertex. Whenever the camera comes closer to a vertex the shader adds more detail to the mesh based on specified values. In the video below, I fly over a standard plane that has a distance-based tessellation shader. The tessellation values like the tessellation power and tessellation falloff can be changed through the shader properties.

It once again surprised me how easy it was to apply this tessellation technique in Unity. By including the built-in “Tessellation.cginc” file I got access to different tessellation methods. The tessellation file used by Unity is free to use and can be found on GitHub here. By using the “UnityDistanceBasedTess” method from the tessellation file and adding a couple lines, my shader supports distance-based tessellation. This is the method I end up choosing for my snow shader, why I did so is explained in the comparison part of this chapter.

2.3 Edge length based

Distance based tessellation is generally only useful if the triangles of a mesh are of equal length. In a situation where both the ground and a random small prop in the environment, like a barrel, have the same tessellation shader, they both get a certain amount of extra detail based on the distance to the vertices of that object from the camera. Most likely resulting in an overdetailed barrel in that scenario. Edge-based tessellation has a solution for that problem. In stead of solely looking at the distance to the vertex compared to the camera position, edge-length based tessellation looks at the length of the mesh edges. If the edge is longer than the specified amount, extra detail is added to the mesh. In order to achieve edge-length tessellation all we have to do is change the tessellation method to the edge-length tessellation method. In the image below, which is from the official Unity documentation, edge-length based tessellation is shown.

2.4 Phong

Phong tessellation is an addition to the previous mentioned methods. Phong tessellation adjusts the extra detail added by the tessellation process to follow the normals of the faces on the mesh. By following the normals the low-poly models get extra detailed curves. The image below, which is from the Unity documentation, helps to understand what Phong tessellation does. On the left is the mesh in regular scene view. On the right is the same mesh with wireframe view added. The top row is the mesh without tessellation, while the lower row shows Phong based tessellation in action. The sharp edges on the characters face disappear thanks to the Phong algorithm.

To Implement Phong tessellation all we have to do is specify a variable that can be given to the “tessphong” parameter in the pragma line. In my case I made the variable a property so I can change it in the editor view of the material. With Phong tessellation, the curves made by the indents in my snow shader will be smoother.

2.5 Comparison

All the tessellation methods have their own specific use cases where they shine. In some cases tessellation can be overkill overkill. In my case I want to increase the triangles of the terrain in my demo scene. As the snow trail will only be affected where the player has been, I don’t need to increase the detail of the terrain that the player cannot see. Fixed tessellation is out of the question for me as it increases the detail of the entire terrain regardless of camera world position.

Distance based tessellation was a perfect fit for me as I want to add extra detail based on the camera position. Only the area around the player will have higher detail, thus saving computing power and giving the right amount of detail where needed. I passed on edge-length based tessellation as I don’t have multiple objects with the same shader, which is where the method shines the most in my opinion.

In combination with distance based tessellation I use Phong tessellation to smooth out the trails left behind by the player. Without Phong tessellation the trail has relatively sharp edges and looks less convincing. How I achieved the snow trail indenting the mesh will be explained in the next chapter.

3. Mesh manipulation

Now that the terrain has higher detail around the player, we can work on actually leaving indents in the snow. The terrain mesh is manipulated by a surface shader that takes a texture for the spots where indents in the snow should be. Because the texture needs to be updated in real-time while a player walks around the environment I will use a render texture that a separate camera renders to. There are multiple ways of tracking where the player has walked, the one I choose is with a particle system attached to the player.

3.1 Particle system

The particle system is used to track where the player is within the bounds of the separate render texture camera. The particles will be emitted from the players feet in world space based on the distance which the player has walked. The particles will fade in color over lifetime so the strength of a particle on the render texture decreases over time, resulting in the illusion of the snow filling up the trail over time. As seen in the video below, the particle system leaves a smooth trail behind when the object is moving. The particles also render facing up regardless of camera position, this is achieved by changing the particle render mode to “Horizontal Billboard” in the render settings of the particle effect.

3.2 Camera set-up

In the scene I have a separate camera that renders what it sees to a texture. The render texture is in turn used by the shader to leave trails in the snow. As the separate camera is only used to render the part where a player walked, I set the camera to isometric mode. The isometric view makes it easier to define an area that the camera should render as it looks at the world without perspective. The camera should face towards the ground, in my case that would be downwards. As I work with the particle system approach, I need to set the particle system to a specific layer that the camera can focus on. The render texture camera culling mask will be set only to the particle system layer so it ignores the rest. Setting the background to a solid black color gives a really nice texture that can be used by the snow shader.

3.3 Snow displacement

Now that the render texture is created everywhere the player walks, it is possible to use that texture in a shader to offset mesh vertices based on the red pixels in the texture. The displacement method first gets the value of the displacement textures’ red channel with the texture coordinates of the vertex. This float is then multiplied by the specified displacement amount, which is used to control the maximum snow height. The resulting float is then multiplied by a specified float giving extra definition to the bottom of the snow trail. These values result in a float that is used in the calculation for manipulating the y position of the vertices.

When subtracting the resulting number from the displacement value, the snow is heightened to the height of the displacement value except for the red spots on the render texture. When multiplying the resulting value by a snow level that ranges from 0 to 1, it is possible control the percentage of how high the snow is towards the displacement height.

4. Results

The snow shader updates in real-time and works with the season system. The season system itself works like a charm as well, it is easy to expand upon and has a lot of flexibility with the variables that can be changed. The season framework and shaders are light enough to keep the demo running at an average 100 frames per second in the Unity editor. To allow other object to manipulate the snow, all I have to do is add the particle system prefab to the object as a child and it magically works.

There are always things that can be improved, one such example is further reducing the sharp lines in the displaced snow. By averaging the height of a vertex with the height of its neighbors, the sharp lines will be less apparent. Something else that can be improved is the detail of which objects can manipulate the snow. The next time I am going to make a snow shader, which will happen, I will look into the depth render a camera makes. By using the depth render of a camera that looks at the world from the ground up I can see what objects are further away compared to other objects. When working with animated characters that have footsteps, the depth render method can show the player lifting its feet when walking through the snow, leaving detailed footprints.

5. References

Gabriel Aguiar Prod. (2021, June 1). Unity Shader Graph – Snow Interactive Effect Tutorial [Video]. YouTube. Retrieved March 30, 2023, from https://www.youtube.com/watch?v=ftCyZ7F5q9E


Henley, S. (2021, March 6). The Snow Is The Best Part Of Red Dead Redemption 2, Actually. TheGamer. Retrieved March 14, 2023, from https://www.thegamer.com/red-dead-redemption-2-snow/


How To Make a Fast Screen-Space Snow Accumulation Shader In Unity. (n.d.). The Knights of Unity – Blog of Knowledge. Retrieved April 4, 2023, from https://blog.theknightsofunity.com/make-it-snow-fast-screen-space-snow-shader/


Infinite Bits. (2019, December 13). How SNOW and ICE are used in Video Games – Infinite Bits [Video]. YouTube. Retrieved April 4, 2023, from https://www.youtube.com/watch?v=8BP2lV2IjeI


Minions Art. (2019, March 29). Interactive Snow w/ Tesselation | Minions Art on Patreon. Patreon. Retrieved March 6, 2023, from https://www.patreon.com/posts/25641162


Noordenbos, K. (2021, October 25). Recreating snow with a shader – HVA GPE Game Lab – Fall 2021. Game Lab. Retrieved March 7, 2023, from https://summit-2122-sem1.game-lab.nl/2021/10/25/snow-deformation/


TwoTailsGames. (2017, December 14). Unity-Built-in-Shaders/Tessellation.cginc at master · TwoTailsGames/Unity-Built-in-Shaders. GitHub. Retrieved March 20, 2023, from https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/CGIncludes/Tessellation.cginc


Unity Technologies. (2018, March 20). Unity – Manual:  Surface Shaders with DX11 / OpenGL Core Tessellation. Unity Documentation. Retrieved March 17, 2023, from https://docs.unity3d.com/Manual/SL-SurfaceShaderTessellation.html


WB Games Montreal. (2014, March 17). Deformable Snow Rendering in Batman: Arkham Origins. GDCVault. Retrieved March 22, 2023, from https://www.gdcvault.com/play/1020177/Deformable-Snow-Rendering-in-Batman3