Texture atlas (custom UV mapping) causing artifacts/bleed at edges

Hi. I’m using a single texture (basis format, 512x512px) as a texture atlas and then modifying the UV of a geometry to only show a single 128x128px area of that texture.

var uv = new Float32Array([0.25, 0, // top right
    0, 0, // top left
    0.25, 0.25, // bottom right
    0, 0.25]); // bottom left
geometry.attributes.uv.array = uv;
geometry.attributes.uv.needsUpdate = true;

This works, however with the geometry being a plane 1x1 in size and flat next to many others (essentially it’s the floor - each 1x1 tile showing the floor texture) you can see through where the vertices/UV don’t meet correctly:

chrome_2020-07-09_20-51-00

I’m using these on the texture so I don’t think it’s mipmapping causing the issue:

texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.wrapS = THREE.ClampToEdgeWrapping;

The texture atlas has transparent pixels under the area I’m sampling so it makes sense that it’s somehow sampling these too, but I’m unable to modify the UVs above to resolve this. I seem to be missing something, probably obvious.

If anyone has any ideas it would be appriciated!

Atlas is not a collection of textures, it’s 1 texture. So no matter if you do use clamp-to-edge or not - texture will be treated as a continuous space.

When you use nearest filter - that helps, but if your UV falls somewhere in-between two texels - you will may end up with an un-intended pixel showing up.

Mipmaps. You’re not using them, which is good, but it could cause bleeding as well. Not in your case though.

Next, a lot of texture compression formats are block-based, meaning that the image is divided into squares (blocks) of 2x2, 4x4 or more in some cases, this means that you typically get some bleeding/artifacts between pixels in a block, due to how a lot of these compression methods work. Compressed textures are lossy, this means that you end up with some information from the original texture lost.

Okay, so what can you do? If you use an atlas, use a border of empty space or same pixel color as the edge of the atlas patch, this way you avoid bleeding. I suggest picking a border width of at least 4 pixels, you can pick smaller or larger number, larger than 16 is typically not needed, smaller than 2 is next to useless. Border width depends on whether or not you use mipmaps, and what is the compression distortion distance. Since you’re using basis - you have basically no way of knowing which format will be used at the client’s side, so better err on the side of caution.

3 Likes

Thanks Usnul. Using a border around each area does indeed resolve it. Or sampling a few pixels inside the area - either works fine.

I later found that it was doing it again (even with a border of transparent pixels) but that was due to the order in which I added the meshes. I needed to draw from back to front or disable depthWrite in the material otherwise those transparent border pixels appear as white pixels and you get the same bleed effect again. Makes sense!

Many thanks.

1 Like