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

Author Topic: Scrolling tilemap issue: black bars  (Read 5371 times)

0 Members and 1 Guest are viewing this topic.

Laguna_1989

  • Newbie
  • *
  • Posts: 4
    • View Profile
Scrolling tilemap issue: black bars
« on: November 04, 2020, 03:24:49 pm »
Hi,

I have an issue with scrolling tilemaps. Black bars appear sometimes, as you can see in the attached gif.

This happens regardless of vsync on of off, also a framerate limit does not help.

Some additional information:
SFML-2.5.1, win 10, 64 bit
The renderTarget is cleared with black every frame. After that the tilemap is rendered. I do not have a sprite for every single tile but one per tile. E.g. one water tile and one grass tile. I then loop over all tiles and check if I want to draw water or grass. the respective sprite is positioned and drawn.

The camera/scrolling is implemented using the sf::view move() function. I take the framtime into account ,e.g. view->move(scrollspeed * elapsedTime, 0);

Is this possibly some floating point/pixel alingment issue?

Best,
Laguna

Arcade

  • Full Member
  • ***
  • Posts: 229
    • View Profile
Re: Scrolling tilemap issue: black bars
« Reply #1 on: November 04, 2020, 03:39:28 pm »
Quote
Is this possibly some floating point/pixel alingment issue?

It could be. Similar problems comes up fairly often on these forums, so if you search around you might find other threads of people talking about why this happens and some possible solutions. Here's one example.

Laguna_1989

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Scrolling tilemap issue: black bars
« Reply #2 on: November 04, 2020, 06:42:26 pm »
Thanks for the quick reply. I wasn't aware this is an opengl issue.

There are two possible fixes:

a) As the tilemap is drawn on the complete screen anyway, I could just omit the clearing of the rendertarget.
b) Make sure the view position is only set to integer positions instead of pixel positions.The following code fixed it for me:

    sf::Vector2i camOffsetInt { static_cast<int>(m_CamOffset.x + getView()->getSize().x / 2),
        static_cast<int>(m_CamOffset.y + getView()->getSize().y / 2) };

    getView()->setCenter(
        sf::Vector2f { static_cast<float>(camOffsetInt.x), static_cast<float>(camOffsetInt.y) });



Best,
Laguna

Stauricus

  • Sr. Member
  • ****
  • Posts: 338
    • View Profile
    • Email
Re: Scrolling tilemap issue: black bars
« Reply #3 on: November 05, 2020, 12:39:54 am »
a) As the tilemap is drawn on the complete screen anyway, I could just omit the clearing of the rendertarget.
i'm almost sure that it could lead to some artifacts anyway (they just aren't going to be black)

b) Make sure the view position is only set to integer positions instead of pixel positions.
i think that its not on the view, but mainly the tiles positions. try to position them with integers instead of floats.

Hapax

  • Hero Member
  • *****
  • Posts: 3071
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Scrolling tilemap issue: black bars
« Reply #4 on: November 05, 2020, 04:14:40 pm »
a) As the tilemap is drawn on the complete screen anyway, I could just omit the clearing of the rendertarget.
I think I understand where you are going with this thought but not clearing is a bit of a 'hacky', messy way. If the intention is to leave similar texture behind so the black bars are not noticeable by "background drawing", you could (after clearing) simply draw the tiles in their previous position first before drawing them where they should be. Not only would this be the effect I think you are aiming for, it's more purposeful but - more importantly - correct, as not clearing the target would actually leave the image from two frames ago due to double buffering.

Another thing to note is that although the black lines can be due to those areas not being drawn, it's more likely to be parts of the black texture around the part you want creeping in and this means that those black parts are actually drawn with the tiles so would cover anything below anyway.
Extending tiles by a pixel is a simple solution. You can also sometimes get away with shrinking the texture rectangle by a fraction (half is most secure but 0.2 could make a serious reduction on artifacts) of a pixel on each edge but this is better when the texture doesn't have to be "pixel perfect".
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Laguna_1989

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Scrolling tilemap issue: black bars
« Reply #5 on: November 05, 2020, 04:47:40 pm »
i think that its not on the view, but mainly the tiles positions. try to position them with integers instead of floats.

