Turbulent Water Shader

Lately I’ve been fascinated by either underwater or space worlds. I decided to make another water shader.

This time I went for a more turbulent effect. Everything was done in Shader Graph with Unity’s default plane mesh. It supports refraction, water depth, waves and more.

This effect has 5 main components: depth fog, foam, normal scrolling, reflection + refraction and vertex displacement.

Depth fog generates a fog effect when looking through the material, which generates a color that is combined with the final albedo.

Foam generates on the water when it is close to or has intersection with other geometry, its visibility depending on said distance. This is also added to the final albedo.

A common trick for water shading is to use more than 1 scrolling normal map on the surface with different directions and speeds. This distorts much more realistically. That’s what this section does. It samples both normal textures and outputs them to the normal input on the master node. It’s also used as an input for the reflections and refractions to distort those more realistically as well.

This section generates and adds reflections to the final albedo. Using the normal result of the previous section, we distort screen UV coordinates which we pass into the Scene Color (which is simply the color of the scene behind the material, generated as a usable texture by Unity automatically). We combine this with the actual reflections, which are generated in a Sub Graph called Simple Reflection:

Using an LOD (Level Of Detail) and an Index Of Reflection (IOR), we can generate reflections. Aside from Reflection Probe support which I also added in, this is normally not available for Shader Graph. At least not for the version I was using while making this effect. That’s why we use a Custom Function node to gain access to manual shader code. In here we pass in the required arguments for the refract() function and we get back reflected color values.

To generate waves, we have to manipulate the surface’s vertices. That’s what this section does in a Sub Graph called Wave Displacement:

The x and z dimensions are used as UV coordinates for scrolling gradient noise. This noise is then used to generate the y offset that we need. Then everything is combined and linked to the output.

The caustics are done using a custom render pass, inspired by Unity’s Boat Attack URP demo. You can find the repo here!