2d Refraction Shader example for SpriteBatch.

Woops amendment Dx with Hi Def profile version.

I should note the mouse Y is reversed again for dx.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;

namespace RefractExampleDx
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;

        Effect refractionEffect;

        Texture2D tex2dForground;
        Texture2D tex2dRefractionTexture;

        Texture2D tex2dXnaUkImage;
        Texture2D tex2dMgLogo;
        Texture2D tex2dRefractionImg;

        // some default control values for the refractions.
        float speed = .02f;
        float waveLength = .08f;
        float frequency = .2f;
        float refractiveIndex = 1.3f;
        Vector2 refractionVector = new Vector2(0f, 0f);
        float refractionVectorRange = 100;

        // To alter the size and position on the screen of all the image squares.
        // Change the w h values.
        public int StW { get; set; }
        public int StH { get; set; }

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1200;
            graphics.PreferredBackBufferHeight = 800;
            Window.AllowUserResizing = true;
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
            StW = graphics.PreferredBackBufferWidth / 4;
            StH = graphics.PreferredBackBufferHeight / 3;

            graphics.GraphicsProfile = GraphicsProfile.HiDef; // << profile hi def DX
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>("MgGenFont");

            //Reqisite images.
            tex2dRefractionImg = Content.Load<Texture2D>("RefactionTexture");
            tex2dXnaUkImage = Content.Load<Texture2D>("XnaUkImage");

            // extra images for testing.
            tex2dMgLogo = Content.Load<Texture2D>("MG_Logo_Small_exCanvs");

            // the primary two textures used for all the tests.
            tex2dForground = tex2dXnaUkImage;
            tex2dRefractionTexture = tex2dRefractionImg;

            refractionEffect = Content.Load<Effect>("RefractShader");
            //refractionEffect = Content.Load<Effect>("RefractEffects");
        }

        protected override void UnloadContent()
        {
        }

        float pauseTimer = 1.0f;
        float pauseDelay = .5f;
        int choice = 0;

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            if (pauseTimer > 0f)
                pauseTimer = pauseTimer - (float)gameTime.ElapsedGameTime.TotalSeconds;
            else
            {
                if (Keyboard.GetState().IsKeyDown(Keys.Space))
                {
                    choice++;
                    if (choice > 3)
                        choice = 0;
                    switch (choice)
                    {
                        case 1:
                            tex2dForground = tex2dXnaUkImage;
                            break;
                        case 2:
                            tex2dForground = tex2dMgLogo;
                            break;
                        //case 3:
                        //    tex2dForground = tex2dfire01;
                        //    break;
                        default:
                            tex2dForground = tex2dXnaUkImage;
                            break;
                    }
                    pauseTimer = pauseDelay;
                }
                if (Keyboard.GetState().IsKeyDown(Keys.Up))
                    waveLength += .001f;
                if (Keyboard.GetState().IsKeyDown(Keys.Down))
                    waveLength -= .001f;
                if (Keyboard.GetState().IsKeyDown(Keys.Right))
                    frequency += .001f;
                if (Keyboard.GetState().IsKeyDown(Keys.Left))
                    frequency -= .001f;
            }


            refractionVector = Mouse.GetState().Position.ToVector2();
            refractionVector.Y = graphics.GraphicsDevice.Viewport.Bounds.Size.ToVector2().Y - refractionVector.Y;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // Draw the images well be using.

            // row 1  far left we squeeze the regular two images in here that we use.

            spriteBatch.Begin();
            spriteBatch.Draw(tex2dRefractionTexture, new Rectangle(StW * 0, StH * 0, StW, StH / 2), Color.White);
            spriteBatch.Draw(tex2dForground, new Rectangle(StW * 0, StH / 2, StW, StH / 2), Color.White);
            spriteBatch.DrawString(font, "wavelength " + waveLength + "\nfrequency " + frequency, new Vector2(10, 10), Color.Wheat);
            spriteBatch.DrawString(font, "Mouse: " + refractionVector, new Vector2(10, 60), Color.Moccasin);
            spriteBatch.End();

            // row 2 and 3 far left
            Draw2dRefractionTechnique("RefractMonoCromeClipDarkness", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 0, StH * 1, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);
            Draw2dRefractionTechnique("RefractDiagnalAverageMonochrome", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 0, StH * 2, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);

            // row1

            Draw2dRefractionTechnique("Refraction2", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 1, StH * 0, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);
            Draw2dRefractionTechnique("Refraction2", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 2, StH * 0, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), true, gameTime);
            Draw2dRefractionTechnique("RefractionSnells", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 3, StH * 0, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);

            // row 2

            Draw2dRefractionTechnique("RefractionMap", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 1, StH * 1, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);
            Draw2dRefractionTechnique("RefractionMap", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 2, StH * 1, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), true, gameTime);
            Draw2dRefractionTechnique("TwoPassTechnique", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 3, StH * 1, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);

            // row 3 

            Draw2dRefractionTechnique("RefractGoldenRatioHighlight", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 1, StH * 2, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);
            Draw2dRefractionTechnique("RefractAntiRefractionArea", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 2, StH * 2, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), false, gameTime);
            Draw2dRefractionTechnique("LimitedRefractionArea", tex2dForground, tex2dRefractionTexture, new Rectangle(StW * 3, StH * 2, StW, StH), speed, refractiveIndex, frequency, waveLength, refractionVector, refractionVectorRange, new Vector2(10, 1), true, gameTime);

            // Draw to the whole screen for testing.
            //Draw2dRefractionTechnique("RefractAntiRefractionArea", tex2dForground, tex2dRefractionTexture, new Rectangle(0, 0, StW * 4, StH * 3), speed, refractiveIndex, frequency, waveLength, new Vector2(10, 1), true, gameTime);

            base.Draw(gameTime);
        }

        /// <summary>
        /// Draw a refracted texture using the refraction technique.
        /// </summary>
        public void Draw2dRefractionTechnique(string technique, Texture2D texture, Texture2D displacementTexture, Rectangle screenRectangle, float refractionSpeed, float refractiveIndex, float frequency, float sampleWavelength, Vector2 refractionVector, float refractionVectorRange, Vector2 windDirection, bool useWind, GameTime gameTime)
        {
            Vector2 displacement;
            double time = gameTime.TotalGameTime.TotalSeconds * refractionSpeed;
            if (useWind)
                displacement = -(Vector2.Normalize(windDirection) * (float)time);
            else
                displacement = new Vector2((float)Math.Cos(time), (float)Math.Sin(time));

            // Set an effect parameter to make the displacement texture scroll in a giant circle.
            refractionEffect.CurrentTechnique = refractionEffect.Techniques[technique];
            refractionEffect.Parameters["Texture"].SetValue(texture);                              // << Requisite for Dx
            refractionEffect.Parameters["DisplacementTexture"].SetValue(displacementTexture);
            refractionEffect.Parameters["DisplacementMotionVector"].SetValue(displacement);
            refractionEffect.Parameters["SampleWavelength"].SetValue(sampleWavelength);
            refractionEffect.Parameters["Frequency"].SetValue(frequency);
            refractionEffect.Parameters["RefractiveIndex"].SetValue(refractiveIndex);
            // for the very last little test.
            refractionEffect.Parameters["RefractionVector"].SetValue(refractionVector);
            refractionEffect.Parameters["RefractionVectorRange"].SetValue(refractionVectorRange);


            spriteBatch.Begin(0, null, null, null, null, refractionEffect);
            spriteBatch.Draw(texture, screenRectangle, Color.White);
            spriteBatch.End();

            DisplayName(screenRectangle, technique, useWind);
        }

        public void DisplayName(Rectangle screenRectangle, string technique, bool useWind)
        {
            spriteBatch.Begin();
            var offset = screenRectangle;
            offset.Location += new Point(20, 20);
            spriteBatch.DrawString(font, technique, offset.Location.ToVector2(), Color.White);
            if (useWind)
            {
                offset.Location += new Point(0, 30);
                spriteBatch.DrawString(font, "wind on", offset.Location.ToVector2(), Color.White);
            }
            spriteBatch.End();
        }

    }
}

