Shader that works in XNA 4.0 is broken in MonoGame

vOutput, do you mean the structure passed from the vertex shader to the pixel shader?

I don’t think it will, you can pack that with what ever you like in the vertex shader (if you write your own). But here with a SpriteBatch Draw call, I think it will return the regular stuff as shown in my example. It may include normals in there too, but for what you need, these three should be fine. The GPU pipeline will match the type and channel to the structure we give as best it can (think this is called vertex patching) though thinking about it, I think this describes what’s done when the pipeline is trying to match the vertex stream we pass with the intended vertex structure we describe in our shader. This is digging a little deeper than we need to here though lol

Glad it helped you out :smiley:

When I was messing around with it, I ended up with this:

float4 PixelShaderFunction(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 coords: TEXCOORD0) : COLOR0

So I thought maybe the vsOutput struct itself would have any impact. One more quick question. Why does removing this line break the shader?

float4 v = tex2D(TextureSampler, input.texCoord);

I do not see v being used anywhere right?

yea, so the structure is the same as what you have there, just tidier :slight_smile:

Right, that is the issue I found, v is not used at all, other than to sample the TextureSampler. If I comment that out, the texture pipeline goes to poop… I think this is a but in MG…

Yeah that is so strange. I know it likes to remove unused variables and stuff, but you are using the TextureSampler in the return line. So I am not sure why that v variable would be needed. Strange!

I think it’s the fact that the texture is being sampled first, before any other in the shader, before the displacement texture was samples first, this just ensured that this texture got sampled first. I imagine if you sample the displacement texture before that it will break it again… it’s v odd, and as I say I think a bug in MG :confused:

Awesome. Thank you for the help! I do not know HLSL very well and as you can see, this shader is very simple. I was struggling on this for a few days and was pulling my hair out!

1 Like

The more you play with them, the better you will get. It’s worth investing the time in them, as ultimately, EVERYTHING you send to the screen will go through a shader, and if you can write your own, you will have total control :slight_smile:

Do you recommend any resources (books, videos, or websites)? I tried looking for better resources other than “Here is how to make your sprite look greyscale” type of shaders.

First book I ever got was this, Programming Vertex, Geometry, and Pixel Shaders, I have the first edition, it’s out of print now though as I think this is too :frowning:

I have a few samples on my repo, you could re purpose the post processing shaders I have in there for 2D sprite rendering, as in that sample I am using them on SpriteBatch Draw calls too :slight_smile: Though, in my engine, I use a screen space quad, this stops these sort of issues we have just spoken about here too.

Good luck, it can be frustrating playing with shaders, but at the same time, it can be very rewarding too :slight_smile:

Ohh, looks like the first edition (the one I have) is on amazon here, only £2.61!!

Forgive me spamming you, but this is a great reference book too.

Awesome thank you. I purchased them!

1 Like

You don’t have to use the apply call unless you add a vertex shader you don’t have to use the set either on the first texture you can redo it like so.

to paste code just make sure there are a couple emptly lines above and below the code you paste.
drop it in and highlight all of it.
then hit the preformatted text button.

//
// refract
//
#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

// This vector should be in motion in order to achieve the desired effect.
float2 DisplacementMotionVector;
// displacement amount
float DisplacementIntensity;

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

Texture2D DisplacementTexture;
sampler2D DisplacementSampler = sampler_state
{
    AddressU = wrap;
    AddressV = wrap;
    Texture = <DisplacementTexture>;
};

float4 PsRefraction(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // Look up the displacement amount.
    float2 displacement = tex2D(DisplacementSampler, DisplacementMotionVector + texCoord * DisplacementIntensity) * 0.2 - 0.15;
    // Offset the main texture coordinates.
    texCoord += displacement;
    // Look up into the main texture.
    return tex2D(TextureSampler, texCoord) * color;
}

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

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;

        Effect refractionEffect;

        Texture2D tex2dForground;
        Texture2D tex2dRefractionTexture;

        Texture2D tex2dEgyptianDesert;
        Texture2D tex2dpgfkp;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1200;
            graphics.PreferredBackBufferHeight = 700;
            Window.AllowUserResizing = true;
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }

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

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            tex2dEgyptianDesert = Content.Load<Texture2D>("EgyptianDesert");
            tex2dpgfkp = Content.Load<Texture2D>("pgfkp");

            tex2dForground = tex2dEgyptianDesert;
            tex2dRefractionTexture = tex2dpgfkp;

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

        protected override void UnloadContent()
        {
        }

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

            base.Update(gameTime);
        }

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

            // Draw the background image.
            spriteBatch.Begin();
            spriteBatch.Draw(tex2dForground, new Rectangle(0, 0, 300, 150), Color.White);
            spriteBatch.Draw(tex2dRefractionTexture, new Rectangle(0, 150, 300, 150), Color.White);
            spriteBatch.End();

            // similar to the original shown.
            Draw2dRefracted(tex2dForground, tex2dRefractionTexture, new Rectangle(300, 0, 300, 150), .05f, (1f/3f) ,gameTime);
            // this one has wind instead.
            Draw2dRefractedDirectionally(tex2dForground, tex2dRefractionTexture, new Rectangle(600, 0, 300, 150), .05f, (1f / 3f), new Vector2(10,1), gameTime);

            base.Draw(gameTime);
        }

        /// <summary>  
        /// Draw a refracted texture using the refraction effect.
        /// </summary>
        /// <param name="texture">the texture to draw that will be effected</param>
        /// <param name="refractionTexture">the texture to use for the refraction effect</param>
        /// <param name="screenRectangle">the area to draw to on the screen</param>
        /// <param name="refractionSpeed">the speed at which the refraction occurs</param>
        /// <param name="displacementIntensity">the displacement intensity of the refraction how drastic it is  1/3 was the default.</param>
        public void Draw2dRefracted(Texture2D texture, Texture2D refractionTexture, Rectangle screenRectangle, float refractionSpeed, float displacementIntensity, GameTime gameTime)
        {
            double time = gameTime.TotalGameTime.TotalSeconds * refractionSpeed;
            var 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["Refraction"];
            refractionEffect.Parameters["DisplacementMotionVector"].SetValue(displacement);
            refractionEffect.Parameters["DisplacementIntensity"].SetValue(displacementIntensity);
            refractionEffect.Parameters["DisplacementTexture"].SetValue(refractionTexture);

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

        /// <summary>
        /// Draw a refracted texture using the refraction directional effect.
        /// </summary>
        /// <param name="wind"> the directionality of the refraction</param>
        public void Draw2dRefractedDirectionally(Texture2D texture, Texture2D refractionTexture, Rectangle screenRectangle, float refractionSpeed, float displacementIntensity, Vector2 wind, GameTime gameTime)
        {
            wind = Vector2.Normalize(wind)  * -(float)(gameTime.TotalGameTime.TotalSeconds * refractionSpeed);
            var displacement = new Vector2(wind.X, wind.Y); // + screenRectangle.Location.ToVector2();

            // Set an effect parameter to make the displacement texture scroll in a giant circle.
            refractionEffect.CurrentTechnique = refractionEffect.Techniques["Refraction"];
            refractionEffect.Parameters["DisplacementMotionVector"].SetValue(displacement);
            refractionEffect.Parameters["DisplacementIntensity"].SetValue(displacementIntensity);
            refractionEffect.Parameters["DisplacementTexture"].SetValue(refractionTexture);

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

actually if you set the other values in load one time displacement speed and stuff all you have to set is the motion. then call spritebatch,Draw

    // the other stuff can be set to the effect after you loaded it in load including the refraction texture.
    public void Draw2dRefracted(Texture2D texture, Texture2D refractionTexture, Rectangle screenRectangle, float refractionSpeed, float displacementIntensity, GameTime gameTime)
    {
        double time = gameTime.TotalGameTime.TotalSeconds * refractionSpeed;
        var displacement = new Vector2((float)Math.Cos(time), (float)Math.Sin(time));

        // might still want to set this if you have other techniques in the shader.
        // refractionEffect.CurrentTechnique = refractionEffect.Techniques["Refraction"];
    
        // then this is all that you need to set.
        refractionEffect.Parameters["DisplacementMotionVector"].SetValue(displacement);

        spriteBatch.Begin(0, null, null, null, null, refractionEffect);

        spriteBatch.Draw(texture, screenRectangle, Color.White); // and call it.

        spriteBatch.End();
    }

Sorry I am a bit confused. Which Apply call are you referring to?

He is saying you can pass the effect to the sprite batch begin call, rather than call the apply on it.

How would that work with multi pass shaders, or if you wanted to use different techniques within the shader?

Would you set them prior to the begin call?

Does the begin run through all the passes in the selected technique?

Forgive my ignorance :slight_smile:

Does the begin run through all the passes in the selected technique?

Im not sure on that one. I would think it would be looping them all by default but im not positive on that.
You would have to look into the base.draw call to see if it is using a loop on the passes or if its calling pass(0).

Edit woops i accidently messed up this post ya it does it runs thru all the passes if you set the effect to begin.
so you could do something like so.

technique TwoPassForSpriteBatch
{
    pass Pass0
    {
        PixelShader = compile PS_SHADERMODEL PsRefractionSnells();
    }
    pass Pass1
    {
        PixelShader = compile PS_SHADERMODEL PsDisplacement();
    }
}

Yep just tried it and it works one on the right is red with the disp texture coords fliped then drawn on top of the other colored red at half alpha same code as the others just the above in the shader and call the two pass technique in the game1 method function.

How would that work with multi pass shaders, or if you wanted to use different techniques within the shader?
Would you set them prior to the begin call?

New techniques between begin end. Typically id say no maybe if your in immediate mode you could do it but it would be hacky id say do a new begin end.

Prolly have to get someone to chime in that knows a bit better on that or dig thru the code.

Ahh cool, that’s interesting :slight_smile:

Thanks for that :smiley:

Which line is calling the Apply method? Sorry I must be overlooking something :frowning:

Earlier i was referring to image of the code he posted shown on line 97 when you pass the effect into spritebatch instead. then you don’t need to call apply Though its not a big deal and you sort of have to do it that way if you want to override the vertex shader and still use spritebatch.