Making Interactive Water using RenderTexture

Interactive water is something I've wanted for a while, and I had a version that uses object position and radius before (so no trails), but then Edward showed off the RenderTexture + particles method and it's so much better. So right away I adjusted my old interactive water shader to use this technique instead.

How does it work?

The quick explanation is that an orthographic camera is outputting a rendertexture while only rendering particles. In this post I'll show the full setup for achieving this effect.

Here we go!

Setting up the water

Water shader with UV distortion

Start by grabbing the watershader from the Caustics Tip (The textures you need are also there!, also a link to explain how the water shader works)

  • Create a new shader, rename it, and drop the code of the Simple Water Shader in there.
  • Make a new Material from this Shader

Material settings as seen on the plane in the gif above

  • Drop the Material on a new Plane object

Time to make a something to contain the water so it looks a bit more sensible

  • Add some stretched cubes with a gray material so we have a pool
  • Add a sphere in a nice bright color, this will be our interactor

The pool is set up, edgefoam is showing, but no movement effects yet :(

Adding the particles

What we're going for

  • Add a particle system as a child of the Interactor sphere
  • Set it to Prewarm,
    Start Lifetime
    between 2 and 3,
    Start Speed
    to 0,
    Start Size
    between 6 and 8,
    Start Color
    to Blue (make sure there is no red or green),
    and Simulation Space to World
  • Change the shape to Edge, with a very small Radius (0.1)

At this point, it should just spread blue sprites where you move it during Play mode

Particle Material
Instead of blue sprites it should looks more like a ripple

I made this black circle with a white ring (there is transparency when you save the image, Patreon just doesn't show it)

  • Create a new Material set to Particles/AlphaBlended and add the above texture
  • In the Renderer section of the Particle System;
    Set Render Mode to Horizontal Billboard,
    Set Material to the new AlphaBlended Ripple Material,
    and set Sort Mode to Youngest in Front

Gradient in Color over Lifetime

  • Turn on Color over Lifetime and create a gradient of blue that blends from and to 0 alpha

Curve for Size over Lifetime

  • Turn on Size over Lifetime and make a curve that starts growing quick and then gradually gets higher

Almost there! 

Last thing we need to change is the emission

  • Set the Rate over Time to between 1 and 2
  • Set the Rate over Distance to 4

Rate over distance gives a nice smooth trail, particles are done!

The Camera and RenderTexture

  • Create a new Custom RenderTexture,
    Mine is size 1024 x 1024, but you can probably go lower,
    Set to ARGB Half,
    Enable Mipmaps for smoother effect
  • Create a new Camera thats pointing down;
    Clear Flags to Solid Color
    Background to Black 0 Alpha,
    set to Orthographic,
    Size 15,
    and Target Texture is your newly created RenderTexture

It should look like this now

Rendertexture result

Looking at the RenderTexture, it's also rendering the pool and the water, so we have to set the particles on their own layer, and let the camera only render that layer.

New layer for the interaction effects

  • Select your ripple particles and add a new layer
  • Set the particles to this layer
  • In your Orthographic Camera Culling Mask, only select this new layer
  • In your Main Camera Culling Mask, deselect this layer

Now the RenderTexture is set up correctly for the shader

RenderTexture to Shader

Now everything is set up, it's time to send info to the water shader.

To correctly project the effect, there are 3 things that need to be sent to the shader via Shader.SetGlobalx;
The position of the camera, the size of the orthographic camera, and the RenderTexture.

  • Add a new script to the Orthographic Camera (Link to script for convenience)
  • Send the data to shaders;
    In Awake:
      "Shader.SetGlobalTexture("_GlobalEffectRT", rt);"  
    In Update, after moving with the target interactor:
      "Shader.SetGlobalVector("_Position", transform.position);" 
  • Set the interactor sphere as target, and the RenderTexture as RT

RenderTexture and Interactor setup on the Camera

Now we can finally go to the shader.

  • Add the Properties we just sent to the Shaders via script, before void vert:

To create the right UV for the rendertexture to be projected on , we need to do some calculations, This image here explains the steps, if you want to know what it's doing:

Use this UV to project the Texture, only the Blue channel is used in this shader

If you return this "ripples" float together with the water color, you can already see the ripples

Lacks some style, but we are almost done

We only need to fix 2 things now, make it a nice, solid ripple, and distort the texture

First make the ripple clearer, by using the step function (also multiplied the ripples so they show up stronger)

That's the ripples done, now the distortion:

Just add the ripples value to the distortion of the texture:

And there we go, it's distorting! Interactive water done :D

Link to final water shader: PasteBin Link
Render texture has a mask added to prevent bleeding when you're moving too far

Add some rigidbodies for extra fun

The same effect can also be applied to the Caustic shader I made earlier this month, here, I did it for you :D

Shader code for interactive Caustics: PasteBin Link 

Also worth checking out: PDF by Loic on how to use this technique for bending grass  (@cayou66)

Thanks for reading, and have fun!!

Tier Benefits
Recent Posts