Shader

//
// 2d Refraction related shaders
//
#if OPENGL
#define SV_POSITION POSITION
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
//#define VS_SHADERMODEL vs_4_0_level_9_1
//#define PS_SHADERMODEL ps_4_0_level_9_1
#define VS_SHADERMODEL vs_4_0
#define PS_SHADERMODEL ps_4_0
#endif

//-----------------------------------------------------------------------------
//
// Requisite variable and texture samplers
//
//-----------------------------------------------------------------------------

// This vector should be in motion in order to achieve the desired effect.
float2 DisplacementMotionVector;
float SampleWavelength;
float Frequency; // .51  .3
float RefractiveIndex;
// one more little test here.
float2 RefractionVector;
float RefractionVectorRange;

Texture2D Texture : register(t0);
sampler TextureSampler : register(s0)
{
    Texture = (Texture);
};

Texture2D DisplacementTexture;
sampler2D DisplacementSampler = sampler_state
{
    magfilter = linear;
    minfilter = linear;
    AddressU = Wrap;
    AddressV = Wrap;
    Texture = <DisplacementTexture>;
};

//-----------------------------------------------------------------------------
//
// Requisite functions.
//
//-----------------------------------------------------------------------------


// function reflect map pixel grabber my version
float4 FuncMyRefractMap(float4 color, float2 texCoord)
{
    // the SampleWavelength grabs a smaller or larger area of texture coordinates.
    // the motion moves the uvs over the refraction map to give the feeling of motion.
    // the Frequency reduces the manitude of the range the coordinates are grabed from.
    texCoord += (tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementMotionVector).xy - float2(0.5f, 0.5f)) * Frequency;
    return tex2D(TextureSampler, texCoord) * color;
}

