Using effects in MonoGame

Modern games use effects to improve visual quality as well as add gameplay values, like fog of war. Effects can be achieved by using textures with alpha blending but it is not efficient way and can dramatically drop FPS especially for full screen effects. Pixel shaders better way to do such things. It is quite easy to add pixel shaders in MonoGame application but not that easy as textures. So let’s try to add simple effect that has one input parameter.

First at all we need pixel shader that implements required processing. Let’s create simple fade effect.

sampler TextureSampler : register(s0);
float ColorAmount;

float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 tex = tex2D(TextureSampler, texCoord); 
    float3 colrgb = tex.rgb;
    colrgb.rgb = lerp(float3(0, 0, 0), colrgb, ColorAmount);

    return float4(colrgb.rgb, tex.a);
}

technique Technique0
{
    pass Pass0
    {
        PixelShader = compile ps_2_0 main();
    }
}

There is one input argument ColorAmount which is used as weight for linear interpolation between original pixel and fully dark one.

Now we have to compile to binary format. So far we need Windows host to run 2MGFX tool shipped with MonoGame installation package. This tool generates binary effect file. So let’s assume that we have compiled it to fade.xnb binary effect file.

Now we need to add it into our project and set its properly as BundleResource.

Снимок экрана 2014-07-31 в 22.17.42

 

Content processing is one of amazing XNA features but effects should be loaded manually. Open Game1.cs and add next code into LoadContent routine.

using (var reader = new BinaryReader (File.Open ("Content/fade.xnb", FileMode.Open))) {
				m_effect = new Effect (GraphicsDevice, reader.ReadBytes ((int)reader.BaseStream.Length));
			}

Now we can use our effect in Draw loop. To test it let’s add simple tile texture and try to fill our screen with tiles processed by our effect.

Adding a texture is extremely simple, here we can use content processing and load it as follows.

m_sprite = Content.Load<Texture2D> ("tile.png");

tile.png is also should be added into Content folder and configured in the same way as fade.xnb file. Since we use content processing and don’t load it directly as binary stream we skip Content folder in texture file path. The Content is already configured as root folder in Game constructor.

public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
			IsMouseVisible = true;
        }

Cursor is hided by default in MonoGame 3.2 so we explicitly enabled it in last line.

Now modify Draw loop and add effect and tile.

spriteBatch.Begin (SpriteSortMode.Immediate, BlendState.AlphaBlend);

			var width = graphics.PreferredBackBufferWidth;
			var height = graphics.PreferredBackBufferHeight;

			for (int y = 0; y < height; y += 32) {
				for (int x = 0; x < width; x += 32) {
			
					m_effect.Parameters ["ColorAmount"].SetValue ((float)x / 400);
					m_effect.CurrentTechnique.Passes [0].Apply ();

					spriteBatch.Draw (m_sprite, new Vector2 (x, y));
				}
			}

			spriteBatch.End ();

Build and run it.

Снимок экрана 2014-06-13 в 0.01.01