21

When I draw a stretched Texture2D, the pixels receive a Blur-like effect.

I want to use 'pixelated' graphics in my game and would like to know how to disable this in favor of the simplest nearest neighbor zoom.

I've created this picture for illustration: (x4 zoom)
enter image description here

In what way can I accomplish this?

Acidic
  • 6,154
  • 12
  • 46
  • 80

3 Answers3

17

In XNA 4, change your SpriteBatch.Begin() to set the sampler state to SamplerState.PointClamp

scottheckel
  • 9,106
  • 1
  • 35
  • 47
  • 5
    I've used the method in this way after looking up the defaults on MSDN: `spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise);` and it works just fine. Thanks! – Acidic Feb 09 '12 at 17:43
6

If you're using a shader to draw the image, you can modify the sampler state:

sampler2D mySampler = sampler_state
{
    Texture=<SomeTexture>;
    Filter=POINT;
};

Point sampling should prevent the GPU from interpolating when sampling the image, which is probably what's causing your antialias / blur behavior.

If you're just using SpriteBatch to draw the image, you can set the filter using:

Device.SamplerStates[0] = SamplerState.PointClamp;

Also, it seems that you may have to set the SpriteBatch to use Immediate mode. See this article on MSDN for more information, or this thread on the App Hub forums.

Here's an earlier SO thread that'll probably be helpful:

See this thread for more information.

Textures are sampled using normalized coordinates (0..1, 0..1) rather than texel coordinates. The GPU will find the four closest texels for a given texture coordinate, and interpolate between them based on the position of the sample point within that square.

So, if I have a texture that is 10 x 10 pixels, and I attempt to read from, say, (0.15, 0.15), the GPU will interpolate between the texels at (1,1),(2,1),(1,2) and (2,2). In this case, the 0.05 should cause the resulting pixel on screen to be simply the average of the four surrounding pixels. However, if the texture were sampled at (0.19, 0.19), the resulting color would be heavily biased towards the texel at (2,2).

Point sampling will cause the GPU to always read an exact color from the underlying texture instead of weighting coordinates around the sample area.

Here's a working .Draw() method that illustrates all of this:

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);

        var largeRect = new Rectangle(50, 50, sprite.Width * 3, sprite.Height * 3);

        /// start the batch, but in immediate mode to avoid antialiasing
        spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);

        /// set the filter to Point
        GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;

        /// draw the sprite
        spriteBatch.Draw(sprite, largeRect, Color.White);

        /// done!
        spriteBatch.End();

        // TODO: Add your drawing code here

        base.Draw(gameTime);
    }
Community
  • 1
  • 1
3Dave
  • 28,657
  • 18
  • 88
  • 151
  • I'm simply using the `spriteBatch.Draw(...)` method. Both drawing it to a larger rectangle and using the `scale` property result in this effect. – Acidic Feb 09 '12 at 16:54
  • @Acidic updated. SpriteBatch provides a property for setting the filter method. – 3Dave Feb 09 '12 at 16:56
  • Trying to change the value of `SamplerStates[0].Filter` results in an error, stating that it is a read-only property. – Acidic Feb 09 '12 at 17:08
  • @Acidic updated again; you have to create a new SamplerState object and assign it to device.SamplerStates[0], or use one of the predefined SamplerState.* members. – 3Dave Feb 09 '12 at 17:18
  • Thanks for the help so far. Should I assign this every frame in the `Draw` method or in the `LoadContent` method? (Both seem to not be working at the moment...) – Acidic Feb 09 '12 at 17:22
  • @Acidic Updated with a working .Draw method that sets everything in the correct order. – 3Dave Feb 09 '12 at 17:54
0

You need to make sure your x,y co-ordinates are integer values. Fractional co-ordinates or dimensions result in anti-aliasing being applied to simulate "fractional" pixel offsets.

Hence the blurring.

Cris Stringfellow
  • 3,714
  • 26
  • 48
  • drawing with a `Rectangle` object still creates the unwanted effect. Question was answered anyway. :) – Acidic Feb 09 '12 at 18:47