It solved the issue for me and it makes perfect sense, why that is. tiles are always placed on full tile positions.
In the version without the fix, the view was position on subpixel coordinates (view position is float and you could easily move it by 0.1 of a pixel, which lead to the opengl glitch).
In the fixed version the view can only be placed at full pixel positions.

Extending tiles by a pixel is a simple solution. You can also sometimes get away with shrinking the texture rectangle by a fraction (half is most secure but 0.2 could make a serious reduction on artifacts) of a pixel on each edge but this is better when the texture doesn't have to be "pixel perfect".
This soulds like a lot of work, as I load the tiles from one tileset image, which is also used in Tiled.
If you have one image per tile, this might be an easier solution.

Hapax

  • Hero Member
  • *****
  • Posts: 3071
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Scrolling tilemap issue: black bars
« Reply #6 on: November 05, 2020, 06:06:34 pm »
To be more clear, I was referring to changing the texture rectangle only. That is, add (n, n) to the rectangle's position and subtracting (n, n) * 2 from the rectangle's size, where n is the fractional amount.

The main issue is that OpenGL doesn't know for certain which pixel it should use when the value is a whole number because that whole number borders two pixels, not just one, so is affected by floating point precision since it could be either side of that whole number.
e.g. (1, 1) could be (simplified):
a) (1.0000001, 1.0000001)
b) (1.0000001, 0.9999999)
c) (0.9999999, 1.0000001)
d) (0.9999999, 0.9999999)
When it's >=1, it's one pixel (1 - 1.99999999...); when it's <1 it's a different one (0 - 0.9999999...)

Again, the usually simpler solution is to just add an extra pixel of the image around each one in the texture, then still set the texture rectangle to ignore that pixel. If it "spills" into the surrounding area (due the reason shown above), it will still be the expected pixel.


Remember, the problem isn't the position of the vertices but the texture co-ordinates. However, these can be unreliable if vertices are not integers.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Wake

  • Newbie
  • *
  • Posts: 11
    • View Profile
    • Email
Re: Scrolling tilemap issue: black bars
« Reply #7 on: May 19, 2021, 04:21:42 am »
I had the exact same problem. Big, ugly, obvious black lines between tiles.

Rendering everything to a sf::RenderTexture got rid of it.

Below is my RenderTexture Initialization Function & Render Function from my GameState:

Code: [Select]
void GameState::initRenderTexture()
{
this->renderTexture.create(this->window->getSize().x, this->window->getSize().y);
this->renderTexture.setSmooth(true);
this->renderSprite.setTexture(this->renderTexture.getTexture());
this->renderSprite.setTextureRect(sf::IntRect(0, 0, this->window->getSize().x, this->window->getSize().y));
}

void GameState::render(sf::RenderTarget* target)
{
if (!target)
target = this->window;

/*Items Rendered to Render Texture*/
this->renderTexture.clear();
this->renderTexture.setView(this->view);
this->renderTileMap(this->renderTexture);
this->renderSprite.setTexture(this->renderTexture.getTexture());
this->renderPlayer(this->renderTexture);
this->renderTexture.display();
target->draw(this->renderSprite);

/*Items Rendered with Default Window View*/
this->window->setView(this->defaultWindowView);
if (this->isPaused)
this->renderPauseMenu(*target);
}

I'm still getting some weird less-noticeable artifacts and have come to the conclusion that rendering lots of little 32.f x 32.f sprite tiles is bad practice.

You essentially want to use larger sprites and fit as much as possible into the .png texture via GIMP or Photoshop.

I'm using this guy's RPG level Tileset for practice: https://pipoya.itch.io/pipoya-rpg-tileset-32x32.

For example, his trees are made up of 4 32.f x 32.f squares... You don't want to set trees like this... You actually want to grab the entire 64.f x 64.f tree... Or better yet, a larger bundle of trees... As large a possible to fit your needs.

The only thing you can do is make it less noticeable is by fitting as much as possible into the PNG texture and making the tile square as large as possible.

Even with this advice, you'll never get 100% avoidance of floating point artifact issues. You'll still get them on the edges of places where tiles are placed next to each other.

Overall... Less Environmental Tiles = Less Floating Point Tile Artifacts plain and simple...

Hope this helps people save some time searching for an answer to this problem :)
« Last Edit: May 19, 2021, 04:29:00 am by Wake »

 

anything