How can I achieve an effect like this using MonoGame?

I’ve been wanting to do this for a long time.

You know, warping/transforming sprites, wave effects, stuff like that. I just don’t know if it’s possible to do this through MonoGame.

These are absolutely possible!

I don’t have a computer right now to set up examples, but google for monogame or XNA distortion shader

I have done things like heat-waves, wavy flags, skewing and such with simple things like stencil-rects. Its not as cool as the stuff in the video, but it is crisp and smooth.

You have anything you want on a render-target.
You have a wave generator (something that spins) that spits out, say 16 different x or y values per frame…

In your draw cycle, draw each line or row of your rendertarget (using a pixel-thin stencil rect) either to a draw pos + alternating wave values, or have the stencil rect move with the wave values.

For a full screen heat wave, this is 1080 draw-calls in a for loop, but I have done it in a game with all sorts of other action happening, and it was fine.

With a seperate class set up to handle this, you can throw any image through, and have it stretch to fit any banner or area of the screen…

Umm… I have no idea what any of this means or how to do it… @monopalle

Okay, well I can understand the concept, but I don’t know how to implement/code it

I can post an example if you want? Pretty sure I have a project that only does that one thing.

Yes please! I’d appreciate it

ok, here is first a custom class… Its basically just a basic sprite that can update its position…
public class Line
{
public Rectangle my_from;
public Rectangle my_destination;
public Vector2 x_finder;

    public Line(Rectangle start)
    {
        my_destination = start;

    }

    public void Update()
    {


        x_finder = Game1.RotateVector2(x_finder,0.04f,Vector2.Zero);

        my_destination.X = (int)x_finder.X;

        
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Game1.sand, my_destination, my_from, /*new Color(my_destination.X*20,100,my_destination.Y/4)*/Color.White  *1.0f, 0, Vector2.Zero, SpriteEffects.None, 0);
    }
}

Here is a texture you can use:

and here is the main class, game1

public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        graphics.PreferredBackBufferHeight = 1080;
        graphics.PreferredBackBufferWidth = 1920;
    }


    protected override void Initialize()
    {

        base.Initialize();
    }

    public static Texture2D sand;

    Vector2 x_finder;
    List<Line> lines;

    protected override void LoadContent()
    {
        sand = Content.Load<Texture2D>(".\\sand");

        spriteBatch = new SpriteBatch(GraphicsDevice);

        lines    = new List<Line>();

        //this vector is rotated around vector.zero, so we get an x value that waves up and down...
        // CHANGE this value to determine how far and fast the waves move
        x_finder = new Vector2(-50,0);

        //here we add a rectangle for each line of image we want wobbled.
        //the rectangle will be modified dynamically by the x_finder above.

        for (int i = 0; i < 1080; i++)
        {
            lines.Add(new Line(new Rectangle(0, i, 1920, 1)));              
            x_finder = (RotateVector2(x_finder, 0.02f, Vector2.Zero));
            lines[i].my_destination = new Rectangle(  (int)x_finder.X , lines[i].my_destination.Y, lines[i].my_destination.Width, lines[i].my_destination.Height);
            lines[i].x_finder += x_finder;
            lines[i].my_from = (new Rectangle(0,i,1920,1));
        }

    }

    //THIS method just orbits a vector around another vector, here we are using it to generate wave patterns.
    public static Vector2 RotateVector2(Vector2 point, float radians, Vector2 pivot)
    {
        float cosRadians = (float)Math.Cos(radians);
        float sinRadians = (float)Math.Sin(radians);

        Vector2 translatedPoint = new Vector2();
        translatedPoint.X = point.X - pivot.X;
        translatedPoint.Y = point.Y - pivot.Y;

        Vector2 rotatedPoint = new Vector2();
        rotatedPoint.X = translatedPoint.X * cosRadians - translatedPoint.Y * sinRadians + pivot.X;
        rotatedPoint.Y = translatedPoint.X * sinRadians + translatedPoint.Y * cosRadians + pivot.Y;

        return rotatedPoint;
    }


    protected override void UnloadContent()
    {
    }



    protected override void Update(GameTime gameTime)
    {
        foreach (Line line in lines)
        {
            line.Update();
        }

        base.Update(gameTime);
    }



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

        spriteBatch.Begin();
      //  spriteBatch.Draw(sand,Vector2.Zero,Color.White);
        foreach (Line line in lines)
        {
            line.Draw(spriteBatch);
        }

        spriteBatch.End();
        // TODO: Add your drawing code here

        base.Draw(gameTime);
    }
}
1 Like

Thats all there is to it…

For best results with heat waves, I think draw the image first, then draw the wave effect with a lowered alpha on top… looks more distorted.

For something like a flag, turn the alpha to 1, and dont draw the original image underneath…

Oh my god, it works. I gotta start playing around with this. Thank you so much. How can I thank you?

I’m just glad to help… I get a lot of help here myself :slight_smile:

So yeah, that effect does the whole screen, you can of-coarse do smaller areas, and other effects…

1 Like