SpriteBatch and custom shaders in XNA Game Studio 4.0

SpriteBatch and custom shaders in XNA Game Studio 4.0

  • Comments 20

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.

  • I'm not sure if I'm doing something wrong or not, but if I'm not, then I think the example of the vertex shader above might be misleading?  If I get rid of the vertex shader in my effect it works correctly, but if I include a vertex shader it still requires some stuff in the body in order to render the sprite at the correct position.  In particular:

    // Half pixel offset for correct texel centering.

    position.xy -= 0.5;

    // Viewport adjustment.

    position.xy = position.xy / Viewport;

    position.xy *= float2(2, -2);

    position.xy -= float2(1, -1);

    Note that you have to set "Viewport" in your code before using the shader.

    I'm not posting this here asking for support, but just to note for others who might come across this post that the preceding code is what worked for me.

  • AndySchatz is right, here is the minimum vertex shader needed :

    float2 Viewport;

    void SpriteVertexShader(inout float4 color    : COLOR0,

                           inout float2 texCoord : TEXCOORD0,

                           inout float4 position : POSITION0)

    {

       // Half pixel offset for correct texel centering.

       position.xy -= 0.5;

       // Viewport adjustment.

       position.xy = position.xy / Viewport;

       position.xy *= float2(2, -2);

       position.xy -= float2(1, -1);

    }

    And in your project :

    effect.Parameters["Viewport"].SetValue(new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height));

    This is very useful if you use ps_3_0 shaders because you'll need a vs_3_0 associated with it, even XNA tells you that. The article needs an update, or XNA needs one to get rid of this nasty viewport fix.

  • @Remi Gillig: Thank you a lot for your SpriteVertexShader solution.

    I was looking more than half this day to figure out how to get my Light Scattering Post Process Shader to work by using SpriteBatch.

    @Shawn pls move this simple example up in your Post so that it more obvious. Its exactly what missing in your Post ;)

  • thanks for this article...i used it and it works perfectly...is this basiceffect will work for drawing polygon...because i used it for drawing polygon..but nothing has shown on screen...

  • Thank you for this explanation and code.... you have helped me so much Shawn, I owe so much to you!!!

Page 2 of 2 (20 items) 12
Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post