4
\$\begingroup\$

I'm making a map viewer where you can specify where tiles go in a map and then see it on the screen, translate it, and zoom in and out.

Here's a picture showing the tiles used to make my example map

spaced out

Here's the tiles connected together, more or less my desired result.

connected properly

The problem is that at particular combinations of translations and zoom levels the spacing is off. Here's an example picture of the problem.

enter image description here

In order to arrange the tiles I'm using a Perspective projection matrix parametrized by the zoom level, a camera matrix which just translates the scene based the camera's 2d position, and finally each tile is translated using another matrix derived from its x and y position.

tile * camera * projection

How can I avoid this weird spacing issue at particular positions? I'm guessing this is some kind of floating point precision issue, but I'm not sure.

(for those interested, the tiles I'm using are from this collection)

EDIT:

I was using the following parameters for the standard openGL perspective projection matrix:

let near = 0.5;
let far = 1024.0;

let scale = zoom * near;
let top = scale;
let bottom = -top;
let right = aspect_ratio * scale;
let left = -right;

where aspect_ratio is the window width divided by the window height, (in pixels,) and zoom in the error case above was set to 8.0. I read some more on perspective projection and floating point errors but couldn't find anything sensible to change so I resorted to adjusting the parameters. Specifically I tried setting near to 1.0 and now I can't replicate the weird spacing. So I guess my problem is solved?

But this is a mysterious and unsatisfying answer, so I would still appreciate it if someone could explain what went wrong, and how I can avoid it in the future without fiddling with numbers until it "magically" works!

EDIT responding to amitp

error with red background

Here's a screenshot from the same version as above (with near still set to 0.5), with the background set to red as suggested. This seems to indicate that it's something wrong with the tile layout, not image bleed.

\$\endgroup\$
  • 1
    \$\begingroup\$ Something to try: in the sprite sheet fill the gaps with red instead of black. Then see if the black lines in the output turn red. If so, it tells you that it's somehow sampling the gaps between the sprites. \$\endgroup\$ – amitp Sep 11 '17 at 23:17
2
\$\begingroup\$

When using pre-packed tile maps like that. It should be noted that it is best to ensure that the 'zoom' or scaling level is maintained at even integer increments: ie 1x, 2x, 4x, 8x, etc..

When the GPU is asked to divide by numbers that generate floating point numbers (ie: using odd numbers or fractions), the resulting numbers' accuracy can (and will) result in imprecisely positioned textures. Resulting in uneven lines appearing.

A well know workaround is used in texture packing tools suck as TexturePacker: the setting is under Advanced Settings -> Sprites -> Extrude.

It is usually only necessary to extrude by 1 pixel, this ensures that when the texturing part of the GPU tries to place the texture at a certain point, that there is some 'buffer pixels' just in case the position needs to be approximated (which is in the case of not using even scaling factors.

So I suggest the following:

  1. Use even scaling factors
  2. Re-pack the texture with "Extrude" >= 1px
\$\endgroup\$
  • 1
    \$\begingroup\$ I am already using a texture with 1 pixel gaps between tiles and the error image happens with a zoom level of exactly 8.0. (the zoom level is a multiplier on t, b, l, and r in the OpenGL Perspective Projection Matrix on this page. I was changing my zoom by multiplying by 1.25 and that was introducing some numeric instability (start at 8.0, zoom in a bit, zoom back out and I'm at 7.999995) but the errors still happen at even scaling factors. \$\endgroup\$ – Ryan1729 Sep 11 '17 at 14:28
0
\$\begingroup\$

As recommended in Sebastian's answer, what worked for me was to make sure the sprites in my sprite atlas were all extruded. To extrude a sprite you need to add pixels to the left, right, top, and bottom of the sprite by duplicating those pixels already at those edges of the original sprite. You should also add extra corner pixels using the colour of the original sprite corner pixels. This results in an n+2 by n+2 pixel sprite, basically whose new edges and corners have been copied from the original sprite's borders and corners.

However, you only record the coordinates of the sprite to include the "centre" n by n pixels. That way, if the u-v coordinates are slightly out then instead of mapping the next sprites's pixels or transparent pixels if you have a gap between sprites, you get the pixels for the sprite you want.

Doing this eliminated the pixel gaps between adjacent tiles. Note that, it is not actually necessary to perform this for all your sprites, just the ones that you know might be adjacent in your game. However, applying to all sprites is probably the quickest route. I found extruding by 1 pixel around the sprite perimeters worked fine.

Secondly, I then observed unwanted "merged" edges between adjacent tiles. On further investigation I found this to be due to the fact I was using WebP as the texture format, which is lossy and lossless. Switching back to PNG solved this problem.

Now I observe seamless tiles with no weird artefacts whatsoever.

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.