The problem is that XNA on Windows Phone doesn't have custom shader support - so you can't write a vertex shader or pixel shader. However, you can use a trick described by Catalin Zima that deforms a vertex grid to achieve the same effect.
If you are not targetting Windows Phone 7, you can use a trick that I described on my blog. Copying the relevant bits in:
These distortions require 2 images. Firstly you need the entire scene as a render target (i.e. Texture2D) as well as the distortion render target. Typically you would use a particle system to fill the distortion render target; using special distortion sprites (example below).
Each color component in the distortion target (and distortion sprites) represents the following:
- R: dx: X offset – f(x)=2x-1 mapping ([0.0f, 1.0f] to [-1.0f, 1.0f]).
- G: dy: Y offset – f(x)=2x-1 mapping.
- B: m: Z strength – f(x)=x mapping.
A good example of a sprite that would be used for a ripple would be:
Determining the outcome of a ripple is as simple as adding the waves together (keeping in mind the mapping you need to perform first to [-1.0f, 1.0f]); because waves in reality are also additive this just works - you will get very good approximations of real waves.
Once you have the two render targets you can use the following shader:
Texture InputTexture; // The distortion map.
Texture LastTexture; // The actual rendered scene.
sampler inputTexture = sampler_state
texture = <InputTexture>;
magFilter = POINT;
minFilter = POINT;
mipFilter = POINT;
sampler lastTexture = sampler_state
texture = <LastTexture>;
magFilter = LINEAR;
minFilter = LINEAR;
mipFilter = LINEAR;
addressU = CLAMP;
addressV = CLAMP;
float4 Position : POSITION;
float2 TexCoords : TEXCOORD0;
float4 Distort (VS_OUTPUT Input)
coords = Input.TexCoords;
color1 = tex2D(inputTexture, coords);
// 0.1 seems to work nicely.
mul = (color1.b * 0.1);
coords.x += (color1.r * mul) - mul / 2;
coords.y += (color1.g * mul) - mul / 2;
color2 = tex2D(lastTexture, coords);
float4 RunEffects (VS_OUTPUT Input) : COLOR0
color = Distort(Input);
PixelShader = compile ps_2_0 RunEffects();
This is the final effect:
This technique should also work for 3D games; although you might have to dedicate more thought to the particle shader and the distortion shader.