Pages

Thursday, November 13, 2008

Water Game Component

I had some requests awhile back for a more independent water effect that I had provided in my camera animation tutorials. And just the other day I remembered that I had totally forgot about this (doh!). So here is a DrawableGameComponent for the water effect. Have a look here to see it in action.

To setup the component we need to fill out a WaterOptions object that will be passed to the water component.

So here will fill out various options such as width and height, cell spacing, the normal map asset names, etc. We then create the water component, and assign it the options object. We then provide the filename of the Water.fx shader, the water's position and we then assign its RenderObjects delegate a function that will be used to draw the objects in your scene.

The component tries to be relatively independent of how your represent your game objects. All that it asks for is that you provide a function that takes a reflection matrix. This function should go through the objects that you want to be reflected/refracted and combine the reflection matrix with the object's world matrix.

mWaterMesh.RenderObjects is the delegate that has the signature of: public delegate void RenderObjects(Matrix reflectionMatrix);

Basically this function should just go through your game objects and render them.

Lastly, before you draw your objects in the scene, you need to send to the water component the ViewProjection matrix and the camera's position by using WaterMesh.SetCamera(). And you need to call WaterMesh.UpdateWaterMaps() to update the reflection and refraction maps. After this, you can clear your framebuffer and draw your objects. For how this effect looks you can take a look at my camera animation tutorials.Water GameComponent:

namespace WaterSample{//delegate that the water component to call to render the objects in the scenepublic delegate void RenderObjects(Matrix reflectionMatrix);

/// <summary>/// Options that must be passed to the water component before Initialization/// </summary>public class WaterOptions{//width and height must be of the form 2^n + 1public int Width = 257;public int Height = 257;public float CellSpacing = .5f;

public float WaveMapScale = 1.0f;

public int RenderTargetSize = 512;

//offsets for the texcoords of the wave maps updated every framepublic Vector2 WaveMapOffset0;public Vector2 WaveMapOffset1;

/// <summary>/// Drawable game component for water rendering. Renders the scene to reflection and refraction/// maps that are projected onto the water plane and are distorted based on two scrolling normal/// maps./// </summary>public class Water : DrawableGameComponent{#region Fieldsprivate RenderObjects mDrawFunc;

//scrolling normal maps that we will use as a//a normal for the water plane in the shaderprivate Texture mWaveMap0;private Texture mWaveMap1;

//user specified options to configure the water objectprivate WaterOptions mOptions;

//tells the water object if it needs to update the refraction//map itself or not. Since refraction just needs the scene drawn//regularly, we can:// --Draw the objects we want refracted// --Resolve the back buffer and send it to the water// --Skip computing the refraction map in the water objectprivate bool mGrabRefractionFromFB = false;

/// <summary>/// Options to configure the water. Must be set before/// the water is initialized. Should be set immediately/// following the instantiation of the object./// </summary>public WaterOptions Options{get { return mOptions; }set { mOptions = value; }}

//set the parameters that shouldn't change.//Some of these might need to change every once in awhile,//move them to updateEffectParams if you need that functionality.if (mEffect != null){ mEffect.Parameters["WaveMap0"].SetValue(mWaveMap0); mEffect.Parameters["WaveMap1"].SetValue(mWaveMap1);

/*------------------------------------------------------------------------------------------* Render to the Refraction Map*/

//if the application is going to send us the refraction map//exit early. The refraction map must be given to the water component//before it rendersif (mGrabRefractionFromFB){ GraphicsDevice.ClipPlanes[0].IsEnabled = false;return;}

//if we're below the water line, don't perform clipping.//this allows us to see the distorted objects from under the waterif (mViewPos.Y < mWorld.Translation.Y){ GraphicsDevice.ClipPlanes[0].IsEnabled = false;}

// We first build the grid geometry centered about the origin and on// the xz-plane, row-by-row and in a top-down fashion. We then translate// the grid vertices so that they are centered about the specified// parameter 'center'.

//Water effect shader that uses reflection and refraction maps projected onto the water.//These maps are distorted based on the two scrolling normal maps.

float4x4 World;float4x4 WorldViewProj;

float4 WaterColor;float3 SunDirection;float4 SunColor;float SunFactor; //the intensity of the sun specular term.float SunPower; //how shiny we want the sun specular term on the water to be.float3 EyePos;

All of your XNA samples look great! This is the first one where I've taken the time to play with it on my own and the component integrated well. I'm kind of a novice, so I was wondering, what is the benefit to using more than two triangles for water?

I've been intrigued into using this as a GameComponent in my project, but i'm having a hard time having it render. Anyway you could possibly spare some time to throw it in a super simple project to show how its used as a GameComponent? The simpler the better. Thanks!

I was having trouble setting upthe "DrawObjects" function in that I wasn't sure how to pull out DrawableGameComponents from the component collection as meshes. Makes sense now though. Thanks for sharing your knowledge. I appreciate it. Your blog is chocked full of good stuff.

I really like this GameComponent design strategy, it really keeps your code clean and organized and makes for extremely re-usable and adaptable components.

I do get the feeling that quite a few people use drawable game components. I've tried to stay away from them in the past articles since it facilitates copying and pasting of code; and not facilitating understanding of what's going on. On the other hand they do allow more easy integration and community sharing of code/methods because they are pluggable. Maybe I will start to use them more often.

I've had quite a few requests to put the water effect into a drawable game component, and Ziggy and the Xna blog picked up the post within a few days of me posting it(even though I had this in my camera animation tutorials for months).

The Fresnel effect is the observance that the amount of reflectance you see on a surface depends on the viewing angle. When the angle is small there is high reflectance, when the angle is large (like when you're looking down into the water), the reflectance is low.

The Fresnel term is calculated to blend between reflection and refraction.

The equation for the fresnel term is:

F = R_0 + (1 - R_0) + (1 - theta)^5

where,

R_0 = (1 - R_i)^2 / (1 + R_i)^2

where, R_i is the refractive index. The refractive index for clear water is 1.3333f. So we precompute R_0 and it is declared at the top of the shader.

Also, once the Fresnel term is calculated, I also add in reflectance based on the height of the camera. Here, .007f is just some arbitrary value I chose that produced pretty good visual results. This makes sure that the water slowly turns to total reflection as the camera height rises.

Finally, we make sure that the final fresnel value is not greater than 1 by taking the min() value.

Hi, I'm building a World Class with different Camera and Terrain Classes, Can I use your Code "with full credits to you" and produce other modified effects like lavaflow(pure refraction) and dirty water etc???

Of course you and anyone else that wants the shaers will be able to get them at https://sourceforge.net/projects/gamedevstoolbox , there is nothing posted at the moment because I want to get something working together first but i'll send you a message on msn when I mod your component with the full source.

Yes the reason is because in the demo, I didn't clip the refracted objects to the water plane. Usually this isn't too important, and most games don't do a refraction pass anyway (they just render the scene to a texture and use it).

So you need to setup a clip plane similar to the reflection rendering.

Hi Kyle - thanks a lot for making this available. I had a bit of trouble with it initially, as my game uses the Z axis as Up instead of Y.

Setting the World matrix of the water mostly worked, but there were a couple of places in the code and the shader where you did a Y axis check for above/below water. Once I changed this to Z, everything was fine. Probably a better thing to do would be to do an actual axis check so that it would work for any transform.

Other than that, it was super-easy to integrate and looks great! If you are interested, you can see some shots of my game here.

Hi! I could havе sworn I've been to this blog before but after reading through some of the post I realized it's new to me.Nonetheleѕs, I'm definitely delighted I found it and I'll be bοokmarking and cheсking baсk frequently!Here is my weblog - Earnonlinesite.net