Improve seams between tiles

Anyone have any ideas on how I can improve the seams between my tiles?

I have a huge .png which is split into smaller 256x256 tiles. and each tile is created like this:

    const bitmap = await this._imageBitmapLoader.loadAsync(tile.dataUrl);
    const texture = new THREE.CanvasTexture(bitmap);
    const { width, height } = tile;
    const geo = new THREE.PlaneGeometry(width, height);
    const mat = new THREE.MeshBasicMaterial({ transparent: true, map: texture });
    const mesh = new THREE.Mesh(geo, mat);

I’ve been playing around a bit with the filters on the textures but no real improvement.

Any suggestions would be greatly appriciated!

Textures are linearly interpolated by default - hence the blur / smoothing. And since there’s no continuation on the edge of the tile, it doesn’t blur “properly”.

You can either:

  1. try setting .wrapS and .wrapT to Three.MirroredRepeatWrapping. In some specific cases this may make the edges look better and save you rest of the points down this list.

  2. alternatively, make 258x258 tiles, and scale the UVs on the plane geometry a little inwards so that the additional pixel padding is not visible, but still available there for the interpolation.

  3. if nothing else works, use Three.NearestFilter as .magFilter. This will disable the interpolation entirely, texture may look more pixelated when zoomed in, but the corners of the tile should match pixel-perfectly.


Thanks for the suggestions!

Unfortunately #1 did not make any difference, but I will give #2 a try.


It won’t work that way. The repeat wrapping is designed for tiling the same texture tile and interpolates to the opposite edge of the same tile, not on the edge of the other tile because your current tile that you are filtering does not know anything about the neighbour tiles. The tiles do not communicate with each other.

The problem is solvable but it is a complex matter. To do this, you have to give each tile a border, e.g. 2, 3 or 4 pixels wide. This edge consists of the edges of all adjacent tiles. This way your tile can interpolate properly. However, the visibility is limited only to the desired part without the edge. This has to be done in a shader.
This is also what @mjurczyk means with his second suggestion.

1 Like

When I encountered this problem, I found that increasing the resolution of each tile solves it. The higher the resolution, the better the results for getting the seams to blend. Try 1024x1024.

@michealmyers81 Yeah using a higher res texture will make the seams smaller… so it definitely helps at the cost of higher texture sizes.

@Attila_Schroeder Three.MirroredRepeatWrapping is slightly different than RepeatWrapping, in that it has a higher edge coherence probability than regular RepeatWrapping since its reflecting the texture, so if a Red texel is on the edge, you’re guaranteed to get another red texel on the other side of the edge… so it acts like a 1 pixel border and can end up looking better than straight RepeatWrapping. ClampToEdgeWrapping (the default) can also be as/more effective.

I think your answer is more effective/correct (creating a N pixel tile border), especially in an Atlas’ing scenario…in practice I’ve found you need X number of pixel border where X is closer to the default anisotropic filtering level, (i think 16?) to kill seams better at all the mip levels. I’m just handwaving here but, those are numbers I’ve found…

For instance when selecting “island margin” in UV unwrapping, a value that gets UV islands closer to 16 texels apart, with dilation/bleeding work the best. One encounters these kinds of issues a lot with automatic UV unwrapping schemes… light mapping and texture baking…

(note the UV island texel bleeding, and how islands that are 2 close together can’t properly bleed enough to get rid of the artifacts.

It’s a tricky problem, and finding working/right values is always a hack…

Like… for map tiles to be filtered, you kinda want each tile to have ~ 16 pixels of each of its neighbors on each side.

So a 256x256 tile might need to be 256+32 width/height = 288 to really filter well.

1 Like