SpriteBatch and custom shaders in XNA Game Studio 4.0

Originally posted to Shawn Hargreaves Blog on MSDN, Monday, April 5, 2010

Improvement #1:

Our Sprite Effects sample uses SpriteSortMode.Immediate to draw sprites with a custom pixel shader:

    // Begin the sprite batch, then activate our custom effect.
    spriteBatch.Begin(SpriteBlendMode.None,
                      SpriteSortMode.Immediate,
                      SaveStateMode.None);

    desaturateEffect.Begin();
    desaturateEffect.CurrentTechnique.Passes[0].Begin();

    // Draw the sprite.
    spriteBatch.Draw(...);

    // End the sprite batch, then end our custom effect.
    spriteBatch.End();

    desaturateEffect.CurrentTechnique.Passes[0].End();
    desaturateEffect.End();

It works, but...   UGLY!

Game Studio 4.0 provides this cleaner alternative:

    spriteBatch.Begin(0, BlendState.Opaque, null, null, null, desaturateEffect);
    spriteBatch.Draw(...);
    spriteBatch.End();

Improvement #2:

If you look at the HLSL shader from previous versions of SpriteBatch, you will notice the Xbox implementation used a complex vertex shader. This meant that, while it was common to use SpriteBatch with a custom pixel shader, customizing the vertex shader was excessively difficult.

As of 4.0, the SpriteBatch vertex shader is much simpler:

    void SpriteVertexShader(inout float4 color    : COLOR0,
                            inout float2 texCoord : TEXCOORD0,
                            inout float4 position : POSITION0)
    {
    }

This makes it trivial to use SpriteBatch with custom vertex shaders. You can even combine SpriteBatch with BasicEffect! This code configures BasicEffect to replicate the default SpriteBatch coordinate system:

    Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
    Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);

    basicEffect.World = Matrix.Identity;
    basicEffect.View = Matrix.Identity;
    basicEffect.Projection = halfPixelOffset * projection;

    basicEffect.TextureEnabled = true;
    basicEffect.VertexColorEnabled = true;

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

By changing the projection and view matrices, it is now possible to position SpriteBatch drawing (including text) wherever you like within a 3D scene.

Blog index   -   Back to my homepage