SpriteBatch and renderstates in XNA Game Studio 4.0

SpriteBatch and renderstates in XNA Game Studio 4.0

  • Comments 42

A repost of one of my more popular articles, upgraded for Game Studio 4.0...

If you are mixing 3D rendering with 2D objects using SpriteBatch, you may notice that your 3D graphics no longer draw correctly after you have rendered sprites. This is because the SpriteBatch changes several device states to values that may not be appropriate for drawing in 3D.

So exactly which states does SpriteBatch change? Here's the complete list:

    GraphicsDevice.BlendState = BlendState.AlphaBlend;
    GraphicsDevice.DepthStencilState = DepthStencilState.None;
    GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
    GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

SpriteBatch also modifies the vertex buffer, index buffer, and applies its own effect onto the GraphicsDevice.

Before you draw anything in 3D you will probably want to reset these states:

    GraphicsDevice.BlendState = BlendState.Opaque;
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;

Depending on your 3D content, you may also want to set:

    GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
  • Adam Goss: Or look at create.msdn.com/.../primitives_3d

  • Cheers Tomas, the rasterizer sorted it.  You've saved me much grief! :)

  • Thanks for the post.  This got me going again after everything turned to black.

  • I am having issues with this myself. At present i only have very basic stuff as i am trying to build an engine. was trying to write my picked positions on the y plane to screen whilst showing a simple square set up with 4 vertices. spritebatch is doing...something which is stopping my square being drawn to screen. my draw call looks like this:

               GraphicsDevice.Clear(Color.Black);        

               myterrain.Draw();

               spriteBatch.Begin();

               spriteBatch.DrawString(font, pickedPosition.X.ToString() + " , " + pickedPosition.Z.ToString(), Vector2.Zero, Color.Yellow);

               spriteBatch.End();

               GraphicsDevice.RasterizerState = RasterizerState.CullNone;

               GraphicsDevice.BlendState = BlendState.Opaque;

               GraphicsDevice.DepthStencilState = DepthStencilState.Default;

               GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

    anyone see anything obvious here?

  • Fixed it. i just needed to recall the method that assigns the vertices of my square. spritebatch wiped the vertex buffer each time so the vertices have to be re-allocated

  • I have a shader on an object in the world which does:texture

    diffuseTexture : DIFFUSE;

    sampler2D diffuseSampler = sampler_state

    {

       Texture = <diffuseTexture>;

       MinFilter = Linear;

       MipFilter = Linear;

       MagFilter = Linear;

       AddressU = wrap;

       AddressV = wrap;

    };

    The texture I have is Power Of two.  But later on, I draw all this to a render target using bloom, and then it crashes on the sprite batch:  

    void DrawFullscreenQuad(Texture2D texture, int width, int height, Effect effect, IntermediateBuffer currentBuffer)

           {

               // If the user has selected one of the show intermediate buffer options,

               // we still draw the quad to make sure the image will end up on the screen,

               // but might need to skip applying the custom pixel shader.

               if (showBuffer < currentBuffer)

               {

                   effect = null;

               }

               effect.CurrentTechnique.Passes[0].Apply();

               GameCore.Device.SamplerStates[0] = SamplerState.LinearClamp;

               GameCore.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, null, null);

               GameCore.SpriteBatch.Draw(texture, new Rectangle(0, 0, width, height), Color.White);

               GameCore.SpriteBatch.End();

           }

    samplers 0, 1 are clamp, and 2 - 15 are wrap, but I'm not using those, and I still get

    "XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two."

    even if I loop through all 16 sampler states setting LinearClamp.  It just seems like no matter what I do, the renderer believes i'm still set for LinearWrap, even though I am not.

    If I Change the shader to clamp, everything is ok, but then my shader doesn't work.

    Any ideas?

  • MarkT: I would recommend the create.msdn.com forums for this question. Blog post comments aren't really the best place for support discussions!

  • I don't understand why static states like SamplerState.PointWrap are already read-only, even if they're not bound to a graphics device.

    var samplerState = SamplerState.PointWrap; // isBound == true

    samplerState.MaxAnisotropy = 8; // InvalidOperationException (read-only)

    graphicsDevice.SamplerStates[0] = samplerState; // bound sampler state to graphics device

  • > I don't understand why static states like SamplerState.PointWrap are already read-only

    Exactly to stop you from changing them!

    These built-in state objects provide easy access to common sets of states. If you changed one of them to use some different state, any other code that used this built-in would get unpredictable results that are most likely not what it wanted.

    Changing the meaning of existing system wide objects is not a good idea. If you want to use a different combination of states, just make a new state object of your own. That way you don't risk breaking whatever other code might also be using this object.

  • It works!

    you are great!

  • Hi Shawn,

    Could you briefly explain why you reset the values of each of the three lines in XNA 4.0:

    GraphicsDevice.BlendState = BlendState.Opaque;

    GraphicsDevice.DepthStencilState = DepthStencilState.Default;

    GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

    , and specifically; for what kind of 3d content would one want to include the last line ?

  • Thanks you, took dam ages to find the right things to change. You were simple and too the point which is great.

  • Very helpful. If I didn't reset my SpriteBatch, my 3D objects became see through.

  • Tomas Voracek thank you, i didn't realize SpriteBatch.Begin also screwed up the vertex/index buffers. once i was setting them at the start of every Draw() call along with the resetting of BasicEffects and the other states, it fixed my problem right up.

  • I was stuck and now I'm not, thank you. Specifically it was GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; that did the trick.

Page 2 of 3 (42 items) 123
Leave a Comment
  • Please add 6 and 5 and type the answer here:
  • Post