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.
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:
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:
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: