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

Author Topic: Pixel-perfect Sprite SubRect  (Read 11018 times)

0 Members and 1 Guest are viewing this topic.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Pixel-perfect Sprite SubRect
« Reply #15 on: September 28, 2011, 03:17:41 pm »
Don't worry, I'll do the tests ;)

But something is not clear: do you subtract this offset from pixel coordinates (0 .. size), or normalized coordinates (0 .. 1)?
Laurent Gomila - SFML developer

lucasncv

  • Newbie
  • *
  • Posts: 15
    • View Profile
Pixel-perfect Sprite SubRect
« Reply #16 on: September 28, 2011, 03:33:07 pm »
Quote from: "Laurent"
But something is not clear: do you subtract this offset from pixel coordinates (0 .. size), or normalized coordinates (0 .. 1)?

Normalized, as I said I'm using my own implementation of the drawing stuff, with pure OpenGL calls, so I subtract that epsilon directly at glTexCoord2f().

I started subtracting 0.0001, nothing changed, then I increased that value until it actually cut a pixel from all sprites, that was at 0.01, and 0.005 didn't fix it, so I tried 0.0075 and ta-da!  :D  Haha I hate these ugly hacks.

Quote from: "Laurent"
Don't worry, I'll do the tests ;)

Be sure to let us know the results!  :wink:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Pixel-perfect Sprite SubRect
« Reply #17 on: September 28, 2011, 03:41:24 pm »
Quote
I started subtracting 0.0001, nothing changed, then I increased that value until it actually cut a pixel from all sprites, that was at 0.01, and 0.005 didn't fix it, so I tried 0.0075 and ta-da!   Haha I hate these ugly hacks

The "right" value should be 0.0078125, which is 0.5 / size (from your screenshot your texture looks like it is 64x64), that's why it's called the half-pixel trick ;)

So it's not a ugly hack, but rather a well-known technique to align pixels and texels -- which are initially aligned differently (one is aligned at the center of a pixel, the other is aligned on the top-left corner of a pixel).

But I'm surprised it works because that's the first thing I tried in order to solve this problem.
Laurent Gomila - SFML developer

lucasncv

  • Newbie
  • *
  • Posts: 15
    • View Profile
Pixel-perfect Sprite SubRect
« Reply #18 on: September 28, 2011, 03:45:57 pm »
Quote from: "Laurent"
The "right" value should be 0.0078125, which is 0.5 / size (from your screenshot your texture looks like it is 64x64), that's why it's called the half-pixel trick ;)


Ooh, I didn't know that! I think I'll implement an epsilon that's based on the texture size  :idea:  It's not as ugly as I've thought!

Quote from: "Laurent"
So it's not a ugly hack, but rather a well-known technique to align pixels and texels -- which are initially aligned differently (one is aligned at the center of a pixel, the other is aligned on the top-left corner of a pixel).

But I'm surprised it works because that's the first thing I tried in order to solve this problem.

Now that you mention it, I remember seeing something similar on HLSL shaders when I did some work with XNA, but I think it was a translation of half-pixel, and not a subtraction.

lucasncv

  • Newbie
  • *
  • Posts: 15
    • View Profile
Pixel-perfect Sprite SubRect
« Reply #19 on: September 28, 2011, 04:13:16 pm »
I thought I'd post this here since it shows the result of the problem on topic. This is a modified code from SFML 1.6 Sprite's tutorial, now upgraded to SFML2, that shows a tile from my sample spritesheet (get it here). It draws the tile using SFML2's Sprite and then it draws it again using pure OpenGL with the half-pixel epsilon applied. Try moving both around, see how the artifact only shows up on the left one (SFML2 Sprite).

Code: [Select]
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML\Graphics.hpp>
#include <SFML\OpenGL.hpp>

