Issues upscaling pixel art with SampleState.PointClamp

| | August 8, 2015

I’m developing a 2D game, and I’m striving to create a nice blocky low-res appearance for the game, yet allow it to be played at variable resolutions. So, I’ve researched resolution independency and have a nice system in place to have my game rendered at 640×480 to a RenderTarget2D, and then have the RenderTarget blown up on the back buffer. Anybody that’s looked into this knows that by default scaled images will be antialiased, and to disable that, change the SampleState to PointClamp. So, I’ve done that, but now I’m having this issue where my sprites aren’t upscaling properly. Certain pixels are becoming particularly chunky where they shouldn’t be, and I’m not sure what the cause of this behavior is, and I haven’t been able to find any documentation of anybody else having this issue, can anybody offer any insight?

Prior to upscaling.
After upscaling.
Upscaling to 2X original size.

The last image illustrates the image rendered in game at 2X the original size. A 2:1 ratio should be a pretty straightforward scale to render, but if you look closely, there are some pixels that are at half size the others.

Edit #2: Here’s some code regarding the scaling.

First, the game is captured by the render target at 640×480 resolution, then it is drawn at the back buffer’s resolution (in the second image, reslution is 800×600, in the third image, resolution is 1280×960, I haven’t messed with alternate aspect ratios yet).

            new Rectangle((int)location.X, (int)location.Y, frameWidth, frameHeight),
            new Rectangle((int)spriteLocation.X, (int)spriteLocation.Y, spriteWidth, spriteHeight,
spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null);
spriteBatch.Draw(RenderTarget, new Rectangle(0,0, backbufferWidth, backbufferHeight), Color.White);

Edit #3: Code involving the sprite’s height and width.
First, in the class that my monster inherits from, is this little method that runs in the base class’ Update() method. It just makes sure that if I have animations with non-uniform sizes, that the sizes are updating properly.

protected void UpdateFrameSizes()
        frameWidth = animations[currentAnimation].Frames[animations[currentAnimation].currentFrame].SizeAndsource.Width;
        frameHeight = animations[currentAnimation].Frames[animations[currentAnimation].currentFrame].SizeAndsource.Height;

Then, just to make sure that the Width and Height were correct in his animation class, I checked those:

idleFrame1 = new AnimationFrame(Texture, new Rectangle(154 * 0, 107 * 0, 154, 107), 1f);
        idleFrame2 = new AnimationFrame(Texture, new Rectangle(154 * 1, 107 * 0, 154, 107), 1f);
        idleFrame3 = new AnimationFrame(Texture, new Rectangle(154 * 2, 107 * 0, 154, 107), 1f);

All looks correct here, I even went to the source image and counted the pixels (Yes, I know, his sprite sheet’s dimensions are weird, but they’re accurate).

And then this data is all used in Edit #2’s code snippet during the draw method.

One Response to “Issues upscaling pixel art with SampleState.PointClamp”

  1. ClassicThunder on November 30, -0001 @ 12:00 AM

    You have to upscale in whole numbers to get the effect you are expecting.

    You are scaling it by about 1.8 so dividing the second image by 0.55 would get you the pixel you are sampling. Below is approximately how the sampling is being handled in the example you provided.

    1 = 0.55 = 1
    2 = 1.1  = 1
    3 = 1.65 = 2
    4 = 2.2  = 2
    5 = 2.75 = 3
    6 = 3.3  = 3
    7 = 3.85 = 4
    8 = 4.4  = 4
    9 = 4.95 = 5  <-- This is what is causing your artifact (It can work both ways causing a jump or repeating a pixel more often than is nommal)
    10 = 5.5 = 6

    Because you are not scaling by a while number you are going to sample inconsistently because the rounding is going to jump in a constant rotation dictated by the ration of the remainder.

    In general sprites don’t scale very well. If aesthetics are important to you you might want to draw all of the sizes a sprite can be.

Leave a Reply