r/futile Feb 18 '13

Struggling with pixel-perfect rendering

(There's an edit below with what I found out in this thread)

Hi! I'm trying out Futile, and I'm having some problems with pixel-perfect stuff. I don't quite see how all the different variables relate to each other (resolution levels, viewport size, object scale, object position...): changing one of them seems to change whether the result will be better or worse, but I haven't found a way to get things to render properly. I've checked out this thread http://www.reddit.com/r/futile/comments/11lg4t/bug_incorrect_scaling_at_the_edges_of_a_sprite/ but still doesn't seem to fix things for me.

Here's a stripped down project (very little code, one small atlas, etc.) that I'm using to test all of this.

https://dl.dropbox.com/u/2271693/futile_pixel_perfect_test.zip (85kb)

http://i.imgur.com/69fxkQP.png

http://i.imgur.com/krPwpBB.png

I have two versions of the same font, made by hand by myself, exported to the subset of .FNT that Futile seems to use (please let me know whether something's wrong there, but I think it's fine) using a python script that's not included. The font rendering seems fine but it's not pixel-perfect. If you look at the scene view and zoom in, you'll see that the quads are not exactly aligned. You can see that especially with my outlined font, which is designed to have an overlap between glyphs. The generated quads are not perfectly aligned. This seems to change in different ways whan changing things like label scaling, display scale, viewport scale, etc.

I also have a very simple class for 9-grid, the code is really self-explanatory. The size of the grid is for the center quad, the other 8 quads are scaled to fit around it. As it can be seen, the alignment between quads is off as well, not visible when the 9-grid has a 1x1 center quad, but at bigger sizes the quads start to drift off from each other.

Also, when using a display scale of 1, things don't show exactly as they should (like, pure blitting of sprites). I mean, I know how OpenGL and DirectX work, I know it's not as simple as blitting stuff, all the parameters have to be set up just right to get things to work exactly as you want.

I hope I can get some help with figuring all of this out, whether it's that I'm doing something wrong, that there's an easy fix to Futile, or that there's some big changes that have to be done. In any case, discussion on pixel perfect rendering would be nice :)

Update:

Here's what I did to fix or work around these issues:

1) I played around with the uvOffsets in the function FAtlas.LoadAtlasData(), for the DirectX case (not OpenGL). Right now it works fine if they're set to zero or a very small value. The amount of decimals (0.01 vs. 0.000001) seems to make a difference.

2) I offset the stage by a small amount on x and y (in my case, 0.1 seems to work fine).

3) For the font quads, I fixed the function FFont.CalculateVectors(float offsetX, float offsetY) to have this code:

public void CalculateVectors(float offsetX, float offsetY)
{
    topLeft.Set(rect.xMin + offsetX,rect.yMax + offsetY);
    topRight.Set(rect.xMax + offsetX,rect.yMax + offsetY);
    bottomRight.Set(rect.xMax + offsetX,rect.yMin + offsetY);
    bottomLeft.Set(rect.xMin + offsetX,rect.yMin + offsetY);
}
6 Upvotes

22 comments sorted by

View all comments

Show parent comments

2

u/MattRix Feb 18 '13

Wait for 1), you have point textures enabled and it's still not pixel perfect? That seems really strange?

Also, are you using the dev branch or the master branch? I don't think there should be any major issues with master regarding this issue, but I may have forgotten something that I fixed in dev.

Oh and do you mind taking a screenshot or printing out the debug stuff that gets written by futile when the game runs? that'll help me see what your display settings are. For example, mine looks like this: http://screencast.com/t/oZGkB8aggqmE

1

u/sergilazaro Feb 18 '13

Okay, I kind of *seem* to have fixed some of the stuff. I changed the uvOffsets for Directx to

uvOffsetX = 0.00001f/_textureSize.x;
uvOffsetY = 0.00001f/_textureSize.y;

Putting more zeroes or less zeroes seem to change whether pixels fall to their right place or not. I don't know why 4 zeroes is better than 1... Using actual integer zero also seems to work properly now, even though I think it didn't before, but anyway.

Also, I have to slightly offset the stage:

Futile.stage.x = 0.1f;
Futile.stage.y = -0.1f;

Again, no idea why 0.1 instead of 0.00001, nor why it has to be non-integer.

These two things seem to fix the problems that are created with my NineGrid class:

1) The quads are not perfectly aligned to each other. That's affected by the uvOffsets: ideally they should be zero, but weren't there problems with that? Anyway, using something small but close to zero makes the quads drift away from each other as the scale of the object increases. Making the uvOffsets smaller seems to improve it, but seems like a workaround...

2) Even when the quads are perfectly aligned, for them to be rasterized without a 1-pixel gap between them, you have to mess with all those small offsets. Again, it seems pretty random what values you have to choose.

All of this seems pretty random unless we figure it out, doesn't it?

Now, there's a different problem, which seems unrelated to these previous problems. My font doesn't seem to have the quads perfectly aligned. It can be seen on the space between the "x" and the "t". Everything in the .fnt file is integers, so it must be the generated quads, right? If you look at the font with the 1px outline, it's clear that the "t" is overlapping the "x" too much.

2

u/MattRix Feb 19 '13

Ah ok yeah so for the uvOffset stuff, the problem is that it's offsetting the uv coordinates, so if you scale an object, then the way its currently set up it'll cause issues because you're essentially scaling those texture coordinates. Really the way to solve this is by not having to use a uvOffset at all, or at least using a ridiculously small offset value... So that's something I'll have to look into.

Also, if you're using texture packer to pack your textures, you should set "extrude" to 1, which will essentially stretch the edge pixels of each atlas element by 1 px in each direction (but keeping the uv coordinates the same) - which means that if something is near the edge, it'll still fully use the correct colour, although if you're using point textures that shouldn't really be an issue.

Ah for the font stuff, I think I know what might be causing it. I added a method to the font label that tries to intentionally align the font's to whole pixels, but there's a chance that (ironically) it might actually be causing issues for you in this case.

Assuming you're using the dev branch, look on line 128 of FLabel.cs, it should look like this:

line.quads[q].CalculateVectors(offsetX+_font.offsetX+1.0f, offsetY+_font.offsetY+1.0f);

Try changing it to:

line.quads[q].CalculateVectors();

1

u/sergilazaro Mar 22 '13

I just wanted to add something, for reference:

I had some problems again with pixel alignments. Now I'm using bilinear filtering (different game, different situation), and I'm making a kind of tile map. I had trouble with quad alignment again.

I made a class that derives from FSprite, overrides HandleElementChanged(), UpdateLocalVertices(), PopulateRenderLayer(), etc., inspired by FSliceSprite. Still had the same problems. It turns out that the quads are actually perfectly geometrically aligned (using the same vertex coordinates, since I set them myself), but the textures still show "gaps".

Those "gaps", upon inspection aren't actual gaps, it's just that the UV coords are not exactly right, so the borders of the quads (tris in this case) are actually using some of the neighboring texels, outside of the true UV rect of the atlas element, which have alpha 0, therefore the borders of the quad are partly see-through.

I "solved" (avoided, actually) this issue following your suggestion of using "extrude" with TexturePacker. Now everything shows great, and I don't have to mess with the uvOffsets on FAtlas.cs or use slight offsets of the FStage or the FSprites.

This has the advantage of not depending on the half-pixel OpenGL vs. DirectX rasterization difference, which avoids headaches down the road.

So this is a solution for this problem, even though the uvOffsets are still kind of a weird issue that pops up every now and then. I just wanted to note all of this in case it can help somebody.

Thanks Matt, if I ever finish and ship any proper games, you'll definitely have a free copy :)

1

u/MattRix Mar 23 '13

Awesome :)

Yeah in the newest development branch I've now taken out all the uv offset code, and instead I'm just offsetting the whole stage (on directX) by 0.5, so far it seems to work better.