9
\$\begingroup\$

After adding velocity to my game, I feel like my textures are twitching. I thought it was just my eyes, until I finally captured it in a screenshot:

enter image description here

The one on the left is what renders in my game; the one on the right is the original sprite, pasted over. (This is a screenshot from Photoshop, zoomed in 6x.)

Notice the edges are aliasing -- it looks almost like sub-pixel rendering. In fact, if I had not forced my sprites (which have position and velocity as ints) to draw using integer values, I would swear that MonoGame is drawing with floating point values. But it isn't.

What could be the cause of these things appearing blurry? It doesn't happen without velocity applied.

To be precise, my SpriteComponent class has a Vector2 Position field. When I call Draw, I essentially use new Vector2((int)Math.Round(this.Position.X), (int)Math.Round(this.Position.Y)) for the position.

I had a bug before where even stationary objects would jitter -- that was due to me using the straight Position vector and not rounding the values to ints. If I use Floor/Ceiling instead of round, the sprite sinks/hovers (one pixel difference either way) but still draws blurry.

\$\endgroup\$
17
  • 1
    \$\begingroup\$ Could this be related to the texture filtering being applied? \$\endgroup\$ Dec 11, 2012 at 13:09
  • \$\begingroup\$ Do you have the code for your shader? Some people add half a pixel on both axis to get the pixels centered. Not sure though, I think that's a DirectX only thing. \$\endgroup\$
    – William
    Dec 11, 2012 at 13:10
  • 3
    \$\begingroup\$ Deactivating filtering is a workaround not a solution. \$\endgroup\$
    – Archy
    Dec 11, 2012 at 13:31
  • 1
    \$\begingroup\$ Rendering doesn't know anything about your velocity, so either the position, size or anything else your pass along to SpriteBatch.Draw is different or the error was present before you added velocity. How do you call SpriteBatch.Draw exactly? \$\endgroup\$
    – Archy
    Dec 11, 2012 at 15:58
  • 1
    \$\begingroup\$ @ashes999 did you debug this call and checked this.X, this.Y and this.origin? What is this.origin set to? There is basically no way the render result is different when this Draw call is the same. \$\endgroup\$
    – Archy
    Dec 11, 2012 at 17:35

5 Answers 5

3
\$\begingroup\$

Well, this is embarrassing.

It turns out that I was not drawing using integer positions, but float positions. Archy pointed me down the right path with his comment @ashes999 did you debug this call and checked this.X, this.Y and this.origin?

As soon as I added a trace statement, I noticed that nothing traced out. It turns out that my SpriteComponent class correctly used integer values, but my SpriteSheetComponent still used float values from the raw Vector2 Position.

Although I didn't try texture filtering and clamping, I was suspicious that these were not the correct change, because I was drawing a 2D image at the same width and height as the source image (no scaling).

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Done, glad you found it! \$\endgroup\$
    – Archy
    Dec 12, 2012 at 7:35
2
\$\begingroup\$

XNA uses DirectX9 which uses the center of the pixel as its location. This is noticeable when using the Vector2 based overloads of the draw class. I believe that subtracting (.5, .5) should fix your problem.

More info here.

\$\endgroup\$
6
  • \$\begingroup\$ I can't technically do this, because I'm passing in a Vector2 with int, int as the arguments. Besides which, things look perfectly fine when I don't have any objects in motion. \$\endgroup\$
    – ashes999
    Dec 11, 2012 at 15:04
  • \$\begingroup\$ What do you mean by the object is in motion? XNA has no notion of motion it draws things where you tell it too. Its a series of stationary snapshots. Also try using the Rectangle overloads since you are rounding to a rectangle anyways. \$\endgroup\$ Dec 11, 2012 at 17:22
  • \$\begingroup\$ I have my own implementation of velocity. SpriteComponent has a Vector2 position, which increments as floats depending on the velocity; when I draw, I create a new Vector2 with integer (rounded) versions of my position X and Y. This is not the issue, since stationary objects without velocity appear fine. \$\endgroup\$
    – ashes999
    Dec 11, 2012 at 17:28
  • \$\begingroup\$ So your saying that you have a position vector p and a velocity vector v, add them such as p += v, then create a copy of p using rounded values. And that this yeild different values then having a position that is the same as the previous p += v despite the draw calls being identical? Because that makes no sense. \$\endgroup\$ Dec 11, 2012 at 17:37
  • \$\begingroup\$ Yep, that's what @Archy is saying too. Let me debug and check again. I add p += v during Update, and render with new Vector2((int)Math.Round(p.X), (int)Math.Round(p.Y))). \$\endgroup\$
    – ashes999
    Dec 11, 2012 at 19:00
2
\$\begingroup\$

Rendering doesn't know anything about your velocity, so either the position, size or anything else your pass along to SpriteBatch.Draw is different or the error was present before you added velocity. How do you call SpriteBatch.Draw exactly?

Did you debug this call and checked this.X, this.Y and this.origin? What is this.origin set to? There is basically no way the render result with velocity is different when this Draw call is the same.

\$\endgroup\$
1
\$\begingroup\$

The problem is likely that texture filtering is turned on. If you're not sure what that means, consider a situation where you have a image that is 2 pixels wide: The first pixel is black, the second pixel is white. If you zoom in on this texture, if texture filtering is on, you will see that it gets blurry and that the area between the black and white pixel uses a gradient gray color.

A classic example of games with and without filtering are Super Mario 64 which had filtering while Doom did not.

When you don't use velocity, your Sprite is likely positioned so that the center of the texture is at the sample point that XNA (or whatever underlying api is being used) is grabbing the color. When you move with a float velocity, then the position of your Sprite changes, thus the sample point may not line up directly with the center of a pixel, so the end result is that a color that is an average of the nearest pixels is being used to render.

You can verify that filtering is turned on by simply rendering your Sprite really large on screen. If it's blurry, then that's the issue.

If you must have pixel-perfect accuracy in rendering, you will want to have a float value for position stored off somewhere, but use integer values for the position of the object you are rendering...or of course you could just turn off filtering if your game doesn't need it.

You may also want to read this.

\$\endgroup\$
1
  • \$\begingroup\$ Like I mentioned in my question, I'm already using integers for my location, and the original image size, when I call Draw. So I'm not sure how that could be the case -- but I'll try rendering large-scale and seeing if turning off texture filtering will help. \$\endgroup\$
    – ashes999
    Dec 11, 2012 at 22:39
1
\$\begingroup\$

Try setting your SamplerState to SamplerState.PointClamp in the SpriteBatch.Begin call.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .