Main | Z-up shader »

Water Shader

This one is hella complex.  It's a water shader with a multitude of options including sun spots (for outside water) cheap depth and then more complex (but better looking) depth, vertex movement and paintable water foam.  I would like to say that I have not tweaked this with final textures so it may not be perfect yet, all I know is that all the features currently work...

I apologize if this is hard to follow at times, it was tricky to write.


The depth options were scavenged from:   courtesy of Hywel Thomas

Here's an overview of the whole network.  At 140 instructions base it's quite hefty, with ALL the options turned on it ends up being 182 which is quite expensive, but water normally is.  I shall begin dissecting this one with the Foam Normals.

So the first nodes placed deals with the tiling of the foam texture.  The parameter "water bubbles tiling a" and "b" work with an object radius node to alter the UV's of the foam.  This means that the tiling amount will stay consistent no matter how much you scale the water mesh.  You set the tiling amount in the material instance and then never worry about texel issues again.  These UV changes go into a panner who's time input has time>sine multiplied by a parameter that governs the choppiness of the water.  The first sine has an integer of 12 and the second one of 7 so that the two foam textures are offset in their movement slightly.  This whole chunk of nodes then goes into the UV's of the foam normal map (also a texture parameter for easy switching).  The bottom and top foams are then added together (the param switch is there in case you dont want to spend the extra instructions on the subtle under foam).  The added foams then get clamped with a min of 0 and max of 1 (you can skip the multiply and vector 3, coming back to the shader I don't know why I put it there).  Lastly the clamped foam normals are put into a Normalize node and are ready to be sent off to the rest of the shader.

This chunk governs the actual distortion of the water and starts off the same as the foam, with object radius/param controlling UV's and a parameter multiplied by time (in the time slot of the panner) controlling water choppiness.  The panning nodes here are sending the textures in opposite directions.  The panners then influence the water normals, one for each direction and are then added together, softened by being multiplied by the base normal color and then normalized (by the normalize node).  The normalize node then goes off to control the reflections.  The Multiply node just before it however, is used as the top layer in a lerp between the foam chunk (from before) and the current water normals.  The alpha channel is the red channel in a vertex color node allowing the blending between each by painting the verts of a mesh.  The switch param controls whether or not the foam has a normal map which is then masked (R,G channels) to be used for a depth biased alpha input to fade out the normal maps.  Lastly, the normalize node for this chunk and the foam chunk are lerped together with a red channel vertex color for the alpha input, the lerp then goes into a switch param (in case you want to turn off foam altogether) and this then goes into the normal input for the shader itself.

This chunk governs the reflectivity of the foam and it's color. It is the exact same chunk that governs reflectivity and color for the actual water.  The dot product at the bottom of the shader is where the Normalize node at the end of Foam normals is plugged in (create a duplicate of this whole chain and plug the normalize node at the end of the "normal map distortion" into the duplicated Dot product for the water version).  

Start with a camera vector plugged into the a node of th dot product accepting the Normalize node from "Foam Normals".  This then goes into the abs node and is then inverted by a 1-x node.  This in turn is the base for a power node with an exponent constant of 2.  This is then inverted again and plugged into the b input of a multiply node.

The A input of that multiply node comes from this:  Start off with a reflection vector and mask out the red and green channels.  Multiply by 0.5 and then add to the same .5 constant.  The multiply (accepting the mask and .5) is then used to manipulate the UV's of an environment cube map. This cube map is multiplied by the param "angle Reflect color" which (as the name suggests) changes the color of the reflection.  The add node from before goes into a duplicate of the Cube map texture's UV input and is then added with the cubemap having it's color changed.  Now that we have the angle of the reflection and it's color we multiply that with the camera vector chain that ended with the 1-x node that we made at the beginning of this chunk.

The last part of this chunk governs the color of the water/foam.   The parameter "foam ref" determines the overall reflectivity of the foam and is multiplied by the multiply end node of the last two paragraphs.  Another vector param is then used to determine the color of the water but is toned down being multiplied by a .2 constant and then added to the multiply that the "foam ref" goes into.  

All of this is plugged into an add node whose second input is the add from the duplicate of this chunk of code (the one governing water color if this is your foam node).  This add then goes into the A slot of a Lerp, the B node of which is the final add ) of the water reflection the alpha of which is the red channel of a vertex color node (I mentioned this earlier).  This lerp then goes into a switch param allowing to turn off entirely (not just the normal map) the foam.  The True of the switch is the lerp blending between the foam reflection and the water reflection, the False input being just the add of the water reflection (be sure it's not the add node combining both water and foam reflections or you will be trying to figure out why it's not working right ;)

This chunk blends the normal maps of the foam and water for the distortion.  The normalize from the foam is masked (R,G) and multiplied by a constant 2 vector which becomes the alpha for a depth biased alpha node (with a bias of 20).  This is then the A input for a lerp, the B input of which is the depth biased alpha node from the "normal map distortion" chunk (4th image in this post).  The alpha for the lerp is a noise texture (with it's tiling increased) multiplied by a parameter so it can be adjusted.  The noise texture itself gives the blend between the water and foam an irregular soft edge.  Finally, the lerp is then the B input for another lerp, the A input of which is the depth biased alpha from the "Normal map distortion" chunk and the alpha is a red channel vertex color being both inputs for a depth biased alpha node.  This whole chunk then goes into a switch param to turn on and off the foam (named the same as the other foam switches), the true input being the lerp from thich chunk and the false being the depth biased alpha from the end of the "normal map distortion" chunk.


This chunk is a duplicate of the "foam normals" chunk.  Instead of normal maps however, it's the diffuse texture for the foam.  This chunk is the A slot for a Lerp I will cover in a little bit.

This chunk is from Hourences ( and creates small dots of reflection across the surface, and is great for outdoor water.  It's just panning textures governed by the Object radius to manipulate their UV's.

This chunk is a cheaper version of the depth options I scrounged from the link at the start.  It uses the camera to determine how to soften the edges of the water where meshes intersect and gives the water a little depth.  This is the false input for a switch param between the two depth options.

I like the water to actually move, so I threw the wiggle chunk in there too :). See the earlier post for an explanation of this chunk.

The final chunk combines all the reflection chunks into a working diffuse.  The lerp on the far right is the Lerp between the Water reflection and the foam reflection.  This goes into a switch param to turn off foam, the lerp between the two is the true slot, the false input being just the add from the water reflection and color.

The foam normal switch (which we just covered) is the B input for a lerp, the A input of which is the foam movement chunk (that governs the movement of it's diffuse) and the alpha input is a chain of nodes that moves a noise texture out of sync with the foam for that added bit of randomization.  This will mean that even if the water is 100% foam, there will be little patches of the water poking through being masked out by this noise alpha.

The add node going into the "turn on sun dots" true input combines the final add from the water reflection with the final multiply from the sun spots.  The false input for the sun spots switch is just the final add from the water reflection.

The static switch for the sun spots and the Lerp for the foam alpha go into a last Lerp.  The spots for input A and the previous lerp into input B.  The alpha is blended nicely with vertex colors (red) and the lerp then goes into the Diffuse input for the whole texture.

The final material instance should look something like this.  I've tweaked the numbers as well to get something I like.


That's the water shader, and boy is it a complicated one, so I wouldn't be surprised if people have questions or trouble following this post.  If you want to go a step further and add a THIRD overlaying texture, for things like leaves, newspapers and other detritus it would be relatively simple but probably push the shader into 200 instructions.

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>