Personal Developer Notebook

MonoGame HLSL Shader Example for a Post-Processing Blur Effect

Not long ago I wrote about custom shaders on Windows Phone 8 using MonoGame, see here.

In this blog post a custom shader is used for a post-processing effect. For such effects  2D only shaders are used.

The most common scenario:

  • 3D scene is constructed the usual way,
  • but the 3D scene is rendered into a RenderTarget instead, and
  • then the RenderTarget is rendered to screen using a post-processing effect

To create a really simple example we use a quad as our 3D scene and a very simple diagonal blur shader.

First we take a look at the resulting screenshot on a Windows Phone 8 device without and with the post-processing effect applied.

Scene Without Blur Effect

Scene without Blur Effect

Blurred Image

Blurred Image

First the code:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PostProcessMini1
    public class Game1 : Game
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Matrix _view, _projection;
        private Texture2D _tex2d;
        private BasicEffect _basicEffect;
        private Effect _blurEffect;
        private VertexPositionNormalTexture[] _vertices;
        private int[] _indices;
        private RenderTarget2D _renderTarget;

        public Game1()
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

        protected override void Initialize()

        protected override void LoadContent()
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            _tex2d = Content.Load("s7");
            _view = Matrix.CreateLookAt(new Vector3(0, 0, 2), Vector3.Zero, Vector3.Up);
            _projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 4.0f / 3.0f, 1, 500);
            _basicEffect = new BasicEffect(GraphicsDevice);
            _blurEffect = Content.Load("BlurEffect");

            _renderTarget = new RenderTarget2D(GraphicsDevice,


        private void SetupBasicEffect()
            _basicEffect.World = Matrix.Identity;
            _basicEffect.View = _view;
            _basicEffect.Projection = _projection;
            _basicEffect.TextureEnabled = true;
            _basicEffect.Texture = _tex2d;

        private void CreateQuad()
            _vertices = new VertexPositionNormalTexture[4];
            _vertices[0].Position = new Vector3(-0.5f, -0.5f, 0f);
            _vertices[0].TextureCoordinate = new Vector2(0f, 1f);
            _vertices[0].Normal = Vector3.Backward;
            _vertices[1].Position = new Vector3(-0.5f, 0.5f, 0f);
            _vertices[1].TextureCoordinate = new Vector2(0f, 0f);
            _vertices[1].Normal = Vector3.Backward;
            _vertices[2].Position = new Vector3(0.5f, -0.5f, 0f);
            _vertices[2].TextureCoordinate = new Vector2(1f, 1f);
            _vertices[2].Normal = Vector3.Backward;
            _vertices[3].Position = new Vector3(0.5f, 0.5f, 0f);
            _vertices[3].TextureCoordinate = new Vector2(1f, 0f);
            _vertices[3].Normal = Vector3.Backward;

            _indices = new int[6];
            _indices[0] = 0;
            _indices[1] = 1;
            _indices[2] = 2;
            _indices[3] = 2;
            _indices[4] = 1;
            _indices[5] = 3;

        protected override void Update(GameTime gameTime)

        bool _useBlur = true;

        protected override void Draw(GameTime gameTime)

            foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
                GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, _vertices, 0, 4, _indices, 0, 2);

            if (_useBlur) {

                _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
                _spriteBatch.Draw(_renderTarget, new Vector2(0, 0), Color.White);


Last but no least the HLSL shader:

sampler TextureSampler : register(s0);
Texture2D <float4> myTex2D;
float4 PixelShaderFunction(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 texCoord : TEXCOORD0) : SV_TARGET0
    float4 tex;
    tex = myTex2D.Sample(TextureSampler, texCoord.xy) * .6f;
    tex += myTex2D.Sample(TextureSampler, texCoord.xy + (0.005)) * .2f;
    return tex;

technique Technique1
    pass Pass1
        PixelShader = compile ps_4_0_level_9_3 PixelShaderFunction();  

Please leave a comment if it helped you getting started with your post-processing effects.