Code free interactive water!

Tutorial / 01 February 2019

After talking with our art mentor, Jonathan, our team began looking into adding water to get some of the beautiful reflections back into our level. We have a few options,  decal based puddles, or splat shaders with a height blend. Each of those give us our reflections, but they don’t add a sense of life to the scene. If we can have real time interaction between objects, players and the water, then we can also use the water to sell the time freeze effect!

This is a technique I have wanted to work out for a long time, but I was always missing a sage or two. With a pointer by our tech mentor Andrew I found this tutorial:

https://www.patreon.com/posts/24192529 – by Minions Art

 

He uses a lot of shader code, but If we take that technique and apply it to Unity’s Scriptable Render Pipeline with Shadergraph we can do the entire thing code free.

Step 1: Basic Setup

All we need in the scene are one Particle System (Visual Effects Graph now), Orthographic Camera, Our water and Ground planes, and our Player Object.

A plane for the Water, a plane for the ground, and an orthographic camera.
A plane for the Water, a plane for the ground, and an orthographic camera.

We’ll create a Render Texture, a Material and a PBR Graph.

Assets we create for the water system.

I also created a water ripple texture, you can make them easily in Photoshop/Gimp, here is the one I used for my first attempt.

A particle for water ripples. It is partly transparent and white, so may be hard to see.

Step 2: Shader Setup

The shader is really simple, we can use ShaderGraph to make our water without any code!

The Shadergraph for water interaction.

This node graph has three major parts:

The fist part is to get the water interaction in, we’ll do this by sampling our render texture. Our render texture can be treated like any other texture:

The nodes to bring in our interaction texture.

The first node is just a texture node but turned into a Property. You can find those over in the blackboard:

The blackboard with all of our shader parameters.

With the sample 2D node we want to take only the red channel. This lets us use the other 3 channels (G, B, & A) for other types of interaction (footsteps, tire imprints, craters/impact marks).

The next few nodes are some math to make our water particle more interesting, it is not essential to the shader, I remade the simple gradient for opposite and exaggerated black and white maps. Then by multiplying them we get a more detailed ripple.

The other node group deals with some simple waves:

The nodes to make up some procedural waves.

Game water is often just a water texture scrolling in two directions, for our shader I used two noise textures but you can substitute water textures, a properly flow-mapped water texture or any other method you prefer. The tilling and time nodes are what make the scrolling effect work.

The final set of nodes merges our interaction with our waves as a height map (Black and White) and generates a base colour, and most importantly a normal map. The normal map will let us see the interaction through the reflections on the waters surface!

The node group to get our waves and interaction to mix.

We bring in the interaction texture and add it to the wave texture, then we split it into colour and normal. The colour just lightens our black and white map with a light blue for our water tint, It will be barley noticeable, but it will highlight the waves a bit.

The important part is the ‘Normal form Height’ node. It will take our height map and generate a normal map, essentially converting our elevation (white is high, black is low) to angles (blue is up, red is right/left, green is up/down in the image space). This the works with unity to create nice smooth reflections on the water.

The last two Properties let you control the transparency of the water int he editor! Each one is a Vector 1.

Now we have a functioning water shader, but we need data to interact with.

Step 3: Building the interaction elements

The interaction elements are very simple, you need a particle system and a layer for that system! Then with some camera adjustments we are good to go!

First well add our interaction layer:

The layers dialogue, with our water layer added.

With our WaterInteraction layer added we can create a particle system parented to the player/interacting object.

The ripple particle system in the viewport.

When we create our water particle system we need to make sure it is on the WaterInteraction layer, so that we can use a mask on our cameras to make sure it only shows up in our RenderTexture.

Our interaction particles need to be in the WaterInteraction layer.

Here are the settings for the particle material, we will be using the legacy/Particles/Additive shader that comes with unity. Most shaders are not compatible between default unity and the new Scriptable Render Pipelines, luckily some unlit shaders still work, and the Additive Particle Shader works really well with our particle system controls for tint and opacity.

The first settings for our particle, the red colour lets us isolate water interaction to the red channel in our Render Texture.
The emission settings will make sure that there is always interaction around the player, and that there are enough particles when they move.
By having the smallest possible emission area we can control the particles more accurately.
Over time we want our particles to fade out, a feature uniquer to the additive shader in HDRP. This will clean up any jarring transitions from our ripples to the waves.
Using the size curve we can control how the ripples spread out.
Adding some rotation to the particles can give us a more realistic and random result without needing multiple textures or flip books.
In the renderer we want to make sure we are using our particle material (the one with the legacy additive shader on it) and that our ripples are horizontal billboards. This way they will always be perfectly flat.
The shader settings for our material, the shader is legacy/particles/additive and our ripple texture has been connected.

Finally we need to change our main camera to ignore our water interactions, and our orthographic camera to only see our interactions.

Our main camera can see every layer except the Interaction ones.
Our Interaction camera can only see the interaction layers.

Now we are all set to mix it together!

Step 4: Adding it all together

Add our Render Texture to the output of the interaction/orthographic camera:

The Render Texture should be the target for the camera output.

Then add our shader to our Water material, add the interaction Render Texture to the texture input.

The water material should have the WaterShader and the Interaction Render Texture.

Done!

With those setup your water should be ready! Hit play and walk around a bit to see the interaction take effect!

For more Unity experiments and game art, checkout Portfolio.MattMurch.com!