Asked 4 months ago by AstralSentinel110
How Can I Resolve Texture Seams Between Tiles When Using Mipmaps in Three.js?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 4 months ago by AstralSentinel110
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Hello.
I have implemented a TileMap
/SpriteMap
component for my Three.js application. Currently, I configure the loaded Texture
object to use NearestFilter
for both magFilter
and minFilter
properties, which avoids visible seams/artifacts between the tiles. However, when I use LinearFilter
for these properties, undesired seams appear all around the tiles.
I have researched this well-known issue extensively but haven’t found a detailed technical explanation of why these artifacts occur or a clear method to resolve them. Can someone explain what causes these seams and suggest a practical fix?
For context, I am developing this component not only for my application but also with the intention of submitting a PR to the Three.js project. If there is sufficient interest, it could be a valuable addition for the community; otherwise, I plan to offer it as a standalone library.
Comments 0
•Answers 1
•Views 245
Answer by TychoAI Agent • 9 days ago
The issue you're experiencing with seams appearing when using LinearFilter
instead of NearestFilter
is a common problem in texture mapping, especially with tiled textures. Let me explain the technical cause and suggest some practical solutions.
When using LinearFilter
, the GPU performs bilinear interpolation between texels (texture pixels). At the edges of your tiles, this interpolation samples colors from adjacent tiles, causing visible seams. This happens because:
NearestFilter
works without seams because it simply picks the closest texel without any blending, so there's no interpolation between tiles.
Here are several approaches to fix this issue while still using LinearFilter
:
Add a 1-2 pixel border around each tile in your texture atlas that duplicates the edge pixels of each tile. This ensures that when interpolation occurs, it samples similar colors.
JAVASCRIPT// When loading your texture texture.load(url, (loadedTexture) => { loadedTexture.magFilter = THREE.LinearFilter; loadedTexture.minFilter = THREE.LinearFilter; // Set proper UV coordinates that account for the padding // in your geometry or material });
Inset your UV coordinates slightly to avoid the very edges of each tile:
JAVASCRIPT// Example of adjusting UVs to avoid edge sampling function adjustUVs(geometry, tileSize, textureSize, padding = 0.001) { const uvs = geometry.attributes.uv; for (let i = 0; i < uvs.count; i++) { // Adjust each UV coordinate to avoid the edge // This is a simplified example - you'll need to adapt to your specific mapping const u = uvs.getX(i); const v = uvs.getY(i); // Calculate adjusted UVs that avoid the edges const tileU = Math.floor(u * tileSize) / tileSize; const tileV = Math.floor(v * tileSize) / tileSize; const adjustedU = tileU + padding + (u - tileU) * (1 - 2 * padding); const adjustedV = tileV + padding + (v - tileV) * (1 - 2 * padding); uvs.setXY(i, adjustedU, adjustedV); } uvs.needsUpdate = true; }
Set appropriate texture wrapping and repeat settings:
JAVASCRIPTtexture.wrapS = THREE.ClampToEdgeWrapping; texture.wrapT = THREE.ClampToEdgeWrapping;
For more control, you can implement a custom shader that handles the sampling more precisely:
JAVASCRIPT// Fragment shader excerpt that implements custom filtering const fragmentShader = ` uniform sampler2D tileTexture; uniform vec2 tileSize; uniform vec2 textureSize; varying vec2 vUv; void main() { // Calculate tile position vec2 tilePos = floor(vUv * tileSize); vec2 inTilePos = fract(vUv * tileSize); // Add a small inset to avoid edge sampling vec2 adjustedInTilePos = mix(vec2(0.001), vec2(0.999), inTilePos); // Calculate final UV vec2 finalUV = (tilePos + adjustedInTilePos) / tileSize; gl_FragColor = texture2D(tileTexture, finalUV); } `;
For a component you're planning to submit as a PR or standalone library, I recommend implementing options for:
This would give users flexibility based on their specific needs and performance requirements.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
No comments yet.