Sgt. Conker We are "absolutely fine"

3Nov/097

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;

About CorporalX

Professional XNA/.NET developer.
Comments (7) Trackbacks (0)
  1. 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.

  2. 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.

  3. 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 :D

  4. Existence – Fixed the images and sample download.

  5. 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

  6. 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.

  7. 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 ‘)’


Leave a comment


*

No trackbacks yet.