I have a 2D game engine that draws tilemaps by drawing tiles from a tileset image. Because by default OpenGL can only wrap the entire texture (GL_REPEAT
), and not just part of it, each tile is split off in to a separate texture. Then regions of the same tile are rendered adjacent to each other. Here's what it looks like when it's working as intended:
However as soon as you introduce fractional scaling, seams appear:
Why does this happen? I thought it was due to linear filtering blending the borders of the quads, but it still happens with point filtering. The only solution I've found so far is to ensure all positioning and scaling only happens at integer values, and use point filtering. This can degrade the visual quality of the game (particularly that sub-pixel positioning no longer works so motion is not so smooth).
Things I have tried/considered:
- antialiasing reduces, but does not entirely eliminate, the seams
- turning off mipmapping, has no effect
- render each tile individually and extrude the edges by 1px - but this is a de-optimisation, since it can no longer render regions of tiles in one go, and creates other artefacts along the edges of areas of transparency
- add a 1px border around source images and repeat the last pixels - but then they are no longer power-of-two, causing compatibility problems with systems without NPOT support
- writing a custom shader to handle tiled images - but then what would you do differently?
GL_REPEAT
should be grabbing the pixel from the opposite side of the image at the borders, and not pick transparency. - the geometry is exactly adjacent, there are no floating point rounding errors.
- if the fragment shader is hard coded to return the same color, the seams disappear.
- if the textures are set to
GL_CLAMP
instead ofGL_REPEAT
, the seams disappear (although the rendering is wrong). - if the textures are set to
GL_MIRRORED_REPEAT
, the seams disappear (although the rendering is wrong again). - if I make the background red, the seams are still white. This suggests it's sampling opaque white from somewhere rather than transparency.
So the seams appear only when GL_REPEAT
is set. For some reason in this mode only, at the edges of the geometry there is some bleed/leakage/transparency. How can that be? The entire texture is opaque.
GL_NEAREST
sampling in theR
coordinate direction also work just as well as array textures for most things in this scenario. Mipmapping is not going to work, but judging by your application you probably don't need mipmaps anyway. \$\endgroup\$