Echo here. I haven’t posted anything graphicsy in awhile, so I thought I’d share some details on our water shader. We’ve been exploring many different potential environments for our upcoming title, from swampy wastes to bustling port cities. The common denominator though, is always water. Since water will be omnipresent, it’s pretty important that we make it look good. This short video shows a quick flythrough demonstrating the current state of our water technology.
First – if you’ve got a good eye you may have noticed a few things missing. I haven’t implemented reflections yet, but we’re planning on having them. Also, the specular highlight in the water doesn’t line up with the position of the sun in the sky. Clouds and sky objects will be getting a lot of love soon, but for now we’re still using a placeholder skybox.
The water shader is very loosely based on the NVIDIA water demo from a few years back. Alex Vlachos from Valve also has an excellent paper on how to create a convincing flowing water effect. This is perfect for us since we’re planning on having a lot of small islands and canals, which would impact the flow of the water. Notice in the video how the water breaks and flows around the island, with a quiet patch on the far side.
I’ve found that combining coarse and fine waves together makes them look much more dynamic. Also, the flowing water effect works great, but it can suffer from repeating patterns which can be hard to break up. Because of this, the fine waves follow the water flow map, while the coarse waves flow along a fixed vector. The coarse waves are softened as they diverge from the flow map, which gives us a full range of water turbulence. The foam and white caps add the finishing touches on the effect. Foam comes from a noise map, and strengthens as the water gets less turbulent. The white caps are based off of the slope of the coarse wave, highlighting the peaks.
I also wanted the shoreline to be soft, since a harsh line between water and land really breaks the effect. Typically, this is achieved by performing a depth-based alpha blend when rendering water over the lit and shadowed scene. This works okay with a forward renderer, but our game uses a fully deferred renderer and I want our water lit and shadowed accurately. Also, a straight-up alpha blend isn’t entirely appropriate in this situation. Even if the water is translucent, the light traveling through the water volume impacts the final color in ways that can’t be represented by a simple blend. To solve both problems, we render water as part of the deferred pipeline. Relevant gbuffer channels are copied prior to rendering water and are sampled, distorted, and blended within the water shader, which gives us full control over how we want to blend each individual material property.
The order of operations ends up looking like this:
-Render solid objects.
-Copy gbuffer diffuse, gloss, and normals.
-Stencil out the water volume and sky.
-Render ambient occlusion, using stencil to ignore water and sky.
-Render water to the gbuffer.
-Compute scene shadows.
And that’s about it. Our water shader, like everything, is a work in progress but I’m pretty happy with where it’s heading.