Phill Johnson, Game Level Designer in Dallas, TX

Essentials

Professional Experience: 4 Years in Technical Art, Design & Instruction
Engines: Unity3D, Unreal Engine, id Tech 4, Flash
Software: Photoshop, Maya, 3DSMax, Modo, ZBrush, CrazyBump, Flash
Programming: JavaScript, Python, Objective-C, ActionScript 3.0

Tutorial

Compelling water in real time is always difficult. Fortunately, with current game engines we can achieve more advanced visual effects. To start off, this tutorial was inspired largely by Alex Vlachos of Valve Software's Siggraph 2010 presentation on the subject. That said, this tutorial focuses on recreating the effect in a useful manner through the UDK. I've seen this effect on YouTube or reels, and haven't seen a good tutorial on it. So, here goes. :)

Step 1: Freescaled Textures

Water surfaces can vary wildly in size. That said, we don't want to create a material instance controller for every puddle, or have to have wild UVs in order to make it work. So, our first step is to apply a texture that will automatically map to the surface based on the size of the mesh.

For this, we will be using a basic, subdivided plane. Texture-wise, it has one shell that completely fills the UV space. We'll go with a square because it's easy to work with, but you can proportionately fill this space with a planar projection.

We can accomplish this by multiplying the TextureCoordinates against the ObjectRadius.




Step 2: Set Up Panning & Reset

Next, we have to tell the material to move the texture. One of the difficulties with flow maps is that the pixels follow a set path, which over time can lead to coherency issues with the texture (it can become unrecognizable if it flows for more than a few seconds). So, to dodge this we're going to set a total length of the animation. For this example, we'll go with 3.0 seconds.

We can do this by taking the time of the simulation (the Time expression) floating modulus three. Modulus is basically long division, but the answer is the floating point remainder (example: 3 fmod 5.5 would be 2.5) Floating Modulus is represented with the Fmod expression.

Go ahead and set the Panner's X and Y speed to something like 0.2, the texture should be panning up and to the left. If you want to change the overall speed of the flow later on, you'll want to adjust these values. Once this is set up, it should pop back once every three seconds. This is what we want!



Step 3: Texture/Vertex to Mod Pan Time (This is the flow part!)

Now that we have a set time frame to animate against, it's finally time to define how our texture should flow and warp. We can do this with an actual texture, or we can use the vertex colors (mesh paint mode). For this example, we'll create a texture.

Knowing that most 3D programs align R,G,B to X,Y,Z (look at the color of the widget's arrows in the UDK) and textures are two-dimensional, the flow map needs to have the important information stored on the R and G channels. You don't have to do it this way, but I've found it's helpful both visually and cognitively to do so.

You can create your influence texture in Photoshop, or any program you're familiar with. While most image editors store color information on a 0-255 scale, it will be mapped to a 0-1.0 scale in the UDK. This texture was created with a background of 0.5 Red and 0.5 Green, making a barfy yellow (I believe this is what Pantone calls it, but don't quote me on it).

We will be multiplying one channel against time, and then the next channel through a second panner. Essentially, we're panning vertically, and then horizontally. We must use two separate panners because the Time input only accepts a single channel.

That said, we're using the influence texture to affect the speed of the water multiplied against time. At this stage, 0.0 (black) multiplied against time is always zero, so any black portions of the influence texture will never move or flow, but 1.0 (white) will move at full speed, and 0.5 (neutral gray) moves at half-speed. Using a flow map allows us to control this on a per-pixel basis.



Step 4: Lerp to Hide The Reset

The hard reset is a bit jarring, so now we're going to hide it. Create a new Linear Interpolation expression with the flow as input A, a Constant3 (any value, really) for B. For the Alpha, you'll want the cosine of Time, so it will osculate between black and the flow. Set the cosine's period to match the length of the animation: 3.0 seconds. With just the cosine, you shouldn't see the reset directly anymore. However, there is a problem: cosine osculates from a value of -1 to 1. We only really care about the positive values as a negative alpha is useless to us. So, add 1.0 to the cosine, and divide by 2. Feed that result into the alpha of the Lerp expression.



Now we're 90% of the way there! Next, we need to replace the Constant3 with an out of phase duplicate of the original animation. Control+Alt+LeftDrag to draw a selection box, or Control+Click to select the relevant expressions. Copy and paste them, and move the selected expressions out of the way. Connect the new texture-sample to the B of the Lerp. If done correctly, it should seem like the lerp isn't doing anything at all at this point. To fix this, you will want to find the Time expression of the second animation, and add 1.5 (or 50% of your animation length) to it before passing it through the Fmod.



At this point, your material should never visibly pop back to normal.

Step 5: Finalization

It looks pretty good now, but the flow seems to pulse slightly. This is because every 1.5 seconds, we start to see the unwarped image. We can solve this by pre-warping it slightly at the beginning of each cycle. To line up with this animation, add 1.5 to the Fmod of both animations before passing to the Time of the first panner. This will visually advance the animation, making the reset less evident.

You may also want the fluid to flow in reverse at some locations. You can do this by defining your idle point. Often times, you will want this to be pukey yellow (0.5, 0.5), where anything below that will flow in reverse. You can do this by subtracting 0.5 from your flow map, pushing anything less than 0.5 into the negative range. Keep in mind that flowing water doesn't sit still at any given point, but if you want to create a thicker, viscous fluid (gels, lava, etc) this is a good way to do it.

You can use the same UV animation sections and alpha input from the previous maps to apply a normal, specular, or distortion that matches the flow.

Now that you've gotten to this point, you may want to convert portions of this shader into material functions, so you can reuse them later on. You may also want to relink self-similar portions of this shader to make changes a little easier (or, make it into a material instance for quicker tweaks). I have not done this for this tutorial, as the exploded view helps for understanding.

For mastery of the concept, try mapping a whitewater or undercurrent to layer the flow. Some of the best examples I've seen use two to three layers of current to emphasize the fluid look. Finally, you might want to try breaking up the flow even more by applying a noise texture to modulate time. This will help alleviate any visual tiling, as outlined in the Valve Software Siggraph presentation on the subject.

You can push this as far as you like. :)

Notes:

I've seen a few different methods for creating this effect, and I've seen a lot of people asking how this is done when they see a video on YouTube for it, so I'm including a UDK file made in the 2011-08 build. In it, is a mesh with the material applied at each step, including the texture swap and tweaks to use the flow in a water example.

Download the sample map and material at each step, requires the August 2011 UDK build or later.

I hope this helps!



Projects that were done on grants, or for pay, are the intellectual property of the respective client or institution. See the credits page for more information.