// function reflect map pixel grabber the ported version.
float4 FuncGuysRefractMap(float4 color, float2 texCoord)
{
    texCoord += tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementMotionVector) * 0.2 - 0.15;
    return tex2D(TextureSampler, texCoord) * color;
}

// function reflect map pixel grabber hlsl snells
float4 FuncHlslRefractMap(float2 ray, float4 color, float2 texCoord)
{
    float2 normalValue = abs(normalize(tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementMotionVector).rg));
    texCoord = texCoord + refract(ray, normalValue, RefractiveIndex); //* Amplitude;
    // Look up into the main textur's pixel and return.
    return tex2D(TextureSampler, texCoord) * color;
}

// function monochrome
float4 FuncMonoChrome(float4 col)
{
    col.rgb = (col.r + col.g + col.b) / 3.0f;
    return col;
}

// function outline
float4 FuncDiagnalAvg(float4 col, float2 texCoord)
{
    col -= tex2D(TextureSampler, texCoord.xy - 0.003) * 2.7f;
    col += tex2D(TextureSampler, texCoord.xy + 0.003) * 2.7f;
    return col;
}

// function outline
float4 FuncDiagnalAvgMonochrome(float4 col, float2 texCoord)
{
    col -= tex2D(TextureSampler, texCoord.xy - 0.003) * 2.7f;
    col += tex2D(TextureSampler, texCoord.xy + 0.003) * 2.7f;
    col.rgb = (col.r + col.g + col.b) / 3.0f;
    return col;
}

// function highlight golden ratio steped.
float4 FuncHighlight(float4 col, float2 texCoord)
{
    // try to brighten on blue.
    float2 temp = texCoord;
    float dist = 0.01f;
    float gldr = 1.6f;
    temp += float2(dist * gldr, 0.00f);
    float4 col01 = tex2D(TextureSampler, temp);
    temp = texCoord;
    temp += float2(0.00f, dist * gldr * 2.0f);
    float4 col03 = tex2D(TextureSampler, temp);
    temp = texCoord;
    temp += float2(-dist * gldr * 3.0f, 0.00f);
    float4 col02 = tex2D(TextureSampler, temp);
    temp = texCoord;
    temp += float2(0.00f, -dist * gldr * 4.0f);
    float4 col04 = tex2D(TextureSampler, temp);

    float4 tempCol = ((col01 + col02 + col03 + col04) - col) / 2.0f;
    tempCol.bg = col.bg;

    col.rgb = saturate(col.rgb * tempCol.rgb);

    return col;
}



//-----------------------------------------------------------------------------
//
// Requisite Shaders.
//
//-----------------------------------------------------------------------------



//__________________________________________________
// (Tech)  refraction map shader mine
//__________________________________________________

float4 PsRefractionMap(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = FuncMyRefractMap(color, texCoord.rg);
    return col;
}

//__________________________________________________
// (Tech) refraction map shader guys
//__________________________________________________

float4 PsRefraction2(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = FuncGuysRefractMap(color, texCoord.rg);
    return col;
}

//__________________________________________________
// (Tech) RefractDiagnalAverageMonochrome
//__________________________________________________

float4 PsDiagnalAverageMonochromeShader(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = FuncMyRefractMap(color, texCoord.rg);
    col = FuncDiagnalAvgMonochrome(col, texCoord.rg);
    return col;
}

//__________________________________________________
// (Tech)  the golden ratio spiral highlight
//__________________________________________________

