SFML community forums
Help => Graphics => Topic started by: easy on August 19, 2011, 01:38:43 pm
-
Hello!
The level is made of 32x32 sprites, and they all share a texture (a tileset). I use no smoothing on the texture, but a camera (a view) to adjust where to render the tilemap.
Sometimes it renders with a horizontal lines under the tiles (see the screenshot), and this glitch remains while moving the camera horizontally.
(http://kepfeltoltes.hu/thumb/110819/sfml2-rendering-glitch_www.kepfeltoltes.hu_.png) (http://kepfeltoltes.hu/view/110819/sfml2-rendering-glitch_www.kepfeltoltes.hu_.png)
I guess it's related to the floating-point rendering, how can I avoid it?
Thanks in advance,
easy
P.S. My operating system is Linux Mint Debian.
-
Same issue here: http://sfml-dev.org/forum/viewtopic.php?p=37862, found this after i posted other. Laurent please do something!
-
It's a known limitation of the rendering system in SFML 2, you must use integer coordinates to get a pixel perfect rendering. Unfortunately this is very hard to achieve with a custom view: it's not the coordinates in the 2D scene that must be integer, but the coordinates after being transformed by the current view. The simplest workaround is to manage to keep the view at integer coordinates too.
-
It's a known limitation of the rendering system in SFML 2, you must use integer coordinates to get a pixel perfect rendering. Unfortunately this is very hard to achieve with a custom view: it's not the coordinates in the 2D scene that must be integer, but the coordinates after being transformed by the current view. The simplest workaround is to manage to keep the view at integer coordinates too.
So you're saying its either smooth movement och proper drawing ? :/ Is there any upcoming fix for this ? Like a .. sf::View::ForceInteger(true); or something?
Guess ill have to go back to saving each tile as a separate image for now.
-
Thanks Laurent!
So the solution is:
- To have a vector for smooth movement: "sf::Vector2f cameraPosition;"
- And to convert floats to integers when repositioning the view: "view.setCenter(sf::Vector2i(cameraPosition));"
That will work! :)
-
Thanks Laurent!
So the solution is:
- To have a vector for smooth movement: "sf::Vector2f cameraPosition;"
- And to convert floats to integers when repositioning the view: "view.setCenter(sf::Vector2i(cameraPosition));"
That will work! :)
It does not for my case tho, since i magnify the view 2x, so the following will be really choppy. Tested this, also, it would work if i used integer types for playermovement as well, but i really need float types for precise hw-independent movement/collision detection etc.
-
So you're saying its either smooth movement och proper drawing ?
Absolutely.
Is there any upcoming fix for this ? Like a .. sf::View::ForceInteger(true); or something?
There's no possible fix, it depends on:
- the entity's transformations
- the view
- the viewport
So the solution is:
- To have a vector for smooth movement: "sf::Vector2f cameraPosition;"
- And to convert floats to integers when repositioning the view: "view.setCenter(sf::Vector2i(cameraPosition));"
That should work, but be careful with odd sizes, since the view's position is its center, maybe you need a +0.5 sometimes.
-
Ohwell, conclusion; If you are in the same position I am in, do what I just did;
Cache your tiles with as an sf::Texture that is WHOLE of your tile(part of your tileset), and just draw that instead. Took the opportunity to fit this into my Tile::GenerateCache() (for collisionmap etc), it eats a little bit more memory but for these kind of projects it should be all right!
-
That should work, but be careful with odd sizes, since the view's position is its center, maybe you need a +0.5 sometimes.
Thanks for the hint!
Ohwell, conclusion; If you are in the same position I am in, do what I just did;
Cache your tiles with as an sf::Texture that is WHOLE of your tile(part of your tileset), and just draw that instead. Took the opportunity to fit this into my Tile::GenerateCache() (for collisionmap etc), it eats a little bit more memory but for these kind of projects it should be all right!
I've got all my tile textures on one tileset texture already, and all the sprites has this large texture applied, and I call .SetSubRect on them. So just packing all the small textures into a big one does not help; you'll need to do the convert-to-integer trick above.
-
I've got all my tile textures on one tileset texture already, and all the sprites has this large texture applied, and I call .SetSubRect on them. So just packing all the small textures into a big one does not help; you'll need to do the convert-to-integer trick above.
Well I explained above why I cant use the integertrick, also it works kinda fine . Resource for textures loads in the tilesheet, every tile from that sheet caches their tile as a texture. No big graphics were talking here.
-
Here comes my solution:
/* Hack to get rid of the annoying horizontal and vertical
lines that are caused by floating point rendering */
sf::Vector2f correction = sf::Vector2f((int(view.GetSize().x) % 2 == 0 ? 0 : 1) * 0.5f,
(int(view.GetSize().y) % 2 == 0 ? 0 : 1) * 0.5f);
view.SetCenter(floor(position.x) + correction.x, floor(position.y) + correction.y);
This works, and the choppiness of the movement is barely noticable.
Well I explained above why I cant use the integertrick, also it works kinda fine . Resource for textures loads in the tilesheet, every tile from that sheet caches their tile as a texture. No big graphics were talking here.
Oh then I've misunderstood you. I thought you're talking about using a tileset texture. But honestly I still don't get it what you're doing :D
-
Hello everyone, this is my first post. I've been using SFML for a while now and I love it. Thanks for your hard work, Laurent.
I have this unwanted line problem also. I'm confused as to what I need to do. Using easy's advice to convert the view to integer coordinates doesn't work for me. And Haikarainen, I don't understand what you have suggested. Could you clarify please?
My view never moves. I render my game at 1280x720 and use the view to scale it up or down, depending on the resolution. Here is where I set my view:
MainView->Reset(sf::FloatRect(0, 0, 1280, 720));
MainView->SetViewport(sf::FloatRect(0.0f, 0.0f, 1.0f, 1.0f));
MainWindow->SetView(*MainView);
That never changes. I get the lines when I'm not rendering to a native 1280x720 resolution or window size. Does that mean I'm out of luck? Is there any hack I can apply? Am I doing this completely wrong and should I be using another technique instead of using the view to scale my game?
Edit: I just looked into RenderTexture. Would this be the correct way? I could render the entire scene into a texture and then scale that to the window? Would there be a performance hit for that?
Edit again: I just tried the RenderTexture. It was so easy to change over. It works perfect now. I can scale my game to any resolution without the lines showing up. I'd still like to know if I have the right idea though.
-
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 :)