Tiling Textures Without Seams.


By Steve Baker
    "Look! You can hardly see the join."
    -- Eric Morcombe.

Introduction

Sooner or later, you'll need to draw a texture that is too large to fit into a single texture map. You'll assume that you can split the image up into perhaps four smaller maps - and just attach those to four separate polygons.

Nice in theory - but in practice, you'll find that you get a thin seam between the adjacent textures...especially if you use MIPmapping.

Causes.

There are basically three possible reasons for this:

Blending across maps

If you can tolerate GL_NEAREST (yuk!) or GL_NEAREST_MIPMAP_* (double-yuk!) filtering, then the results will be perfect because OpenGL won't blend adjacent texels at all - and therefore the sudden change in texel value will be the same throughout the image.

Given that you probably want GL_LINEAR or GL_LINEAR_MIPMAP_*, you will see seams at the texture edges because the edge texels are not blending with the correct neighbour texels (which are off in another map somewhere).

There really isn't a *great* solution.

The "official" mechanism uses OpenGL's texture border mechanism - but this is so patchily (and often poorly/inefficiently) implemented that I really don't recommend it.

What you end up needing to do is a couple of things:

How Much Overlap?

Regrettably, if you want MIPmapping, there is no perfect answer. At the HIGHEST level of detail, a one texel overlap is enough - but when the map drops down one level of detail, you still need a one texel overlap. Of course if you want to adjust the texture coordinates on the polygon to overlap the second highest level of detail map by one texel, then the topmost map has to overlap by TWO texels.

Similarly: at the third, fourth and fifth levels of detail, you still need a one texel overlap - which translates to a four, eight or sixteen texel overlap in the original image.

In the limit, to get a *perfect* result, the lowest level of detail map of all (the 1x1 texel map) has to overlap it's neighbour by one texel - which means that the entire map has to overlap! ...which is just plain silly!

So, the *practical* solution is to experiment with overlaps of one, two or four texels and see how little overlap you can tolerate in your application. You can't ever get it 100% right - but you can get arbitarily close with enough overlap. A lot depends on the nature of the application, the resolution of the maps and the sharpness/contrast of the original image. For a very fuzzy image, you may be able to get away without overlaps at all.

You might also want to experiment with reducing the contrast in the lower resolution MIPmaps - and gradually forcing them to the same colour in the 1x1 map. This means writing your own MIPmap generator to replace gluBuild2DMipmaps() - but that's a good idea in any case for *LOTS* of reasons.

Some MIPmap generation techniques benefit from you MIPmapping the entire source image and THEN chopping it up rather than chopping up the source image and MIPmapping the smaller images. gluBuild2DMipmaps is pretty crude though - I don't think it helps to use that function on the entire source image.

Conclusion.

Like so many things, this ends up requiring a little care and acceptance of a certain amount of image problems since it cannot be done perfectly.