Saturday, January 7, 2017

Stylized Water for Epitasis Breakdown

Basically since I've shown it off, I've gotten quite a lot of requests to show how I accomplished the water that is found in Epitasis.

It's gone through quite a few different variations and improvements to get where it is now, and honestly it could still be refined. But its current state works quite well, and is close to where I want it to be, so I figured I'd do a detailed breakdown of how you too can also accomplish a similar water system.

The water is essentially broken down into two parts: The material and the blueprint.

The material is exactly what you expect - everything related to rendering the water. In addition to this, you'll also need a lightly tessellated mesh to get the wave effect that you see in many of the GIFs shown of it.

The blueprint is essentially something you can just drag and drop into a level, change its size, and change various material parameters on the fly. This allows maximum flexibility and you don't need to use material instances to get desired effects for each water instance you use in levels (although, it is certainly possible to do it that way if you so choose).

Lets start with the material, as this is the most complex bit.

First and foremost, lets create a master material for our water.

We have quite a few settings to check and mess with inside the material - take a look:

Just most of your basic stuff but we need to enable stuff like tessellation and make sure we have the correct shading model, blend mode, etc.

Here is an overview of the material, and as you can see its big and complex and lots of nodes going everywhere. I'm going to go over each section, starting with the Base Color comment block, and work our way down.

Base Color

The base color section has two subsections, the water color and foam. These two get blended together by using depth fade, as we can find the water edges and use it as an alpha to lerp the water color and foam together.

The water color has two vector variables - Water Light and Water Dark, which blend together using a fresnel (which gets its normal from our normal section). This creates a nice blending effect of two different colors to get a deeper ripple effect. 

The foam section has a vector variable to control the foam color along with two float variables for the scaling of the foam in uv space. They also pan on 0.02x-0.02, to get a nice effect of the foam moving. The texture samples you see are just a nice foam texture which get added together.

Metallic, Specular, and Roughness

Nothing to strange or complex here, only difference is we use the depth fade from the foam fade scaling in the water color block to make sure its not too shiny where the foam is.


Nothing to crazy here, we are essentially using depth fade to control our min and max opacity and multiply it against a sphere mask that uses pixel depth, which helps fade our the water when its right next to the player / camera. This makes it so the water does not just clip into the camera when your halfway above and below it. Its a bit expensive, and thats why there is a quality switch there as well.


We divide a world position offset input by the wave size, which is essentially the scaling of the normal. We mask R and G, use two different panners (gets wave effect when animated against each other) at 0.02x0.03 and -0.04x0.03, then use two different float variables (Small Ripples and Large Ripples) to control scaling. This then gets plugged into two texture samples of our water normal texture and they are then added together. I also have a vector parameter called Normal Intensity this then gets multiplied against, which at times helps control how strong it looks but this is definitely an area that can be improved because it does not always work well.

There is also a blank normal map, which is lerped together from the foam depth fade in the base color. This is because we dont want to use the wave normal for the foam too.

Tessellation Amount

A simple little graph here that plugs into the tessellation multiplier input.

Wave Movement

To get the wave movement, we use absolute world position, divide it by how a float variable of how frequent we want the waves (wave frequency), mask the red channel from it, and add it so our wave speed. We then use a sine node and do some more math, and eventually multiply it against the variable Water Height Surge, which controls how high the waves are.

We then plug this into a quality switch since its fairly taxing and plug that in both the world position offset and world displacement inputs.

We also plug it into a normal input of a new material function we need to create, which controls the subsurface scattering of the material, which we'll go into next.

Subsurface Water Material Function

This function is very complex. I also need to mention I did not actually create this function, and I copied it off the web from TK's Dev Blog. Huge props to him, as this adds in a super awesome effect we wouldn't have otherwise. I'll go over some of the variables here and what they do.

The Light Vector is the vector from your sun position, so we can get the correct lighting for the subsurface. The Scattering Color is just what you expect, the subsurface scattering color, which is multiplied against everything right before the final input.

We then have three float variables, Scattering Color Intensity, Scattering Fade Distance, and Scattering Scale. These are all fairly self explanatory and simply just control how much scattering there is to see and how intense it is.


This is fairly simple as well, but we use depth fade again so we don't refract right at the waters edges. You'll get some bad artifacts if you do in certain cases.


Now that the material is out of the way and created, lets move  onto the blueprint. Its fairly simple, mostly just consisting of a few things we have to do on tick and on construction of the blueprint.

Lets create our water blueprint and add our tessellated mesh to it and apply the master water material to the mesh. We are then going to have to add variables to the blueprint that we have in our material, and make them editable so we can edit them in the editor. Its essentially up to you at this point to add in the variables that you wish to change found in the material. I've personally added them all, for maximum flexibility.

In addition, you will also need a reference to your directional light or whatever vector you want to use for the light vector variable found in the subsurface water material function. Since I have an entire sky blueprint, I use that and grab the sun location.

Construction Script
This thing is huge and I'm not going to go over it all, its fairly self explanatory from the following image:

We essentially use a sequence to update all the parameters in the material thats located on the static mesh plane of the water. Every time we update something in the editor, this will be run and it will be updated. We just use the "Set Scalar / Vector Parameter Value on Materials" nodes to control the parameters (just specify the name of the parameter).

Event Graph

The last thing to do here is to update the light vector parameter on tick. In my case I grab the sky reference, get the sun position, get its world location, and stick in there for the vector.


Now that we're done with the material and blueprint, the only thing left to do is to throw it into a level and start messing with its parameters and seeing the results! This is a big, complex system that still has room for improvement. If you've ever seen the water plugin on the unreal forums, you'll know that water can get very complex.

Some last notes: At one point I used a planar reflection component in the water blueprint, but eventually removed it because it was incredibly taxing (although, the results looked awesome!). If that was ever improved to a point where it would not be taxing I would probably add it in again, but I dont see that happening since how that method of reflection is done. Another note, usually in my environments wherever I have sand and water I'll use a slightly more reflective sand material and paint that around the edges and under the water, which gives the effect of wet sand. Lastly, one thing I do in levels already but need to just add to the blueprint to be universal is a sound dampening and post process volume, so you get a better looking underwater effect.

Cheers! Let me know if you have any questions or comments.


  1. Super cool looking water. I've got a few optimization suggestions:

    Any time you preform some operations on the UV's of a texture, you could move those tasks to the vertex shader using custom UVs. It won't save too much but it will reduce instructions. This explains it pretty well.

    I would also suggest that instead of reading the location of your "sun flare" on a tick you have a separate sky or sun actor that sets a value in a material collection that the water material reads directly. This makes it easy to use and reuse a global "light vector" parameter. You'll also avoid ticking your water, which is always a good CPU optimization.

    1. Thanks man, I'll take a look into these and add them in, they sound useful! Theres still quite a few other optimizations and features I'd like to add in, mostly with the tesselation/waves.