Saturday, June 19, 2010

More water improvements

I've logged some solid hours programming this weekend, continuing to work on water effects. The water shader has been almost completely rewritten to add such features as specular lighting, normal mapping, waves, and caustic effects.

The first thing that I added was a simple wave function implemented in the vertex shader. The formula is pretty much just y += sin(x)*sin(y). If you were to zoom way out, the resulting wave form would follow a very obvious pattern, but when you're at eye level looking at the shore line it looks pretty realistic. This shader isn't meant to look like waves, but rather to give the water level a little bit of an ebb and flow.

Next, I got specular lighting working. I actually haven't used specular lighting in DirectX 10, and never had to code the lighting calculation myself. Once I got it working, I realized that the water needs texture to make the specular light look right. A flat sheet of water just shows a single dot of specular light, which looks pretty lame. Fortunately, normal mapping is pretty easy do in DirectX 10. It only took about 5 minutes to incorporate this into my shader. I used a normal map of a ripple pattern and overlaid two octaves (two versions of the same texture scaled to different sizes) moving at different rates across the surface. The resulting ripples in the water look infinitely more complex than a single normal map on its own. I'm very satisfied with how realistic the ripple effect came out.

Another simple feature I added was Fresnel alpha blending. If you're on a boat in the middle of a lake and look out toward the horizon, you'll see a reflection on the surface of the water. If you look straight down at the water, you'll see fish/rocks/etc underneath the surface. This is because the amount of light that's transmitted vs. reflected off the surface is dependent on the angle of incidence. The fraction of light that is reflected is the Fresnel term. There are lots of different ways to calculate this term, but the simplest way is to take the dot product of the viewing vector (camera to surface) and the normal vector of the surface. This calculation has already been done to compute specular lighting, which saves time. The Fresnel term is then used to interpolate between the color of the reflected pixel vs. the color of what's underneath.

The last thing I added was a caustic effect superimposed on the terrain underwater. Many examples I've found simply render the pattern to the water surface and call it good. In reality, the pattern is projected onto whatever geometry is at the bottom of the body of water. This requires a bit more work, but I think the added realism pays off. The approach here is similar to how I did the water ripples. Two octaves of the caustic pattern are overlaid and move randomly. It took a little bit of tweaking the scaling and rate of motion of the two textures to get this to look right. You have to look pretty hard to figure out how the pattern is animated.

Viewing the Water from Eye Level


Close Up of Caustic Effect

1 comment: