MonoGame Nothing Shader

Okay for this first post, I’m going to keep things simple. Here is a shader that does nothing. We’ll put that in a file called Nothing.fx and load it as content in a MonoGame OpenGL game.

sampler TextureSampler : register(s0);
float2 ViewportSize;
struct VertexToPixel {
    float4 Position : SV_Position0;
    float4 Color : COLOR0;
    float4 TexCoord : TEXCOORD0;
};
VertexToPixel SpriteVertexShader(float4 color : COLOR0, float4 texCoord : TEXCOORD0, float4 position : POSITION0) {
    VertexToPixel Output = (VertexToPixel)0;

    // Half pixel offset for correct texel centering. - This is solved by DX10 and half pixel offset would actually mess it up
    position.xy -= 0.5;

    // Viewport adjustment.
    position.xy /= ViewportSize;
    position.xy *= float2(2, -2);
    position.xy -= float2(1, -1);

    //pass position and color to PS
    Output.Position = position;
    Output.Color = color;
    Output.TexCoord = texCoord;

    return Output;
}

float4 SpritePixelShader(VertexToPixel PSIn): COLOR0 {
    float4 diffuse = tex2D(TextureSampler, PSIn.TexCoord);
    return PSIn.Color * diffuse;
}

technique SpriteBatch {
    pass {
        VertexShader = compile vs_2_0 SpriteVertexShader();
        PixelShader = compile ps_2_0 SpritePixelShader();
    }
}
//In LoadContent():
effect = Content.Load<Effect>("Nothing");


//In Draw():
effect.Parameters["ViewportSize"].SetValue(new Vector2(Window.ClientBounds.Width, Window.ClientBounds.Height));
spriteBatch.Begin(effect: effect);
spriteBatch.Draw(image, new Vector2(200, 100), Color.White);
spriteBatch.End();

Note: Not seen but it is implied here that a Texture2D is loaded in the variable “image”.

As you can see, we pass a ViewportSize to the shader and draw the Texture2D using our Nothing shader at a location of 200, 100.


Some information about how shaders work.

sampler TextureSampler : register(s0);

This line receives the “image” Texture2D we gave to the draw call.

float2 ViewportSize;

This line receives the Vector2 passed in using the shader parameters.

SpriteVertexShader(float4 color : COLOR0, float4 texCoord : TEXCOORD0, float4 position : POSITION0)

Here we receive 3 variables in our vertex shader.

position with POSITION0 as the semantic. This means that position uses the screen’s coordinate system. Earlier, in our draw call we passed a Vector2(200, 100). That Vector is used to translate position by 200, 100.

color with COLOR0 as the semantic. This value comes from our draw call. Earlier we passed Color.White. Colors have a value between 0 and 1 in other words, White is (1, 1, 1, 1). The forth parameter is the alpha transparency channel.

texCoord with TEXCOORD0 as the semantic. This one has a mapping of 0 to 1. We use texCoord to sample the texture we received above.

Okay, now we get to the interesting part, the meat of this vertex shader. Turns out this isn’t as complicated as it looks.

// Half pixel offset for correct texel centering. - This is solved by DX10 and half pixel offset would actually mess it up
position.xy -= 0.5;

// Viewport adjustment.
position.xy /= ViewportSize;
position.xy *= float2(2, -2);
position.xy -= float2(1, -1);

So we start with the position variable. This is in screen coordinates. We want to transform this in a different coordinate system. On the X axis, it will go from -1 to 1 and on the Y axis, it will go from 1 to -1. Here is an example for a screen that is 1000px by 500px.

After the transformation, coordinate (0, 0) becomes (-1, 1) and coordinate (1000, 500) becomes (1, -1). This is the coordinate system that the pixel shader expects later. As you can notice, (0, 0) is the center of the screen.

After that, it’s just a matter of bundling the variables in the VertexToPixel struct and returning that.


float4 diffuse = tex2D(TextureSampler, PSIn.TexCoord);

tex2D is a function that samples the color of a texture at the point TexCoord. Here our texture is “image” which is stored in TextureSampler.

return PSIn.Color * diffuse ;

All this does is it multiplies our diffuse color which we just extracted from our “image” with the color we passed in. That color is White which is 1 therefore the result is simply diffuse. As an exercise, you can try passing in other colors in the draw call with new Color(100, 150, 200)to see how the image’s colors get changed.


That’s it for now! If you see any errors, make sure to tell me in the comments below. Hopefully this can act as a template that can be used to build various shaders.

Hello world!

Hello World! This is the first blog post on this site. Actually that’s a lie, I lunched this site back in 2012 when I was working on my own programming language along with a compiler for it from scratch. Back then I would actually post often about my thinking process and how the project was moving along. The code was open sourced and people could check it out.

Since then, time has passed and Vyne has become my company in which I work on programming projects. The current project is a video game called Rashtal. Rashtal is a sort of puzzle game in the canopy of a foreign world. I started working on that project in September 2016.

Anyway, that’s it for me for now, the next article will be about shaders in MonoGame.