OptionalinternalFormat?: numberWebGL internal format. Defaults to SRGB8_ALPHA8 for the colour
pipeline; pass gl.RGBA8 for linear data (metallic-roughness,
normal maps, etc.).
Optionalmipmap?: booleanAllocate the atlas with a full mip pyramid and sample
trilinearly. Default false. When true, every successful
addTexture triggers gl.generateMipmap to refresh the
pyramid for the entire atlas — fine when textures upload
once at load, scales with atlas size as more textures
stream in.
Optionalpadding?: numberOptionalsentinelColor?: [number, number, number, number]Sentinel pixel colour as four bytes. Defaults to white. Override
with [128, 128, 255, 255] for normal-map atlases so the
untextured-fallback decodes to a flat tangent-space normal.
Optionalsize?: numberTrue when allocate has succeeded.
WebGL internal format — selects sRGB-decoded vs linear sampling.
true when the atlas was allocated with a full mip pyramid
(floor(log2(size)) + 1 levels) and is sampled trilinearly.
Set from the constructor option; false keeps the cheap
single-level path.
Notifies inspectors that the atlas was modified. Fires when entries are added or after webglContextRestored re-stamps everything.
Padding (gutter) reserved around each entry.
RGBA byte value the 4×4 sentinel block is filled with. Most atlases
use white (255, 255, 255, 255) because that's the multiplicative
identity for albedo / MR / occlusion. Normal-map atlases override
to (128, 128, 255, 255) so the decoded tangent-space normal is
(0, 0, 1) — i.e. "no perturbation" — for untextured meshes.
UV transform for "no texture" — collapses every fragment to the sentinel white texel at atlas (0.5/size, 0.5/size). Initialised in allocate.
Atlas square size in pixels.
Static ReadonlyDEFAULT_Default gutter (pixels) around each entry.
Static ReadonlyDEFAULT_Gutter (pixels) around each entry on a mipmapped atlas. Each
mip level halves the entry's footprint, so adjacent entries
can bleed across the original level-0 boundary at higher
levels — 8 covers entries down to ~16-pixel level-0 sizes
cleanly. Larger entries waste a small fraction of the atlas;
smaller entries (~tens of pixels) might still bleed at the
tiniest mips, which is acceptable for first-cut Phase 1.
Static ReadonlyDEFAULT_Default atlas dimension (square). 4096 means each atlas occupies 64 MB of GPU memory (×4 bytes RGBA × 3 atlas types per UV-bearing batch = ~192 MB). The trade-off is fewer batch splits on texture-heavy models like Sponza — at 2048 the same model would spawn ~5-8 batches just from atlas overflow.
Adds an image to the atlas — or returns the cached transform if id
is already present. Returns null if the atlas is full.
Sources whose width or height (with padding) would exceed the
atlas dimensions are automatically downscaled to fit, with a
warn-level log. The renderer is built around continuous streaming
of varied content, so a too-big texture lands as a lower-res copy
with PBR detail intact rather than as a sentinel that would surface
as a visible material artefact.
Caller must have called allocate first.
Creates the GPU texture and stamps the sentinel white block. Required before addTexture. Idempotent re-allocation isn't supported — call destroy first.
Non-destructive shelf-pack probe — tells the batch router whether
a texture would land in this atlas ("fits"), would fit in a
fresh atlas of the same size but not this one ("would-fit-in-fresh-atlas",
meaning "spawn a new batch"), or is too big for any atlas at all
("too-big", meaning the upload will hit the sentinel fallback —
spawning a new batch wouldn't help).
Sources larger than the atlas dimensions are auto-downscaled by
addTexture, so this probe uses the post-downscale
dimensions for its shelf-fit replay — meaning "too-big" is now
essentially reserved for the degenerate w <= 0 || h <= 0 case.
A oversize texture that triggers a downscale will still report
"fits" or "would-fit-in-fresh-atlas" based on the scaled-down
footprint, so the batch router doesn't pointlessly spawn a new
batch (which wouldn't have helped — the downscaled copy fits in
the current atlas just as well as in a fresh one).
Already-cached entries (matched by id) always report "fits" so
a SceneTexture shared by multiple meshes doesn't keep triggering
batch overflow.
Frees the GPU texture and clears all CPU-side state.
If a mip-bearing atlas has level-0 writes pending since the
last flush, regenerate the pyramid and clear the dirty flag.
Cheap to call when the flag is false (one branch).
Called by the renderer immediately before binding the atlas for a draw so each atlas pays at most one regeneration per frame regardless of how many slices were written since the previous draw.
Bytes occupied by the texture on the GPU.
Looks up an already-added entry's transform. Useful for sharing one SceneTexture across multiple meshes in a batch.
Bytes used by the actually-stamped sub-rects (best-effort).
Re-upload the pixels of an already-added texture, reusing its
cached sub-rect placement. Returns true if the entry exists
and the upload was issued, false otherwise (id not in this
atlas).
Used by the post-finalize onSceneTextureImageDataChanged path,
when a caller has mutated a SceneTexture's imageData and wants
the GPU copy refreshed without rebuilding any meshes or
materials. The shelf-pack state is left untouched — the atlas
doesn't know whether the new pixel buffer's dimensions match the
cached placement, so it's the caller's responsibility to ensure
the source's width/height haven't changed.
Re-creates the GPU texture after WebGL context loss and re-stamps the sentinel and every previously-added image. Existing transforms remain valid because the layout is identical.
GPU 2D shelf-packed texture atlas — one per PBR map type per GPUMemoryBatch (albedo, metallic-roughness, future normals / occlusion / emissive).
Each added image occupies a sub-rect with a configurable padding gutter (default 2 px) to mitigate bilinear bleed. Returns an AtlasTransform the caller writes to the per-mesh attribute texture; the shader applies that transform per-fragment.
Use
internalFormatto switch between sRGB-decoded (albedo) and linear (MR / normals / occlusion / emissive HDR-prep) atlases.v1 limitations:
nulland the caller falls back to the sentinel.SceneTexture.id).[0, 1)before quantisation; the atlas's wrap mode isCLAMP_TO_EDGE.Coordinate convention: the atlas reserves a 4×4 white sentinel block at the top-left so untextured meshes can sample (1, 1, 1, 1) via a zero-scale UV transform — exactly the right value for both albedo (multiplies through unchanged) and metallic-roughness (passes the material's roughness/metallic through unchanged).