glTF Sparse Accessor Indices and Values Explained
In glTF (GL Transmission Format), sparse accessors provide an
efficient method to store modifications or sparse data—such as morph
targets or blend shapes—without duplicating an entire base buffer. This
article explains how the indices and values
arrays within a sparse accessor property interact,
detailing the one-to-one mapping mechanism used to locate, extract, and
substitute data points within a base buffer array.
The Structure of a glTF Sparse Accessor
A standard glTF accessor defines a contiguous array of
elements (such as VEC3 positions or SCALAR
weights). When an accessor contains a sparse object, it
signals that most elements remain default (or zeroed, if no base
bufferView is supplied), while a subset of specific
elements are overwritten.
The sparse object contains three primary properties:
count: An integer defining the total number of elements that will be modified.indices: An object pointing to the buffer segment containing the target positions to be overwritten.values: An object pointing to the buffer segment containing the new payload data.
The indices Array
The indices object points to an array of integers using
a specified componentType (such as
UNSIGNED_BYTE, UNSIGNED_SHORT, or
UNSIGNED_INT).
Key characteristics include:
- Array Size: Contains exactly
sparse.countelements. - Reference Target: Each entry represents an element index within the main accessor's logical array, not a byte offset.
- Ordering Requirement: The glTF specification
requires that the values within the
indicesarray must be strictly increasing (\(index_0 < index_1 < index_2 \dots\)). This prevents conflicting assignments and allows decoders to process updates in a single linear pass.
The values Array
The values object specifies the raw substitution
data.
Key characteristics include:
- Data Type Matching: The data type, element type,
and component layout of the
valuesbuffer must strictly match the parent accessor'stypeandcomponentType(for instance, aVEC3ofFLOATcomponents). - Array Size: Contains exactly
sparse.countitems, matching the number of target indices element-for-element.
Interaction and Reconstruction Mechanism
The interaction between indices and values
operates as a synchronized, 1-to-1 parallel lookup:
- Initialization: A virtual array is initialized
using the parent accessor's configuration (
count,type,componentType). If the parent accessor specifies abufferView, the array is populated with base data. If nobufferViewis present, the array is initialized to all zeros. - Iterative Mapping: The runtime iterates through a
loop from index \(k = 0\) to \(k = \text{count} - 1\):
- It reads the target destination index: \(D = \text{indices}[k]\).
- It reads the corresponding source value: \(V = \text{values}[k]\).
- It assigns the value to the destination: \(\text{TargetArray}[D] = V\).
Because both arrays share identical element counts and correspond
index-by-index, the \(k\)-th index in
the indices array determines precisely where the \(k\)-th element from the values
array is placed. This decoupling allows 3D applications to store massive
datasets with sparse differences using only a fraction of the memory
that dense vertex buffers would otherwise require.