////////////////////////////////////////////////////////////
/// Entry point of application
///
/// \return Application exit code
///
////////////////////////////////////////////////////////////
int main()
{
    // Create the main rendering window
    sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Graphics");

    // Load the sprite image from a file
    sf::Texture Tex;
    if (!Tex.LoadFromFile("platform.png"))
        return EXIT_FAILURE;

    // Create the sprite
    sf::Sprite Sprite(Tex);

    // Change its properties
    Sprite.SetPosition(64.f, 64.f);
    Sprite.SetSubRect(sf::IntRect(0, 16, 16, 16));

    // Start game loop
    while (App.IsOpened())
    {
        // Process events
        sf::Event Event;
        while (App.PollEvent(Event))
        {
            // Close window : exit
            if (Event.Type == sf::Event::Closed)
                App.Close();
        }

        // Get elapsed time
        float ElapsedTime = App.GetFrameTime() / 1000.f;

        // Move the sprite
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Left))  Sprite.Move(-100 * ElapsedTime, 0);
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Right)) Sprite.Move(100 * ElapsedTime, 0);
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up))    Sprite.Move(0, -100 * ElapsedTime);
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down))  Sprite.Move(0, 100 * ElapsedTime);

        // Rotate the sprite
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Add))      Sprite.Rotate(- 100 * ElapsedTime);
        if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Subtract)) Sprite.Rotate(+ 100 * ElapsedTime);

        // Clear screen
        App.Clear();

        // Display sprite in our window
        App.Draw(Sprite);

        // Now that SFML set-up the viewport, bound the texture and everything, just draw directly
        sf::Vector2f v = Sprite.GetPosition() + sf::Vector2f(64.f, 0.f);
        float Epsilon = 0.0078125;

        glBegin(GL_QUADS);
        {
            glTexCoord2f(0.f, 0.25f); glVertex2f(v.x, v.y);
            glTexCoord2f(0.f, 0.5f - Epsilon); glVertex2f(v.x, v.y + 16);
            glTexCoord2f(0.25f - Epsilon, 0.5f - Epsilon); glVertex2f(v.x + 16, v.y + 16);
            glTexCoord2f(0.25f - Epsilon, 0.25f); glVertex2f(v.x + 16, v.y);
        }
        glEnd();

        // Display window contents on screen
        App.Display();
    }

    return EXIT_SUCCESS;
}

ChonDee

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Pixel-perfect Sprite SubRect
« Reply #20 on: January 21, 2012, 03:57:34 am »
I hope it's okay for me to bump this somewhat old topic, I rather not start another one, since my problem is exactly identical as described in lucasncv's first post.

I see that there is a kinda hackish solution to this, I am wondering if somebody knows some better way, since I am handling the sprite selection a bit differently.

I am using SFML 2.0.
I load the sprite sheets using sf::Texture mySpritesheet;
I make a sf::IntRect as the boundaries for the sprite, and initialize them in the following way:
Code: [Select]
if (size == 1)
{
orb_surf = orb1_sheet;
for (int i = 0; i < 5 ; i++)
{
orb1_sheet_sprite[i].Left = i*3;
orb1_sheet_sprite[i].Top = (type-1)*3;
orb1_sheet_sprite[i].Width = 3;
orb1_sheet_sprite[i].Height = 3;
}
}

My sprite is displayed in the following way:
Code: [Select]
orb_surf.SetTextureRect(orb1_sheet_sprite[sprite_count]);
orb_surf.SetPosition(x, y - camera.Top);
App.Draw(orb_surf);


In this specific example, there are 2 rows of 5 3x3 sprites.
top row for red, bottom for orange, and the x position is the different states of animation (flashing)

when the player is moving on the y axis, making the camera move, there are certain "sweet spots" where the red orb is displayed as top half red, bottom half orange, so the selection rectangle went down a pixel.

What would you guys advise?
Should I change all my sf::IntRects to sf::FloatRect and substract 0.0078125 from the borders?
Is there a cleaner way to do this?

Also, is there some advantage (performance-wise perhaps) of using gl calls, as shown in the post above mine, or sf::Rect is okay?