Handling Texture Atlas Border Clamping in glTF
In glTF 2.0, texture samplers apply wrapping and clamping behaviors exclusively to the entire image asset rather than to individual sub-tiles within a texture atlas. Because the specification lacks native sub-rectangle clamping parameters, sampling atlas tiles requires specific authoring techniques—such as padding, texel extrusion, or custom shader logic—to prevent visual artifacts like texture bleeding. This article explores how glTF texture samplers function, why border clamping fails on atlas tiles by default, and how to properly handle these constraints.
glTF Texture Sampler Wrap Modes
The glTF specification defines texture sampling behavior using the
sampler object, which provides wrapS and
wrapT properties. These properties support three standard
WebGL/OpenGL wrap modes:
CLAMP_TO_EDGE(33071): Clamps coordinates to the range[0.0, 1.0], repeating the outermost edge texels of the full image.REPEAT(10497): Tiles the texture by ignoring the integer component of the UV coordinates.MIRRORED_REPEAT(33648): Tiles the texture while flipping it at every integer boundary.
These wrap modes are executed at the hardware sampler level across the complete dimensions of the assigned image file.
The Sub-Tile Clamping Problem
A texture atlas packs multiple independent textures into distinct UV
sub-rectangles (e.g., from [0.0, 0.0] to
[0.5, 0.5]). When a 3D model samples an atlas tile, the
GPU's hardware sampler has no contextual awareness of the sub-tile's
boundaries.
If a material's UV coordinates cross or touch the perimeter of an assigned atlas tile:
- Wrap Mode Failures: Setting the glTF sampler to
CLAMP_TO_EDGEclamps UVs to the edges of the entire atlas, not the sub-tile boundaries. A UV coordinate exceeding the sub-tile border continues reading into adjacent tiles. - Bilinear/Trilinear Filtering Bleed: Linear interpolation blends samples from adjacent texels. Texels right on the boundary of an atlas tile will blend with the texels of neighboring tiles.
- Mipmapping Artifacts: As lower-resolution mip levels are generated, texels from different tiles merge together, resulting in visible discoloration or seams around the geometry edges at distant viewing angles.
Solutions for Atlas Border Clamping in glTF
Because glTF does not offer an arbitrary tile clamp mechanism in its core specification, developers must mitigate atlas clamping issues using the following methods:
1. Padding and Edge Extrusion (Recommended for Standard glTF)
The standard and fully glTF-compliant approach is to build the atlas with safety margins around each sub-tile:
- Gutters: Leave empty space between sub-tiles.
- Texel Extrusion (Bilinear Padding): Duplicate the outer single-pixel edge of each sub-tile into the gutter. This ensures that linear interpolation pulls identical edge colors instead of neighboring textures.
- Mipmap Sizing: Ensure the padding thickness is large enough for the lowest mip level used (e.g., a margin of 16 to 32 pixels for a \(2048 \times 2048\) atlas).
2. UV Range Clamping via Shaders
If rendering outside the constraints of an unextended glTF viewer, you can enforce sub-tile clamping inside the fragment shader:
vec2 clampedUV = clamp(v_UV, tileMinUV, tileMaxUV);
vec4 color = texture(u_Atlas, clampedUV);While this restricts the UV coordinates to the tile's defined
sub-rectangle, it requires passing bounding bounds per-material or
per-draw call, which is not supported in the standard
KHR_texture_transform extension.
3. Limitations of
KHR_texture_transform
The KHR_texture_transform extension allows offset and
scale controls for UV coordinates. While it can map geometry UVs to the
correct sub-rectangle of an atlas, it only scales and offsets
coordinates; it does not introduce boundary clamping logic.
4. Array Textures as an Alternative
When independent clamping and wrapping are strictly required without
atlas bleeding, modern pipelines often abandon 2D atlases in favor of 2D
Texture Arrays. Texture arrays preserve individual texture boundaries,
allowing standard CLAMP_TO_EDGE behavior on every layer
without cross-tile contamination.