How to Encode Point Clouds Using glTF and Draco

This article provides an overview of how point cloud datasets are structured, compressed, and encoded using the glTF 2.0 format paired with Google Draco compression. It covers the structural definition of points within glTF primitives, the configuration of the KHR_draco_mesh_compression extension, the quantization and spatial encoding of point attributes, and the decoding pathway for real-time visualization.

glTF Representation of Point Clouds

In the glTF (GL Transmission Format) specification, geometry is defined through meshes containing arrays of primitives. A point cloud is defined by setting the primitive's rendering mode to 0 (which corresponds to POINTS in WebGL/OpenGL). Unlike standard polygonal meshes, point clouds do not require triangle index arrays (indices), though an index buffer can optionally be provided.

The basic properties of each point are stored as vertex attributes. Standard glTF attributes include:

  • POSITION: Floating-point coordinates (XYZ) defining point locations.
  • COLOR_0: RGB or RGBA values representing color data.
  • NORMAL: Surface normal vectors, if surface reconstruction or lighting calculations are required.
  • Application-specific application data, such as LiDAR intensity, classification, or timestamps, can be represented using custom attributes prefixed with an underscore (e.g., _INTENSITY).

Draco Point Cloud Compression

Raw point cloud datasets containing millions of points result in massive file sizes that hinder web transmission. Google Draco addresses this via the KHR_draco_mesh_compression extension in glTF.

When applied to point clouds, Draco bypasses its edge-breaker connectivity compression (which is used for triangle meshes) and instead utilizes point-cloud-specific compression techniques:

  1. Quantization: High-precision floating-point values (like 32-bit floats for positions) are scaled and converted into discrete integer grids (e.g., 11-bit to 16-bit integers). This determines the coordinate precision and provides the primary baseline for size reduction.
  2. Spatial Partitioning: Draco organizes points spatially using a Kd-tree decomposition. Points are recursively split along alternating spatial axes, allowing Draco to encode spatial coordinates as relative deltas within bounding boxes rather than absolute coordinates.
  3. Prediction Schemes: Attributes like colors and normals are encoded using predictive schemes based on spatial proximity. Once point positions are ordered via the Kd-tree, subsequent attribute values can be predicted from neighboring points.
  4. Entropy Coding: The resulting quantized and delta-encoded integer streams are compressed using an entropy encoder (such as Shannon entropy or ANS - Asymmetric Numeral Systems) to eliminate bit redundancy.

Encoding Draco Point Attributes in glTF

To bind Draco-compressed point data into a glTF asset, the primitive definition in the glTF JSON references the KHR_draco_mesh_compression extension:

{
  "meshes": [
    {
      "primitives": [
        {
          "mode": 0,
          "attributes": {
            "POSITION": 0,
            "COLOR_0": 1
          },
          "extensions": {
            "KHR_draco_mesh_compression": {
              "bufferView": 0,
              "attributes": {
                "POSITION": 0,
                "COLOR_0": 1
              }
            }
          }
        }
      ]
    }
  ]
}

The encoding structure requires two parallel definitions:

  • Fallback Accessors: The standard attributes dictionary at the primitive root points to standard glTF accessors. These provide metadata (such as minimum and maximum bounds) and serve as uncompressed fallbacks for decoders that do not support Draco.
  • Draco Extension Mapping: Inside extensions.KHR_draco_mesh_compression, the bufferView points to the contiguous binary blob containing the Draco-compressed payload. The inner attributes map binds glTF attribute semantics (POSITION, COLOR_0, etc.) to unique integer Draco attribute IDs configured during the Draco encoding process.

Decoding and Rendering Pipeline

When an application loads the glTF point cloud, it checks the extensionsRequired and extensionsUsed arrays. If KHR_draco_mesh_compression is detected:

  1. The Draco payload is extracted from the specified bufferView.
  2. The WebAssembly or native Draco decoder unpacks the Kd-tree and predictive streams.
  3. Quantized integers are dequantized back into standard typed arrays (e.g., Float32Array).
  4. The unpacked attribute arrays are bound to standard GPU vertex buffer objects (VBOs) and rendered using the POINTS draw mode.