Texture-Driven Water

Quick demo of final results:

After trying my hand at 2D cloth simulations, I wanted to implement a 2D water simulation. I could have tried to implementing a Navier–Stokes shallow water equation, but due to some time constraints, I instead tried to see how far I could drive a water simulation with textures. To make it a bit harder, I gave myself the task of rendering the water with real-time reflections and refraction.

Step 1. Create Water layer

This one was a quick gimme. I used the same code and logic from my cloth simulation to create a plane that represented the surface of the water.

Done.

Step 2. Set up the scene for reflections and refraction in real-time

OK, this one took a bit of research. I know that in order to get some real-time refraction and reflections, modern games use light probes and cube maps as look ups textures. When the ray reflects or refracts off of a surface in the fragment shader, there needs to be a texture that it can look up into the get the resulting color. My problem came in the fact that dynamic scene objects cannot be pre-baked into textures.

To create the texture lookups I needed, I would have to implement dynamic cube maps. This means, during runtime, I’d have to render the scene 6 times to create the frames of the cube map texture, then render the scene once more to get the correct lighting effects in my final pass. Here’s a small snippet of what setup looks like:

snippet_gl_water.PNG
Setting up Camera attributes for renders

Then I preceded to render the scene 6 times for my cube map generation. I set up a framebuffer object and attached the cube map textures as render targets. Then I rendered quads (sized 1024 x 1024 pixels) of the scene (per face of the cube).

Step 3. Calculate generate normal map of moving water

This wasn’t too complicated to accomplish. First I created a normal to be loaded every frame (converting a water texture to normal map using Photoshop and Knald):

Then I animated the normal map in the fragment shader. I used multiple directions to create the illusion of flowing water. Here’s a snippet of that code from my fragment shader:

snippet_gl_water2
Normal map sourcing function from fragment shader

I then combined and normalized the “sourced” normals and used them as my normal direction for my reflection and refraction vectors.

With refraction, I used and IOR ratio of 1.00/1.33 (air to water) and used that in my refraction vector calculation (-2*(V dot N)*N + V). For reflection, I used Schlick’s approximation to compute it’s influence in the final color output (fresnel effect and glancing angle calculated in shader) and set that value (color as a combination of reflections and refractions) as my final output.

Here are some static results:

water_scene_croppedwater_scene_fresnel_01water_scene_fresnel_02

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s