float4 PsRefractGoldenRatioHighlight(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = FuncMyRefractMap(color, texCoord.rg);
    col = FuncHighlight(col, texCoord.rg);
    return col;
}

//__________________________________________________
// (Tech) RefractMonoCromeClipDarkness
//__________________________________________________

float4 PsRefractMonoCromeClipDarkness(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = FuncMyRefractMap(color, texCoord.rg);
    col = FuncMonoChrome(col);
    clip(col.r - 0.4f);
    return col;
}

//__________________________________________________
// (Tech) RefractAntiRefractionArea  
// this prevents a area around the point from being refracted
//__________________________________________________

float4 PsRefractAntiRefractionArea(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // Determine distance to the anti Refract position.
    float dist = saturate(distance(position.xy, RefractionVector) / RefractionVectorRange);
    float2 warpedCoords = texCoord + (tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementMotionVector).xy - float2(0.5f, 0.5f)) * Frequency;
    float2 lerpedCoords = (warpedCoords - texCoord) * (dist * dist) + texCoord;
    float4 col = tex2D(TextureSampler, saturate(lerpedCoords)) * color;
    // Visually highlight effect range.
    //col.b += (1.0f - dist) * (1.0f - dist);
    //col.r += (dist) * (dist);
    return col;
}

//__________________________________________________
// (Tech) LimitedRefractionArea  
// this only creates a warped inverse refraction area around the point
//__________________________________________________
 
float4 PsLimitedRefractionArea(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // Determine distance to the anti Refract position.
    float dist = saturate(distance(position.xy, RefractionVector) / RefractionVectorRange);
    float warpedCoords = (tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementMotionVector).xy - float2(0.5f, 0.5f)) * Frequency;
    float2 lerpedCoords = (warpedCoords) * (1.0f - dist) * (1.0f - dist) + texCoord;
    float4 col = tex2D(TextureSampler, saturate(lerpedCoords)) * color;
    // Visually highlight effect range.
    //col.r += (1.0f - dist) * (1.0f - dist);
    //col.b += (dist) * (dist);
    return col;
}

//__________________________________________________
// (Tech) TwoPassTechnique  uses mine modified.
// this is for the double pass test shader.
//__________________________________________________

float4 PsSecondaryShader(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // the frequency grabs a smaller area of texture coordinates the motion moves the uvs over the refraction map.
    float2 coordRange = texCoord * SampleWavelength + DisplacementMotionVector;
    // brightspots and color change well add this last.
    float2 brightspots = abs(float2(sin(coordRange.x), cos(coordRange.y)));
    // we get the texels from the refraction map and shrink grab range.
    texCoord += (tex2D(DisplacementSampler, coordRange).rg - float2(0.5f, 0.5f)) * Frequency;
    // Look up into the main texture.
    float4 col = tex2D(TextureSampler, saturate(texCoord)) * color * float4(brightspots.x, brightspots.y, 1.0f, 0.5f);

    return col;
}

//__________________________________________________
// Unfortunately i cant seem to figure out how to do the 2d one.
// the 3d one seems pretty simple.
// but as it is this ones busted.
//
// (Tech) refraction hlsl call using snells law.
//__________________________________________________

float4 PsRefractionSnells(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float2 ray = float2(0.5f, .5f); //  float2(0.707106f, 0.707106f);
    float4 col = FuncHlslRefractMap(ray, color, texCoord.rg);
    return col;
}



//-----------------------------------------------------------------------------
//
// Requisite Techniques.
//
//-----------------------------------------------------------------------------


technique Refraction2
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefraction2(); // ps_2_0  doesn't error either.
    }
}

technique RefractionMap
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractionMap();
    }
}

technique RefractDiagnalAverageMonochrome
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsDiagnalAverageMonochromeShader();
    }
}

technique RefractGoldenRatioHighlight
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractGoldenRatioHighlight();
    }
}

technique RefractMonoCromeClipDarkness
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractMonoCromeClipDarkness();
    }
}

technique RefractAntiRefractionArea
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractAntiRefractionArea();
    }
}

technique LimitedRefractionArea
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsLimitedRefractionArea();
    }
}

technique RefractionSnells
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractionSnells();
    }
}
technique TwoPassTechnique
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL
        PsRefractionMap();
    }
    pass Pass1
    {
        PixelShader = compile PS_SHADERMODEL
        PsSecondaryShader();
    }
}