Article : Realistic Soft Edged Water In XNA
This looks good, but there isnt much detail in the reflection. Let’s quickly add a skybox to the scene.
Download this cubemap: Link
Add it to your Content project.
Right click on your project and choose New>New Item>Code File, and call it Skybox.cs.
Paste the following code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace LensFlare
{
class Skybox
{
private VertexDeclaration VertexDeclaration;
private VertexBuffer VertexBuffer;
private IndexBuffer IndexBuffer;
private TextureCube Environment;
private Effect Shader;
private GraphicsDevice Device;
public Skybox(GraphicsDevice device, TextureCube environment, Effect shader)
{
this.Environment = environment;
this.Shader = shader;
this.Device = device;
this.VertexDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements);
this.VertexBuffer = new VertexBuffer(device, typeof(VertexPositionColor), 8, BufferUsage.WriteOnly);
this.IndexBuffer = new IndexBuffer(device, 36 * sizeof(ushort), BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
VertexPositionColor[] vertices = new VertexPositionColor[8];
vertices[0].Position = new Vector3(-1, 1, 1);
vertices[1].Position = new Vector3(1, 1, 1);
vertices[2].Position = new Vector3(1, 1, -1);
vertices[3].Position = new Vector3(-1, 1, -1);
vertices[4].Position = new Vector3(-1, -1, 1);
vertices[5].Position = new Vector3(1, -1, 1);
vertices[6].Position = new Vector3(1, -1, -1);
vertices[7].Position = new Vector3(-1, -1, -1);
this.VertexBuffer.SetData(vertices, 0, 8);
vertices = null;
ushort[] indices = new ushort[36];
// top
indices[0] = 0; indices[1] = 1; indices[2] = 3;
indices[3] = 2; indices[4] = 3; indices[5] = 1;
//bottom
indices[6] = 5; indices[7] = 4; indices[8] = 6;
indices[9] = 7; indices[10] = 6; indices[11] = 4;
// front
indices[12] = 3; indices[13] = 2; indices[14] = 7;
indices[15] = 6; indices[16] = 7; indices[17] = 2;
// back
indices[18] = 1; indices[19] = 0; indices[20] = 4;
indices[21] = 4; indices[22] = 5; indices[23] = 1;
// left
indices[24] = 0; indices[25] = 3; indices[26] = 4;
indices[27] = 4; indices[28] = 3; indices[29] = 7;
// right
indices[30] = 2; indices[31] = 1; indices[32] = 6;
indices[33] = 5; indices[34] = 6; indices[35] = 1;
this.IndexBuffer.SetData(indices, 0, 36);
indices = null;
}
~Skybox()
{
this.VertexDeclaration.Dispose();
this.VertexDeclaration = null;
this.VertexBuffer.Dispose();
this.VertexBuffer = null;
this.IndexBuffer.Dispose();
this.IndexBuffer = null;
this.Device = null;
this.Environment.Dispose();
this.Environment = null;
this.Shader.Dispose();
this.Shader = null;
}
public void Draw(Matrix view, Matrix projection)
{
view.Translation = Vector3.Zero;
Matrix viewProjection = view*projection;
this.Device.VertexDeclaration = this.VertexDeclaration;
this.Device.Vertices[0].SetSource(this.VertexBuffer, 0, VertexPositionColor.SizeInBytes);
this.Device.Indices = this.IndexBuffer;
RenderState state = Device.RenderState;
state.DepthBufferEnable = false;
state.DepthBufferWriteEnable = false;
state.CullMode = CullMode.None;
state.AlphaBlendEnable = false;
state.AlphaTestEnable = false;
this.Device.Textures[0] = this.Environment;
this.Shader.Begin();
this.Shader.Parameters["ViewProjection"].SetValue(viewProjection);
this.Shader.Techniques[0].Passes[0].Begin();
this.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
this.Shader.Techniques[0].Passes[0].End();
this.Shader.End();
state.DepthBufferEnable = true;
state.DepthBufferWriteEnable = true;
state.CullMode = CullMode.CullCounterClockwiseFace;
}
}
}
We also need a shader for it. Right click the content project and add a new Effect called Skybox.fx. Fill it with the following shader:
samplerCUBE SkySampler : register(s0);
float4x4 ViewProjection;
void VS(in float4 position : POSITION0, out float4 pos : POSITION0, out float3 texCoord : TEXCOORD0)
{
pos = mul(float4(position.xyz, 1), ViewProjection).xyww;
texCoord = position.xyz;
}
float4 PS(in float3 pos : TEXCOORD0) : COLOR0
{
float3 sample = pow(texCUBE(SkySampler, normalize(pos)), 1.2);
float3 result = pow(sample, 10) * 10;
result += sample * 4;
result *= 0.25f;
return float4(result, 1);
}
technique Sky
{
pass pass1
{
VertexShader = compile vs_2_0 VS();
PixelShader = compile ps_2_0 PS();
}
}
We are getting close to the final result, we just need to create and draw this skybox. Create a new field in your game class:
private Skybox Sky;
Initialize it at the end of the LoadContent method:
this.Sky = new Skybox(GraphicsDevice, Content.Load("Miramar"), Content.Load("Skybox"));
And render it into the reflection and backbuffers. In DrawReflection() right before the DrawTerrain call, add:
this.Sky.Draw(reflectionView, reflectionProjection);
Then in the Draw() method, before the final call to DrawTerrain(), add:
this.Sky.Draw(view, projection);
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 ‘)’