Article : Realistic Soft Edged Water In XNA
The reflection looks nice, but the edges are all blue. What we need is the refraction texture, which is set up in much the same way as the scene depth, except we do not need our own shader.
Add a third render target to the game class:
private RenderTarget2D sceneColor;
Again, initialize it in LoadContent(), but instead of using the screen width and height, use half that size since we do not need as much detail.
sceneColor = new RenderTarget2D(GraphicsDevice, width / 2, height / 2, 1, SurfaceFormat.Color, RenderTargetUsage.DiscardContents);
Now we can draw to it, place this before the DrawReflection() call:
GraphicsDevice.SetRenderTarget(0, this.sceneColor); GraphicsDevice.Clear(ClearOptions.DepthBuffer | ClearOptions.Target, Color.CornflowerBlue, 1f, 0); this.DrawTerrain(null, ref view, ref projection);
Also, just like reflection, we need to pass the texture to the PlanarWater Draw method. Change the PlanarWater Draw method to:
public void Draw(GraphicsDevice device, Texture2D sceneDepth, Texture2D waterReflection, Texture2D sceneColor,
Matrix waterViewProjection, Matrix view, Matrix projection)
{
device.VertexDeclaration = this.PlaneVD;
device.Vertices[0].SetSource(this.PlaneVB, 0, VertexPositionTexture.SizeInBytes);
device.Indices = null;
device.Textures[0] = sceneDepth;
device.Textures[1] = waterReflection;
device.Textures[2] = sceneColor;
for (int i = 0; i < 3; i++)
{
device.SamplerStates[i].AddressU = TextureAddressMode.Clamp;
device.SamplerStates[i].AddressV = TextureAddressMode.Clamp;
}
device.SamplerStates[0].MinFilter = TextureFilter.Point;
device.SamplerStates[0].MagFilter = TextureFilter.Point;
device.SamplerStates[0].MipFilter = TextureFilter.Point;
RenderState renderState = device.RenderState;
renderState.DepthBufferEnable = true;
renderState.AlphaTestEnable = false;
renderState.AlphaBlendEnable = false;
this.WaterEffect.Begin();
this.WaterEffect.Parameters["World"].SetValue(PlanarWater.World);
this.WaterEffect.Parameters["View"].SetValue(view);
this.WaterEffect.Parameters["Projection"].SetValue(projection);
this.WaterEffect.Parameters["WaterViewProjection"].SetValue(waterViewProjection);
this.WaterEffect.Techniques[0].Passes[0].Begin();
const int primitiveCount = 2 * (TesselationN1*TesselationN1);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, primitiveCount);
this.WaterEffect.Techniques[0].Passes[0].End();
this.WaterEffect.End();
device.Textures[0] = null;
device.Textures[1] = null;
device.Textures[2] = null;
for (int i = 0; i < 3; i++)
{
device.SamplerStates[i].AddressU = TextureAddressMode.Wrap;
device.SamplerStates[i].AddressV = TextureAddressMode.Wrap;
}
device.SamplerStates[0].MinFilter = TextureFilter.Linear;
device.SamplerStates[0].MagFilter = TextureFilter.Linear;
device.SamplerStates[0].MipFilter = TextureFilter.Linear;
}
…and update the call to it:
this.water.Draw(graphics.GraphicsDevice, this.sceneDepth.GetTexture(), this.waterReflection.GetTexture(), this.sceneColor.GetTexture(), reflectionViewProjection, view, projection);
Now we are ready to use it in the shader. Add a third sampler for refraction in water.fx:
sampler2D RefractionSampler : register(s2);
Now we can read the refraction texture instead of using a blue color.
Change:
float3 refraction = float3(0, 0, 1);
To:
float3 refraction = tex2D(RefractionSampler, screenTexCoords).rgb;
May 6th, 2010 - 20:22
Finally, a subject on just water and not necessarily ‘adding’ water for Xna.
I am going to try this out as my goal is to just do water itself in Xna, for now.
May 20th, 2010 - 17:52
This is very similar to the technique I use to render water as a post processing pass. In my implementation I render geometry into the stencil buffer and output linear view space depth. Then run a full screen quad over the results of that to produce the water in the back-buffer with stencil rejection enabled.
July 13th, 2010 - 00:52
Hey,
is there a way you could re-upload the sample and images for this tutorial? Would be really nice.
BTW this is just the tutorial i’ve been looking for for months … thank you soooo much
July 14th, 2010 - 17:26
Existence – Fixed the images and sample download.
July 21st, 2010 - 17:32
Hey Sarge,
The pixel shader broke when I ran this under Xna 3.0. Band-aid solution: saved the pixel shader to a static variable before executing begin command with the Effect, then set it back once it went to null. Is there a quick fix to get around the ravages of Microsoft progress?
Best,
Dave
July 22nd, 2010 - 13:06
Nota bene: my previous fix didn’t really work. If you receive the debugging message about not having a valid vertex or pixel shader, then you will probably have to set the pixel shader device version to 2 in the .fx file, instead of 3.
July 22nd, 2010 - 14:47
This sample doesnt seem to work for me it causes problems with the for loops:
for (int x = 0; x < Tesselation-1; x++)
it underlines the ‘;’ after -1 and the ‘)’