Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Subtle positioning behavior  (Read 9088 times)

0 Members and 1 Guest are viewing this topic.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Subtle positioning behavior
« on: April 30, 2015, 10:57:21 am »
First, this is not a SFML bug, yet behavior that one would probably not expect as a user. I stumbled upon it yesterday, after debugging almost an hour and searching in the wrong places :P

There is a bug in the following code, leading to a rendering that doesn't appear smooth when the sprites move.
// Two sprites. Assume they're initialized with texture and everything.
// Origin is (0, 0) and rotation is 0 at all times.
sf::Sprite sprite;
sf::Sprite sprite2;

// Align sprite to integral coordinates (pixel-perfect rendering)
sprite.setPosition(std::round(...), std::round(...));

// Second sprite starts at the center of first.
// Also round sprite2 for pixel-perfect rendering
sf::FloatRect bounds = sprite.getGlobalBounds();
sf::Vector2f center(
        bounds.left + std::round(bounds.width / 2.f),
        bounds.top + std::round(bounds.height / 2.f));
sprite2.setPosition(center);

Does anybody spot it? ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Subtle positioning behavior
« Reply #1 on: April 30, 2015, 01:29:41 pm »
Seems to work as expected here.

Is it that the sprite2's upper left corner will be on the center of sprite instead of the center of the sprite2?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Subtle positioning behavior
« Reply #2 on: April 30, 2015, 01:54:44 pm »
No, that's intended. The bug only occurs in rare situations, that's why it was so subtle and difficult to spot. Mostly, things work just fine.

I wouldn't try to test the code, it's more likely to find out by analyzing it.

Does anybody still want to guess? I'll give the solution soon, but thinking a bit about what this exactly does reveals some interesting SFML internals... and can save you from similar bugs in the future :)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Subtle positioning behavior
« Reply #3 on: April 30, 2015, 05:47:57 pm »
Could you explain a bit more 'doesn't appear smooth when the sprites move'? (Without visual representation of the bug it's hard to get exactly what you mean.)

Wild guess: some rounding issue around some particular value (like 0) makes the sprites jitter?
SFML / OS X developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Subtle positioning behavior
« Reply #4 on: April 30, 2015, 06:09:30 pm »
Wild guess: some rounding issue around some particular value (like 0) makes the sprites jitter?
Good guess 8)

Here's the full explanation. The (in my opinion) unexpected behavior is:

Even if the sprite's size does not change, sf::Sprite::getGlobalBounds() may return a rectangle of different size.

This can even occur when only the position changes. I was quite surprised, but when looking at the implementation, it makes sense. getGlobalBounds() multiplies the current sf::Transform with the local rect (constant, retrieved by getLocalBounds()). In the process, floating point rounding errors occur, so that a sprite with height 39 can have global bounds of height 38.9999. While std::round(bounds.height / 2.f) would be std::round(19.5) == 20 in most cases, it will be std::round(19.49999) == 19 in this case. That's one pixel difference, which is very noticeable, especially while moving. At me, the offset constantly fluctuated between 19 and 20, leading to a jittering object.

Now, how to fix this? A quick solution for this case would be to add an epsilon number like 0.1 before rounding. To retrieve only the sprite's untransformed size, one can use the local bounds or the texture rect's dimensions directly. They are constant and integral, thus exact. But what for scaled sprites? When you multiply those with the scale, you have the rounding errors again. So I guess we still have to use the epsilon.
« Last Edit: April 30, 2015, 06:11:14 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Subtle positioning behavior
« Reply #5 on: April 30, 2015, 07:11:02 pm »
Not easy to fix... but it's a nice challenge!  :D

I'm wondering, if you use std::trunc instead, does it work better or does it break more? (I guess that truncating near 0 will be worse but that should only happen with very very small sprite size if I'm not mistaken.)

Otherwise you would have to make some kind of interpolated rounding by using the history of the value (here just the previous value but it could be extended to more if the jitter is still present):
tmp = round(value/2)
if (abs(tmp - old) <= 1) { // prevent jitter
    new = old
} else {
    new = tmp
}
old = tmp

