GithubHelp home page GithubHelp logo

Sprite position issue about directxtk HOT 17 CLOSED

star69rem avatar star69rem commented on June 21, 2024
Sprite position issue

from directxtk.

Comments (17)

shawnhar avatar shawnhar commented on June 21, 2024 1

I'm having to put a hack in my game right now where if I find that the position is >= 0.5 and < 0.51, I add 0.01f to it just to get it to round up.

You are overthinking this. If you want pixel art drawn at precise pixel locations without any filtering, there's no need to check for specific numeric ranges, just round all your coordinates to the nearest integer.

If you specify point sampling plus draw exactly halfway between pixels, results will depend on the vagaries of numeric precision, which differ across GPUs as well as across a triangle on a single GPU. So don't do that! This isn't specific to Direct3D or SpriteBatch, it's just an inherent characteristic of how GPUs work.

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

Position = 36.51f:

image

Position = 36.50f;

image

from directxtk.

shawnhar avatar shawnhar commented on June 21, 2024

I think you're just seeing the nature of how texture filtering works on GPUs, rather than anything specific to DirectXTK. From these images it looks like you are using point sampling, which tells the GPU to round each sample location to the nearest integer. If you are drawing exactly halfway between integer positions, which way the hardware rounds will be determined by the low bits of interpolator positions, which may not round in the same direction for every pixel.

Solution: use bilinear sampling in place of point if you want the GPU to filter your images, or don't draw them at fractional locations if you want clearly defined pixel snapping.

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

I think you're just seeing the nature of how texture filtering works on GPUs, rather than anything specific to DirectXTK. From these images it looks like you are using point sampling, which tells the GPU to round each sample location to the nearest integer. If you are drawing exactly halfway between integer positions, which way the hardware rounds will be determined by the low bits of interpolator positions, which may not round in the same direction for every pixel.

Solution: use bilinear sampling in place of point if you want the GPU to filter your images, or don't draw them at fractional locations if you want clearly defined pixel snapping.

If that's the case, why does it look like only the bottom right triangle the sprite's made of distorted?

And the behavior seems inconsistent to me. Why isn't 1.51f broken?

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

I'm having to put a hack in my game right now where if I find that the position is >= 0.5 and < 0.51, I add 0.01f to it just to get it to round up. Really sucks and doesn't feel like the correct default behavior to me. It's weird for fractional positions to magically work themselves out other than this range.

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

Maybe it's because Direct3D 11 considers '0.5' to be the pixel center per Microsoft Docs?

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

@shawnhar and FYI linear filtering on the sprite batch doesn't fix it. It just blurs the distorted graphics. This is a bug.

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

This is with linear clamp:
image

image

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

I get that Direct3D wasn't designed for this, but it is "SpriteBatch," and this is a basic scenario. Spritebatch should never draw distorted sprites.

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

I'm having to put a hack in my game right now where if I find that the position is >= 0.5 and < 0.51, I add 0.01f to it just to get it to round up.

You are overthinking this. If you want pixel art drawn at precise pixel locations without any filtering, there's no need to check for specific numeric ranges, just round all your coordinates to the nearest integer.

If you specify point sampling plus draw exactly halfway between pixels, results will depend on the vagaries of numeric precision, which differ across GPUs as well as across a triangle on a single GPU. So don't do that! This isn't specific to Direct3D or SpriteBatch, it's just an inherent characteristic of how GPUs work.

You can't just round positions in a native resolution pixel art game BECAUSE of the fact that internally it already rounds SOME numbers but not others, and because every computer convention in the world treats >= .5 as rounding up OTHER than this one, you have rounding conflicts that make sprites subtly jitter and not move as smoothly as the convoluted workaround I described. It BLOWS.

I'm not overthinking it, because the game has more complicated scenarios than I've gone into here and there's a reason it is the way it is, but if you're not going to do anything about this obvious bug, go ahead and close it I guess.

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

For posterity's sake, this animated GIF shows just how blatant this bug is.
https://imgur.com/a4Xm08m

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

What is happening in your code that results in the animation you provided?

I'm trying to understand where you believe the rounding should occur here, but have you tried modifying the SpriteBatch yourself to add rounding to see if it resolves the issue?

from directxtk.

star69rem avatar star69rem commented on June 21, 2024

What is happening in your code that results in the animation you provided?

I'm trying to understand where you believe the rounding should occur here, but have you tried modifying the SpriteBatch yourself to add rounding to see if it resolves the issue?

For that example I just manually set the sprite position to 36.5f, but you'll get sprite distortion on any value from around x.499f to x.51f. If I have a 1:1 framebuffer and set a position, I would expect the rounding to happen like any normal computer function where >= .5 gets rounded up. It already rounds up a value of 35.6f and rounds down a value of 35.4f. The behavior is inconsistent.

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

Can you check which version of DirectXMath you are using? It's in the DirectXMath.h header as #define DIRECTX_MATH_VERSION?

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

There's no extra logic at all in the SpriteBatch implementation for pixel centers. It simply renders the exact positions you provide it transformed by the viewport matrix. As such, Direct3D 11 and Direct3D 12 consider the "0.5, 0.5" location to be the "pixel center" for 'nearest pixel' sampling, so it does not move the sampled pixel until the location is PAST the center to something like "0.6".

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

You could do something like:

XMVECTOR pos = XMLoadFloat2(&m_position);

static const XMVECTORF32 s_half = { { { 0.5f, 0.5f, 0.f, 0.f } } };

pos = XMVectorAdd(XMVectorRound(pos), s_half);

m_spriteBatch->( ..., pos, ...)

XMVectorRound is equivalent to SSE4 round and ARM-NEON vector round which does "Rounding half to even" or "Banker's rounding". There really isn't "any normal computer function" as you state it for rounding. Instead, you specifically expected "Rounding half away from zero" which is actually quite hard to achieve with SIMD math due to the dependance on the hardware rounding mode (which again is normally set to "Banker's rounding").

See Wikipedia

You would probably be happier with:

XMVECTOR pos = XMLoadFloat2(&m_position);

static const XMVECTORF32 s_half = { { { 0.5f, 0.5f, 0.f, 0.f } } };

pos = XMVectorAdd(XMVectorTruncate(pos), s_half);

m_spriteBatch->( ..., pos, ...)

Which at least make sures that ".0" values land exactly on the pixel center, and then it moves when you hit the next whole number.

from directxtk.

walbourn avatar walbourn commented on June 21, 2024

Updated the doc pages in the wiki to note this for future reference:

https://github.com/microsoft/DirectXTK/wiki/SpriteBatch#pixel-centers

https://github.com/microsoft/DirectXTK12/wiki/SpriteBatch#pixel-centers

from directxtk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.