Home Articles Tutorials Resources About

How to simulate water using shaders


By Pieter Germishuys, January 12 2006

Water.

Water overview
Water is a very interesting topic and almost certainly draws anyone?s attention.
I am going to explain a simple yet effective way of rendering semi-realistic water using the bump mapping article I wrote. It?s essential that you follow that tutorial since it shall be the base of this tutorial.

What can we use from our bump mapping tutorial?
So what have we learned from the previous bump mapping tutorial? We have learnt that we are able to give surfaces a raised look and that can come in handy not just for static surfaces but dynamic (movable) surfaces. One such surface is water.

Simple wave effect
A very easy and simplistic way to recreate water is to have 3 textures that scroll over each other with a time lapse. Applying a normal mapping technique to this will give the water a raised feel and looking very good such as in this screenshot.

So all we do is add another variable to our bump mapping shader called time. In the pixel shader we will calculate 3 sets of normals using a time factor that will scroll the normal. You can add as many textures/normals as you like. I just picked 3 since it gives a really nice effect and doesn?t effect the performance of the application that much.

Adding color
I have chosen to use a cube map. A cube map is just a simple texture mapping technique that arranges textures to the faces of a cube. If you can imagine this, you can view 360 degrees if you look around you. To reflect this we have what we call a cube map that will map the entire environment to a single cube. Direct3d provides us with a easy to use texCUBE function that will extract the texture from the cube map.

Reflection
To properly display this cube map in the water we will need to calculate the reflection vector. The reflection vector is merely the change in vectors striking a surface.

So let's review the steps.
1) Get a time value (pass it to the shader)
2) Calculate 3 normals from the normal map using the normal mapping technique. These normals will be used as textures that scroll over each other giving us the
????nice moving effect of water
3) Use a cubemap to reflect the environment. Direct3d provides us with a way to get the reflection vector. reflect() does just that.

The rest is from the bump mapping tutorial

float4x4 ModelViewProj : WORLDVIEWPROJ; //our world view projection matrix
float4x4 ModelViewIT : WORLDVIEWIT; //our inverse transpose matrix
float4x4 ModelWorld : WORLD; //our world matrix
float4 lightPos; //our light position in object space

float4 time; //the time that we will pass to our shader for calculation of the normals

texture texture0; //our texture
texture texture1; //our normal map

sampler2D texSampler0 : TEXUNIT0 = sampler_state
{
?????Texture = (texture0);
????MIPFILTER = LINEAR;
????MAGFILTER = LINEAR;
????MINFILTER = LINEAR;
};
sampler2D texSampler1 : TEXUNIT1 = sampler_state
{
????Texture = (texture1);
???MIPFILTER = LINEAR;
???MAGFILTER = LINEAR;
???MINFILTER = LINEAR;
};

//application to vertex structure
struct a2v
{
?????float4 position : POSITION0;
?????float3 normal : NORMAL;
?????float2 tex0 : TEXCOORD0;
???? float3 tangent : TANGENT;
?????float3 binormal : BINORMAL;
};

//vertex to pixel shader structure
struct v2p
{
?????float4 position : POSITION0;
?????float2 tex0 : TEXCOORD0;
?????float2 tex1 : TEXCOORD1;
?????float3 lightVec : TEXCOORD2;
?????float3 eyeVec : TEXCOORD3;
?????float att : TEXCOORD4;
};

//pixel shader to screen
struct p2f
{
?????float4 color : COLOR0;
};

//VERTEX SHADER
void vs( in a2v IN, out v2p OUT )
{
?????//getting to position to object space
????OUT.position = mul(IN.position, ModelViewProj);

????//getting the position of the vertex in the world
????float4 posWorld = mul(IN.position, ModelWorld);

????//getting vertex -> light vector
????float3 light = normalize(lightPos - posWorld);

????//calculating the binormal and setting the Tangent Binormal and Normal matrix
????float3x3 TBNMatrix = float3x3(IN.tangent, IN.binormal , IN.normal);

????//Passing the eye vector to the pixel shader for use with reflection
????OUT.eyeVec = normalize(mul(TBNMatrix, eye));
????//setting the lightVector
????OUT.lightVec = mul(TBNMatrix, light);

????//calculate the attenuation
????OUT.att = 1/( 1 + ( 0.005 * distance(lightPos.xyz, posWorld) ) );
?
????OUT.tex0 = IN.tex0;
????OUT.tex1 = IN.tex0;
}

//PIXEL SHADER
void ps( in v2p IN, out p2f OUT )
{
????//Calculating 3 sets of normals that will scroll over the surface.
????float3 normal = 2.0f * tex2D(texSampler, (0.5 * IN.tex0) + (time * 0.5)).rgb - 1.0f; //the normal map
????float3 normal1 = 2.0f * tex2D(texSampler, (1 * IN.tex0) + (time * 1)).rgb - 1.0f; //the normal2nd map
????float3 normal2 = 2.0f * tex2D(texSampler, (2 * IN.tex0) + (time * 2)).rgb - 1.0f; //the normal3rd map

????//our eye vector normalized
????float3 eye = normalize(IN.eyeVec);

????//calculating the final normal from all the normals in the above equations.
????//This will be used to batch the diffuse calculation and get us a light intensity.
????float3 finalNormal = normalize(normal + normal1 * 2 + normal2 * 4);

????//getting the reflection vector and a surface to eye vector
????float3 reflectVector = reflect(eye, finalNormal);

?????//calculate the color from the cubemap
????float4 color = texCUBE(cubeSampler, reflectVector);

????//normalize the light
????float3 light = normalize(IN.lightVec);

????//set the output color
????float diffuse = saturate(dot(finalNormal, light));

????//multiply the attenuation with the color
????OUT.color = IN.att * color * diffuse;
}

technique water
{
????pass p0
????{
????????vertexshader = compile vs_1_1 vs();
????????pixelshader = compile ps_2_0 ps();
????}
}

that's it. Easy huh? If you have any queries/comments/suggestions please do not hestitate to contact me.



Files for this tutorial

Filename Size
? Tutorial_Water_vs2003.rar 2.0 MB
? Tutorial_Water_vs2005.rar 2.0 MB
?
MDX info is an initiative by NetForge. All content is copyright ? 2005-2006 by its respective authors | About MDX info | Terms of Use |