2d Refraction Shader example for SpriteBatch.

The other day i was helping a guy porting over a 2d refraction shader in a post and i thought it would be fun to see what else i could come up with.
So i figured id post the shader i made as a little demo with a bunch of different takes on refraction shading and mix it up with other stuff it might help someone else trying to do it.
Plus its a example for how you can use pixel shaders with spritebatch.

If you try it of course you’ll have to use your own textures and spritefont you only need two textures for the shader found at the bottom and attached were labeled in the comments in game1 all all can be replaced with your own images of course.

The two images you need to recreate the shown image are at the bottom of the post.
Credit goes to charles humphrey for the xna image i used it’s a very good image for the example.

The shader It’s pretty big but i tried to make it clearly labeled and separated.
All the shaders can be called by technique name from the single game draw method that uses them on any texture. The game1 example here draws them all to the screen.

Edit fixed a bug in the limited refraction shader and changed the RefractionVectorRange to be correct in pixels.

//
// 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
#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();
    }
}

the game1

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

namespace RefractionShader
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;

        Effect refractionEffect;

        Texture2D tex2dForground;
        Texture2D tex2dRefractionTexture;

        Texture2D tex2dXnaUkImage;
        Texture2D tex2dcool02;
        Texture2D tex2dEgyptianDesert;
        Texture2D tex2dfire01;
        Texture2D tex2dbackground_mat_rgo;
        Texture2D tex2dpgfkp;
        Texture2D tex2dwater04;
        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;
        }

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

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

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

            // extra images for testing.
            tex2dXnaUkImage = Content.Load<Texture2D>("XnaUkImage");
            tex2dcool02 = Content.Load<Texture2D>("cool02");
            tex2dEgyptianDesert = Content.Load<Texture2D>("EgyptianDesert");
            tex2dfire01 = Content.Load<Texture2D>("fire01");
            tex2dpgfkp = Content.Load<Texture2D>("pgfkp");
            tex2dRefractionImg = Content.Load<Texture2D>("RefactionTexture");
            tex2dwater04 = Content.Load<Texture2D>("water04");
            tex2dbackground_mat_rgo = Content.Load<Texture2D>("background_mat_rgo");

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

        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 = tex2dEgyptianDesert;
                            break;
                        case 2:
                            tex2dForground = tex2dwater04;
                            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["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();
            }

        }
    }

credit to charles for the image.
.

Well i hope this helps someone.

3 Likes

Can I have a credit for the images lol

hello charles it is michael from denmark i am back
i have been verry il for a long time 3 years hard transplant

hope everything is well i am just finish my game for android Flipster
Michael

1 Like

Hi Michael, I thought I recognised your engine post :slight_smile:

I am good, sorry to hear you have been so ill, hope you are well on the road to recovery :slight_smile:

I really like the look that the reflect shader in 2d gives off i was playing around with this some more and refining it. So today i took a little time to do another one this time a Cone version that sprays a distortion outward into the drawn texture from a point.

I turned up the refraction really high to make the effect stand out.

The green areas are refracting the blue is stopping the refraction that otherwise would occur.

Here is the shader (in rough draft form still) if anyone is interested.

//__________________________________________________
// (Tech) Refraction cone jet.
// Requisite shader variables.
// Vec2's : ForceLocation ForceDirectionNormal DisplacementLookupScrollOffset 
// float's  : ForceDistance SampleWavelength Frequency AngularRangeDegrees
//__________________________________________________
float4 PsMyTest(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // Take the refraction vector position and spray towards the ForceDirectionNormal limited by the force distance and angle.

    // Determine distance from the current pixel to the Refract position, the distance acts as the strength of the warp.
    float dist = saturate(distance(position.xy, ForceLocation) / ForceDistance);

    // we need two normals here for comparison.
    float2 vectorDirectionOfForce = normalize(ForceDirectionNormal);
    float2 vectorToPixelFromForceOrigin= normalize(position.xy - ForceLocation);

    // Two vector normals that we can dot.

    // Compare the angles we could get a negative from the dot which would be worthless to us. 
    // So for negative stuff well set it to zero via saturate or clamp.
    float angleFromJet = saturate(dot(vectorDirectionOfForce, vectorToPixelFromForceOrigin));
    // We want the acosine instead of the cos so that we can work with it in linear space, such that if 45 degrees is now .5f then 90 can scale to 1.0 linearly.
    float acosAngularDist = angleFromJet * angleFromJet;
    // Calculate the linear ratio of the limiting angle passed in.
    float linearAngle = 1.0f - clamp(AngularRangeDegrees, 0.0f, 90.0f) / 90.0f;
    // Reduce the angular range and do a linear renormalization on the ratio so the strength doesn't fall off. 
    linearAngle = saturate((acosAngularDist - linearAngle) / (1.0f - linearAngle));
    // combine the distance and anglular distance
    float intensity = linearAngle * (1.0f - dist);

    // standard stuff we have done before.
    float warpedCoords = (tex2D(DisplacementSampler, texCoord * SampleWavelength + DisplacementLookupScrollOffset).xy - float2(0.5f, 0.5f)) * Frequency;
    float2 lerpedCoords = intensity * warpedCoords + texCoord;
    float4 col = tex2D(TextureSampler, saturate(lerpedCoords)) * color;
    


    // Visually highlight effective range.
    col.g += intensity;

    // Visually highlight the max range.
    if (dist > 0.99f && dist < 1.0f)
        col.rgb = 1.0f;

    // visually highlight the force direction ray 
    if (linearAngle > 0.9999f)
        col.r = 1.0f;

    // visually highlight the force location.
    if (distance(position.xy, (ForceLocation)) < 5.0f)
       col.b = 1.0f;


    return col;
}

<img src="/uploads/default/original/2X/4/45c9be5fbfaff9922c6afe419fdaf46e9a56e47a.gif" width=“291” =“253”>

mixing a bunch of the ones i worked on into a chain
the static orange monogame logo becomes this.

tanks man for the clouds in my engine and everything is great now

the engine runs on apple tv box the new one a12 processor with a little lag so I am ready for game making did you know that you can buy xbox controller from apple and it works with apple tv box

the new game console over 200 million sold and that is good for monogame

take care
Michael

Welcome

I wouldn’t take that game1 draw method (i didn’t want to have a ton of methods for testing) or the code structure of the shader itself to be well done. It is just a testing example for the shaders i gave it about a minutes thought and slopped it together and most of it has already been changed drastically in my version. Even the shader variables in just the last couple days since i posted this have been renamed and changed up as some of the names are flat out misleading.

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();
    }
}