// use `new` for drawing
 

Could something like that work?
SFML / OS X developer

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
AW: Subtle positioning behavior
« Reply #6 on: April 30, 2015, 07:20:40 pm »
What about floor() or ceil()?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Subtle positioning behavior
« Reply #7 on: April 30, 2015, 07:45:44 pm »
Different rounding functions were also my first thought, but they only shift the problem. Let's assume our sprite's height is approximately 40, so we want to get 20. It's easy to construct cases that fail:
// std::ceil()
ceil(bounds.height / 2.f) == ceil(40.0001 / 2) == 21

// std::floor()
floor(bounds.height / 2.f) == floor(39.9999 / 2) == 19

// std::trunc()
trunc(bounds.height / 2.f) == trunc(39.9999 / 2) == 19

I think the most robust solution is really to be far from the rounding "boundaries", for example by adding +0.5.
Or use integer division in the first place.
// Bad:  std::round(bounds.height / 2.f)
// Good:  round((bounds.height + 0.5f) / 2.f)

// We want to map both 39 and 40 to 20. This works:
round((40.0001 + 0.5) / 2) == round(20.25005) == 20
round((39.9999 + 0.5) / 2) == round(20.24995) == 20
round((38.9999 + 0.5) / 2) == round(19.75005) == 20
round((38.9999 + 0.5) / 2) == round(19.74995) == 20

If values can be negative (not relevant for sizes), we should replace std::round(x) with a custom rounding function std::floor(x + 0.5), which rounds towards positive infinity independent of the sign. Otherwise, jittering will still occur for values around zero.

Hiura, taking history into account may generally be a good method to smooth such patterns, but here it looks more like symptom treatment than solving the underlying problem ;)
« Last Edit: April 30, 2015, 07:56:54 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

OrientatedCookie

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: Subtle positioning behavior
« Reply #8 on: May 01, 2015, 08:58:54 pm »
First, this is not a SFML bug, yet behavior that one would probably not expect as a user. I stumbled upon it yesterday, after debugging almost an hour and searching in the wrong places :P

There is a bug in the following code, leading to a rendering that doesn't appear smooth when the sprites move.
// Two sprites. Assume they're initialized with texture and everything.
// Origin is (0, 0) and rotation is 0 at all times.
sf::Sprite sprite;
sf::Sprite sprite2;

// Align sprite to integral coordinates (pixel-perfect rendering)
sprite.setPosition(std::round(...), std::round(...));

// Second sprite starts at the center of first.
// Also round sprite2 for pixel-perfect rendering
sf::FloatRect bounds = sprite.getGlobalBounds();
sf::Vector2f center(
        bounds.left + std::round(bounds.width / 2.f),
        bounds.top + std::round(bounds.height / 2.f));
sprite2.setPosition(center);

Does anybody spot it? ;)
Should be in general discussion because it is not a help request. And no I don't see a problem.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Subtle positioning behavior
« Reply #9 on: May 02, 2015, 12:00:48 pm »
Should be in general discussion because it is not a help request.
It's a conversation about bugs that may arise when using sfml-graphics, and not a general design discussion about SFML.

And no I don't see a problem.
Maybe you should read the thread again ;)

Also, please avoid full quotes where possible.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

OrientatedCookie

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: Subtle positioning behavior
« Reply #10 on: May 02, 2015, 03:46:21 pm »
Should be in general discussion because it is not a help request.
It's a conversation about bugs that may arise when using sfml-graphics, and not a general design discussion about SFML.

But it's not a help request either.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Subtle positioning behavior
« Reply #11 on: May 02, 2015, 04:39:08 pm »
If you feel something needs moderation use the "Report to moderator" link and let the moderators decide what should happen, but don't start a discussion on how things should be moderated.

If you feel something needs a longer discussion and that it might be useful to get more opinions in, feel free to open a thread in the SFML website sub-forum.
« Last Edit: May 02, 2015, 09:14:56 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

OrientatedCookie

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: Subtle positioning behavior
« Reply #12 on: May 02, 2015, 08:51:57 pm »
Agknoleged.