Skip to main content

Water Effect

This purpose of this post is to give some insight in to my 2d water effect. The effect  was made in unity and consist of one C# script and two shaders. Additionally this effect can be be broken down in to two parts. The reflection and the distortion. First we will look at the reflection

Reflection

A lot of the work of the reflection is done through the C# script. The script creates a new game object with the sprite renderer of the original object, mirrors it, and puts our water reflection shader on it. You can see it below.

A couple things to note with this technique. First, everything done in this script could be done manually through the editor. It is only for convenience and does not have to be done at run time. You might want to add [ExecuteInEditMode] to this script if you plan on using it. Second, this is kind of a crude way of doing things. Animations, lighting, or anything else that effect your sprite will not get reflected. For animations you could use the script to copy the animator. Then use it as a wrapper for your animator to keep the two animators in sync. That is what I do in the gif below. But if you have a lot going on this could get messy quick. For any more advance scenarios you may be better off using multiple cameras and render textures.

Animation
Now the sprite is reflected but  it will always be there. Independent of water.
Reflection with out our soon to be explained shaders
To make the reflection only show up on water we will use the stencil buffer, We will have to make two separate shaders to do this. This water surface shader and the reflection shader. Both of these shaders will be based off the default unity sprite shader. Any thing that you want the reflection to show up on should use the surface shader. Any thing that you want to show up as a reflection should use the reflection shader.
Add this to the water surface shader

Add this to the water reflection shader

The stencil buffer basically sets a flag when the water surface shader passes. Then when the reflection shader passes it throws out all pixels that don't have the flag set. Resulting in reflections only on top of water. The stencil buffer is a little more complex than that but that is all we need to know for this effect. Finally since the water reflection shader uses the default unity sprite shader we can lower the alpha a bit in the editor to blend it better. Reflection complete!

Distortion

The distortion effect is done completely through the reflection shader. The basic idea is that we sample from different positions of a distortion texture over time to displace the pixels in our sprite texture. The distortion texture could be any thing but since it is a water effect a water normal map works well.
Water Normal Map
In our reflection shader we will add properties for our distortion texture, the scroll speed, and the magnitude of the effect.
We will also want to get world pos in our vertex shader. We will use this for the position to sample our distortion texture so that it will move as the game object does.
Finally, in our fragment shader we will sample from the distortion texture and use it to displace pixels in our sprites texture.
Here is the full code for the surface and reflection shaders

Comments

Post a Comment

Popular posts from this blog

Chromatic Aberration

I made a chromatic aberration shader after not finding one I liked online. You can see the effect above on the sprite bottle to the right. My goal was to make a stylized chromatic aberration effect that I could apply to any sprite.
The effect works by separating the rgb channels and displacing them in different directions. Below you can see the blue and red channels getting displaced in opposite directions on the x axis.  You can control the displacement easily with a script to change how extreme the effect is. I personally think the effect looks best when used subtlety but I could see some cool applications were it is controlled dynamically and combined with a screen shake or something to show a large impact. Here is a link to the shader
Here is an example C# script to control it

Random Map Generator

A lot of games use Perlin Noise to generate maps, so I decided I would give it a try. One of the most popular games to do this is Minecraft which uses a similar noise function to create its map.  The basic premise is that you use the noise to create a height map on a 2d grid. Every point on a grid is assigned a random value and that value is its height. If you where to just use a normal random number function to assign the values they would be all over the place. It wouldn't make any natural looking shapes. That is why Perlin Noise is used. It creates smooth gradients between random points. This make the height changes less jarring and look natural.
 Unity already has a Perlin noise function so I did not have to implement my own. All I did at first was make a 2d array and assign each index a height value. Every height above a cutoff is land and everything below is water, this is the result.

It was a lot easier than I expected to start getting good looking shapes. After that I adde…