SpriteBatch and custom shaders in XNA Game Studio 4.0

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.


    // Draw the sprite.

    // End the sprite batch, then end our custom effect.


It works, but…   UGLY!

Game Studio 4.0 provides this cleaner alternative:

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

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.

Comments (20)

  1. Alejandro says:

    When using custom shaders for the sprite batch, do we have to take care of the half pixel/texel centering? I remember reading that the SpriteBatch took care of this automatically, but not sure if it was in the application or in the vertex shader.

  2. > When using custom shaders for the sprite batch, do we have to take care of the half pixel/texel centering?

    It’s up to you to apply that offset if you want exact SpriteBatch style screen texel alignment, or not if you want some other kind of projection (eg. placing sprites in a 3D scene).

    In the example of using BasicEffect that I posted above, I just baked this offset into the projection matrix.

  3. Adam Miles says:

    Does this mean SpriteBatch has lost its 1 vertex per sprite (in the vertex buffer) optimisation for the 360? Does rotation/flipping/scaling now occur on the CPU?

  4. Clayman says:

    Can I update custom effect parameter after calling effect.begin?

  5. Nice shawn,,

    does this meen that we can pass depthbuffer and recontruct the depthbuffer in a spriteeffect

    and do some magic here like light, paraticles collision

    Best Regrads

    Michael Hansen

  6. > Does this mean SpriteBatch has lost its 1 vertex per sprite (in the vertex buffer) optimisation for the 360? Does rotation/flipping/scaling now occur on the CPU?

    We’re no longer using that vfetch shader trickery on Xbox, but don’t worry, we applied some alternative cunning optimizations to keep sprite drawing nice and fast!

  7. > Can I update custom effect parameter after calling effect.begin?

    There is no Effect.Begin API in Game Studio 4.0. Instead, you use EffectPass.Apply to set effect state onto the graphics device.

    You can change effect parameter values at any time, but this will not affect the device until you call Apply.

  8. Clayman says:

    >There is no Effect.Begin

    It’s my mistake, it should be "update parameter after SpriteBatch.Begin" 🙂

    Is the following code valid:


    set effect params..

    apply params


    set new params

    apply params



    What’s the difference between

    Effect.CommitChange and EffectPass.Apply. Is there somthing like SetShaderConstant() in 4.0 so i can updte parameter directly?

  9. > Is the following code valid:


    > What’s the difference between Effect.CommitChange and EffectPass.Apply. Is there somthing like SetShaderConstant()

    There is no Effect.CommitChanges or SetShaderConstant API in Game Studio 4.0.

    EffectPass.Apply is always used any time you want to set shader parameters onto the device.

  10. Aaron Schultz says:

    Thank you Shawn and team. The XNA font system is pretty locked down, so this finally gives a relatively simple way to draw text billboards. Speaking of fonts, any chance you guys could bake in support for loading non-installed fonts off the disk within the Font Descriptor processor using the PrivateFontCollection API? Or at least provide a way to inject a system Font object (that we can load ourselves) into the processor? In a lab environment (like at school), it can be difficult/impossible to use non-standard fonts because we don't have the privs to install them. The XNA font system in the pipeline is all internal sealed classes, so short of duplicating the XNA internals out using reflector there is no easy way to do this.

  11. Markus says:

    Hello there,

    could anyone point me to a example off how too use spritebatch with own effect (basiceffect) to draw in 3d scene?

    tried and searched but cant get it running.

    many thanks

  12. Markus: have you tried the code example I gave at the end of this article?

  13. Zenhipster says:

    This code is not working. What's wrong?

           protected void Render2TextureNormalCompute()


               RenderTarget2D oldRT = (graphics.GraphicsDevice.GetRenderTargets().Length > 0) ?

                   graphics.GraphicsDevice.GetRenderTargets().GetValue(0) as RenderTarget2D : null;


               graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1, 0);









               gridEffect.CurrentTechnique = gridEffect.Techniques["ComputeNormals"];




                                     new Rectangle(0, 0, 256, 256),





  14. Zenhipster: I would recommend the creators.xna.com forums for this question. Blog comments aren't really a good place to do tech support especially if you need to post code examples!

  15. Zenhipster says:

    But this code example matches the theme of the blog, and does not work.

    I relied on your help, but probably should ask someone else yet))

  16. AndySchatz says:

    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.

  17. Remi Gillig says:

    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.

  18. Robert A. says:

    @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 😉

  19. sandy says:

    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…

  20. Michael says:

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