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

Author Topic: texture smoothing causes render artifacts  (Read 1771 times)

0 Members and 3 Guests are viewing this topic.

zTn

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
texture smoothing causes render artifacts
« on: November 29, 2023, 04:11:58 pm »
I have a simple tilemap game engine that shows vertical and horizontal line artifacts, but only when sf::Texture::setSmooth() is set to true.  Attached is a simplified version of the game engine that demonstrates the problem, along with screenshots for when smoothing is turned on and off.

Simply turning off texture smoothing completely eliminates the artifacts, so either there is a bug here or I'm going about this wrong.

Any help is greatly appreciated!
Code: [Select]
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <string>
#include <vector>

const sf::IntRect mapCharToTileRect(const char ch)
{
    if (ch == 'R') return { 0, 0, 32, 32 };
    else if (ch == 'G') return { 32, 0, 32, 32 };
    else if (ch == 'Y') return { 32, 32, 32, 32 };
    else return { 0, 32, 32, 32 };
}

void fit(sf::Sprite & sprite, const sf::Vector2f & size)
{
    const float scaleHoriz{ size.x / sprite.getLocalBounds().width };
    sprite.setScale(scaleHoriz, scaleHoriz);

    if (sprite.getGlobalBounds().height > size.y)
    {
        const float scaleVert{ size.y / sprite.getLocalBounds().height };
        sprite.setScale(scaleVert, scaleVert);
    }
}

int main(void)
{
    sf::RenderWindow window({ 640, 480, 32 }, "test");

    sf::RenderTexture renderTexture;
    renderTexture.create(64, 64);
    renderTexture.setSmooth(true); // *** artifacts disapear if false ***
    renderTexture.clear();

    sf::RectangleShape rectangleShape;
    rectangleShape.setSize({ 32.0f, 32.0f });

    rectangleShape.setPosition(0.0f, 0.0f);
    rectangleShape.setFillColor(sf::Color::Red);
    renderTexture.draw(rectangleShape);

    rectangleShape.setPosition(32.0f, 0.0f);
    rectangleShape.setFillColor(sf::Color::Green);
    renderTexture.draw(rectangleShape);

    rectangleShape.setPosition(0.0f, 32.0f);
    rectangleShape.setFillColor(sf::Color::Blue);
    renderTexture.draw(rectangleShape);

    rectangleShape.setPosition(32.0f, 32.0f);
    rectangleShape.setFillColor(sf::Color::Yellow);
    renderTexture.draw(rectangleShape);

    renderTexture.display();

    const std::vector<std::string> map = {
        "                  ",
        "   R              ",
        "   G          R   ",
        "   G          G   ",
        "   GGGGGGG    G   ",
        "   GGYGYGGGGGGG   ",
        "   GGGGGGG        ",
        "   GGGYGGGGGG     ",
        "   GGGGGGG  G     ",
        "            G     ",
        "       RGGGGG     ",
        "                  "
    };

    const sf::Vector2f tileSize(40.0f, 40.0f);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (sf::Event::Closed == event.type)
            {
                window.close();
            }
        }

        window.clear();

        const sf::Vector2i mapSize(map.front().size(), map.size());
        for (int y(0); y < mapSize.y; ++y)
        {
            for (int x(0); x < mapSize.x; ++x)
            {
                const char mapChar = map[y][x];
                sf::Sprite sprite(renderTexture.getTexture(), mapCharToTileRect(mapChar));
                fit(sprite, tileSize);
                sprite.setPosition((x * tileSize.x), (y * tileSize.y));
                window.draw(sprite);
            }
        }

        window.display();
    }

    return 0;
}
 

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: texture smoothing causes render artifacts
« Reply #1 on: December 02, 2023, 03:21:56 pm »
The smoothing of Render Texture (and also just Texture) smooth out pixels, especially when stretched (or squashed) so that pixels might not map 1:1 if scaled by a fraction.

It seems that your tiles are 32x32 in the (render) texture but you're scaling them to be displayed at 40x40 so, when rendering, there may be some bleed as you can see from your artifacts. The artifacts are the bleed from the neighbouring colour.

I'm not sure why you want to smooth solid colours exactly so you could easily just leave smoothing off to get the desired effect but I'm guessing you may be testing for using an actual texture. In which case, you'll need to be aware that this is a common problem when using tiles at non-perfect integer pixels. Smooth only adds to this 'error'. A usual easy fix for this is to make the texture rectangle smaller (by a pixel margin) so you could just do this and crop the outside pixel edge or duplicate the outside pixel edge and then use the normal rectangle (cropping off the duplicate edge). With smoothing, you may need extra cropping to account for the "blurred" pixels.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

zTn

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
Re: texture smoothing causes render artifacts
« Reply #2 on: December 02, 2023, 08:45:31 pm »
Thanks for responding Hapax!

"I'm not sure why you want to smooth solid colours..."

Indeed, this is just a solid color example of the problem I'm seeing in the actual game engine that uses textures that are not simple solid colors.


"...this is a common problem when using tiles at non-perfect integer pixels..."

Please help me understand this.  What about my example texture in the code provided is a "non-perfect integer pixel"?  How can I create source texture tilemaps that ARE perfect integer pixels.  I would gladly do whatever this is to avoid having to create tilemaps that have transparent buffer zones around each tile.  I'm just interested in learning how to do this the smart way, or whatever way everyone else does it to avoid this bug.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: texture smoothing causes render artifacts
« Reply #3 on: December 03, 2023, 02:18:24 am »
Thanks for responding Hapax!
You're very welcome!

Please help me understand this.  What about my example texture in the code provided is a "non-perfect integer pixel"?
You're stretching a 32-pixel wide image across 40-pixels.

First, you have to know that you should consider a pixel to be a rectangle with co-ordinates at the corners. So, at 1:1 scaling ratio, a pixel at "0, 0" would be a rectangle from (0, 0) to (1, 1) and a pixel at "3, 2" would be a rectangle from (3, 2) to (4, 3).
When calculating which pixel colour is required from a texture, it can sometimes hit the edge of the pixel rectangle and have to make a decision to which side of the edge it should read from (this is not an actual decision; it's simply just an error of floating point values).
Now, imagine your 32-pixel wide image; it goes from 0 (left of first pixel) to 32 (right of 31st pixel).
If you stretch this to fix 40-pixels, the image pixel "rectangles" start to overlap two actual pixels (rectangles) of the final render. It now goes from 0 - 40 (across 40 pixels). Every 4 pixels are stretched across 5 pixels. For example, the 3nd actual pixel could be reading from the 2nd texture pixel or the 3rd texture pixel (each half of that actual pixel is a different one). When you then move everything by a slight amount, one starts to dominate more and is more likely to be "chosen". This means that a very slight (fraction of a pixel) movement can make that one pixel change from one to the next.
Luckily, without smoothing and movement, your image looks great :)

Add in to this a smoothed texture, and it gets even more complicated. It smooths textures by blending pixels when stretched or squashed (giving a blurry effect). That means when you stretch a 32-pixel texture across 40-pixels, it starts blending pixels. These blended/blurred pixels could be on either of the tiles (that are joined), or both, and could easily be selected. Indeed, if it picks the right pixel but part of the next pixel wants to be seen as well, it'll just blend it in.

If you want to see texture smoothing in action, try stretching an image with fine (but clear) detail.
There's a simple example in the SFML tutorials here (scroll down to the first images):
https://www.sfml-dev.org/tutorials/2.6/graphics-sprite.php#loading-a-texture
You can see that the image that has been smoothed looks slightly blurry (look at its edge) when its size has been increased.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*