Welcome, Guest. Please login or register.

Author Topic: [SOLVED]Float precision and pixel accuracy  (Read 462 times)

0 Members and 1 Guest are viewing this topic.

Elias Daler

  • Sr. Member
  • ****
  • Posts: 478
    • View Profile
    • Blog / Re:creation
    • Email
[SOLVED]Float precision and pixel accuracy
« on: March 05, 2017, 12:15:32 pm »
So, this isn't a novel problem, but so far I haven't seen perfect solution to it.
My game runs at 60 FPS and I use fixed delta time for FPS. Delta is sf::seconds(1/60), so something like 0.016666007.

I tried using rounding and std::floor for positions, but float inaccuracies ruined everything, especially if there were objects which moved with the same speed (hero on platform, for example). Moving them by the same offset caused sprites to be rendered with different offset. (platform or hero would have a bit different offset to each other during some frames).

I guess the best solution is to store position in integers with some subpixel variable indicating 1/4th (1/8, 1/16?) of a pixel. Another way is to use fixed point arithmetic. (Pixel games don't need much sub-pixel precision because to achieve smooth movement I have to make speeds some factors of 60)

Thanks to sf::Vector<T> being customizable, that shouldn't be hard to implement. Does anyone know how to implement this efficiently?
« Last Edit: April 19, 2017, 07:30:52 pm by Elias Daler »

Ushi

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Float precision and pixel accuracy
« Reply #1 on: March 05, 2017, 10:46:12 pm »
In my engine entitiy's have these two member variables:

sf::Vector2f m_position;
sf::Vector2i m_coord;

all of the physics calculations happen using the position variable, like so:

m_velocity += m_acceleration;
m_position += m_velocity;

A couple notes: 1, this assumes that the velocity is in units of [pixels moved per update], otherwise we would need a time variable. 2, this is called symplectic euler integration and it really only works deterministically if acceleration only changes instantaneously.

Then, right before drawing, I round the position of the entity like this:

m_coord = sf::Vector2i(std::round(m_position.x), std::round(m_position.y));

Then the drawing uses m_coord instead of m_position.

All the physics happens smoothly and the sprites never stray from the pixels.
« Last Edit: March 05, 2017, 10:53:10 pm by Ushi »

Elias Daler

  • Sr. Member
  • ****
  • Posts: 478
    • View Profile
    • Blog / Re:creation
    • Email
Re: Float precision and pixel accuracy
« Reply #2 on: March 10, 2017, 07:51:07 am »
Yep, that's a method I used, but it fails if you move two objects at the same speed and if they have slight position differences (e.g. 0.5f)


Suppose that grey rectangle is hero and yellow rectangle is a box. As they both move by 0.2 to the right, their position changes and may cause some jittering, because the player's position on the screen will change by 1 pixel in some frames.

The solution is to round player's and platform's positions so that they have the same fractional part and rounding their position doesn't change the offset. But maybe there are better solutions? :D

dabbertorres

  • Sr. Member
  • ****
  • Posts: 433
    • View Profile
    • website/blog
Re: Float precision and pixel accuracy
« Reply #3 on: March 11, 2017, 04:15:46 am »
std::round rounds up for 0.5 >=, and rounds down for < 0.5.
That could be the cause.

I'd be surprised if std::ceil/std::floor/std::trunc has the same issue.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 31106
    • View Profile
    • SFML's website
    • Email
Re: Float precision and pixel accuracy
« Reply #4 on: March 11, 2017, 11:07:35 am »
Whether the rounding limit is at 0.5, 0 or whatever, there is still one, and around that limit you will still have X that rounds to N and X + 0.00001 that rounds to N+1. Using a different rounding function will never change that, it's how rounding works.
Laurent Gomila - SFML developer

dabbertorres

  • Sr. Member
  • ****
  • Posts: 433
    • View Profile
    • website/blog
Re: Float precision and pixel accuracy
« Reply #5 on: March 11, 2017, 05:15:05 pm »
Ah yes, I was incorrectly assuming the whole parts of both values would be the same, like in Elias' image.

Another solution would be to just embrace it.
I mean, how often do things stay perfectly still on top of moving things? :)
Of course, that depends on the amount of jittering.

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 834
    • View Profile
Re: Float precision and pixel accuracy
« Reply #6 on: March 12, 2017, 10:03:44 am »
That's a problem even old consoles like the NES or SNES had with their backgrounds, you just never noticed it, because they were either drawn in a way to hide that problem or the screen was just too blurry to notice. There'll always be rounding issues.

One potential solution would be to first always round down and second use a fixed precision/fraction you have control of.

For example, NES/SNES games usually use integer math, even for fine scaled things such as movement.

Let's assume your maximum map size is 256x256 tiles and tiles are 16 pixels in size. How would coordinates work? Assuming you're using 32 bit unsigned integers, you've got:

16 bits to identify the tile (0-255)
8 bits to identify the pixel (0-15)
That leaves 8 bits for sub-pixel accuracy.

Of course you could split this or just use a 64 bit integer for example for more control.

As a bonus, this makes identifying offsets/locations a breeze:

position to pixel: position >> 8
position to tile: position >> 16

You could of course add more dimensions, for example screens in the world, so you could have 16x16 tilemaps/screens by adding another 16 bits.

But either way, I'd say the trick is to not only round when displaying, but also when updating/moving. Of course this will limit your possibilities, e.g. at some point movement would be too slow and just drop out.

Hapax

  • Hero Member
  • *****
  • Posts: 2308
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Float precision and pixel accuracy
« Reply #7 on: March 13, 2017, 11:57:42 am »
You could consider implementing some form of "handle" - that is for things that are meant to move together (for example, a player and a box that they are carrying). One would need to be temporary "owned" (or parented/linked) by the other (probably player parents box). You can then calculate the difference when rounded and then position the box dependant on the player's currently rounded position plus that difference. That way, the box will only move its rounded position when the player does.
Selba Ward - SFML drawables
Kairos - Timing Library
Splashentation - Splash Window Presentation

@Hapaxiation - Hapaxia

Elias Daler

  • Sr. Member
  • ****
  • Posts: 478
    • View Profile
    • Blog / Re:creation
    • Email
Re: Float precision and pixel accuracy
« Reply #8 on: April 19, 2017, 07:30:43 pm »
You could consider implementing some form of "handle" - that is for things that are meant to move together (for example, a player and a box that they are carrying). One would need to be temporary "owned" (or parented/linked) by the other (probably player parents box). You can then calculate the difference when rounded and then position the box dependant on the player's currently rounded position plus that difference. That way, the box will only move its rounded position when the player does.
Yep, that's what I did in the end. :)

Mario, thanks for your post as well, it's interesting to know!