1
Graphics / Re: Graphical glicth: horizontal lines in SFML2
« on: December 16, 2019, 01:11:01 am »
Hey everybody! I know this thread is quite old, but I found a solution that solves the problem perfectly, while maintaining floating point movement.
Laurent suggested that we round our movements units to ints in order to achieve pixel perfect rendering, and (s)he is about half right.
Rounding to ints works if we haven't zoomed our view at all, because at this point
1 pixel = 1 movement unit
Sounds simple, right?
Well, this all craps the bed if we zoom in our view, let's say by 4.
view.zoom(1/4.f);
What we've just done here is made the width and height of each pixel = 4 pixels. As a result of that,
1 movement unit = 4 pixels
or
1 pixel = .25 movement unit
If we were to say
player.move(1, 0);
our player would actually move 4 pixels to the right instead of just 1. This looks choppier than a hyperventilating ninja on redbull. Here's where we need floating point precision so that we can move our player 1 pixel at a time instead of 4. Now,
1 pixel = .25 movement unit
So if we wanted to move our player just 1 pixel to the right, we would say
player.move(.25, 0);
But what happens if our players movement isn't exactly equal to the amount required to move 1 pixel?
ie
player.move(.16, 0);
In that case, our view might draw the player at the screen pixel location of 0, 0 or 1, 0, depending on if we've moved our camera(sf::View) at any time in the game. If we went
view.setCenter(somenumber.14, someothernumber.0);
then it would place our player at 1, 0 because .16 +.14 = .31 which is > .25.
This is where the weird vertical lines come into play, because SFML isn't really sure in which pixel to place our scene.
To solve the problem, all we have to do is keep our movements at multiples of our scaling factor. In our case, we just round our movements to multiples of .25, which is 1/4, and this will achieve pixel perfect rendering.
To implement:
#include <bits/stdc++.h> //for fmod
float ZOOM_SCALE = 4;
sf::Vector2f roundVector2f(sf::Vector2f p_vector, float p_scale)
{
sf::Vector2f diff = sf::Vector2f(fmod(p_vector.x, 1/p_scale), fmod(p_vector.y, 1/p_scale));
p_vector.x -= diff.x;
p_vector.y -= diff.y;
return p_vector;
}
int main()
{
//some code
sf::View view(sf::Vector2f(0.f, 0.f), sf::Vector2f(1280, 720));
view.zoom(1/ZOOM_SCALE);
//if we move the camera
sf::Vector2f camPos = sf::Vector2f(234.12, 434.45);
view.setCentre(roundVector2f(camPos, ZOOM_SCALE);
//if we move our player
sf::Vector2f vel = sf::Vector2f(1.1, 2.6);
player.move(roundVector2f(vel, ZOOM_SCALE);
}
Hope this helps
Laurent suggested that we round our movements units to ints in order to achieve pixel perfect rendering, and (s)he is about half right.
Rounding to ints works if we haven't zoomed our view at all, because at this point
1 pixel = 1 movement unit
Sounds simple, right?
Well, this all craps the bed if we zoom in our view, let's say by 4.
view.zoom(1/4.f);
What we've just done here is made the width and height of each pixel = 4 pixels. As a result of that,
1 movement unit = 4 pixels
or
1 pixel = .25 movement unit
If we were to say
player.move(1, 0);
our player would actually move 4 pixels to the right instead of just 1. This looks choppier than a hyperventilating ninja on redbull. Here's where we need floating point precision so that we can move our player 1 pixel at a time instead of 4. Now,
1 pixel = .25 movement unit
So if we wanted to move our player just 1 pixel to the right, we would say
player.move(.25, 0);
But what happens if our players movement isn't exactly equal to the amount required to move 1 pixel?
ie
player.move(.16, 0);
In that case, our view might draw the player at the screen pixel location of 0, 0 or 1, 0, depending on if we've moved our camera(sf::View) at any time in the game. If we went
view.setCenter(somenumber.14, someothernumber.0);
then it would place our player at 1, 0 because .16 +.14 = .31 which is > .25.
This is where the weird vertical lines come into play, because SFML isn't really sure in which pixel to place our scene.
To solve the problem, all we have to do is keep our movements at multiples of our scaling factor. In our case, we just round our movements to multiples of .25, which is 1/4, and this will achieve pixel perfect rendering.
To implement:
#include <bits/stdc++.h> //for fmod
float ZOOM_SCALE = 4;
sf::Vector2f roundVector2f(sf::Vector2f p_vector, float p_scale)
{
sf::Vector2f diff = sf::Vector2f(fmod(p_vector.x, 1/p_scale), fmod(p_vector.y, 1/p_scale));
p_vector.x -= diff.x;
p_vector.y -= diff.y;
return p_vector;
}
int main()
{
//some code
sf::View view(sf::Vector2f(0.f, 0.f), sf::Vector2f(1280, 720));
view.zoom(1/ZOOM_SCALE);
//if we move the camera
sf::Vector2f camPos = sf::Vector2f(234.12, 434.45);
view.setCentre(roundVector2f(camPos, ZOOM_SCALE);
//if we move our player
sf::Vector2f vel = sf::Vector2f(1.1, 2.6);
player.move(roundVector2f(vel, ZOOM_SCALE);
}
Hope this helps