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.count elements.
  • 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 indices array 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 values buffer must strictly match the parent accessor's type and componentType (for instance, a VEC3 of FLOAT components).
  • Array Size: Contains exactly sparse.count items, 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:

  1. Initialization: A virtual array is initialized using the parent accessor's configuration (count, type, componentType). If the parent accessor specifies a bufferView, the array is populated with base data. If no bufferView is present, the array is initialized to all zeros.
  2. 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.