How Three.js Loads and Displays glTF Models

This article explores how modern web graphics engines, particularly Three.js, ingest, decode, and render 3D assets using the glTF (GL Transmission Format) standard. It details the entire technical pipeline, covering network retrieval, parsing the JSON hierarchy and binary buffers, instantiating engine-level scene graph objects, uploading geometry and textures to the GPU via WebGL or WebGPU, and executing the render loop.

1. Fetching and Dissecting the glTF Container

The ingestion process begins using a dedicated loader, such as THREE.GLTFLoader. glTF assets arrive in one of two formats:

  • Standard glTF (.gltf): A JSON file accompanied by external payloads (separate binary .bin files for geometry and animation data, and standard image files for textures).
  • Binary glTF (.glb): A self-contained binary container encapsulating the JSON chunk, binary buffers, and texture images in structured byte blocks.

When handling a .glb file, the loader parses a 12-byte header containing a magic number (0x46546C67), version number, and total byte length. It then extracts two primary chunks: Chunk 0, which contains the UTF-8 encoded JSON schema, and Chunk 1, which holds the contiguous binary buffer (ArrayBuffer).

2. Deserializing Binary Data into Memory

glTF is structured to map directly to modern GPU memory layouts, avoiding costly runtime translation. To recreate 3D geometry, the engine reads three interconnected elements within the glTF JSON:

  1. buffers: Raw arrays of binary data.
  2. bufferViews: Slices of a buffer defining specific offsets, byte lengths, and optional stride intervals.
  3. accessors: Metadata defining how to read a bufferView (e.g., component type like FLOAT or UNSIGNED_SHORT, and data type like VEC3 for coordinates or VEC2 for UV maps).

Three.js resolves these accessors into JavaScript TypedArrays (such as Float32Array or Uint16Array). Each accessor representing an attribute—such as vertex positions, normals, tangents, and UV coordinates—is wrapped into a THREE.BufferAttribute. Combined, these form a THREE.BufferGeometry object.

3. Reconstructing the Scene Graph

The engine parses the structural hierarchy defined in the glTF JSON to recreate parent-child relationships in the DOM-like 3D scene graph:

  • Nodes: Represent transformation points in space. The engine creates THREE.Object3D or THREE.Group instances, reading local transformation matrices or distinct translation, rotation (quaternion), and scale vectors.
  • Meshes: Link structural nodes to geometry and materials. Three.js instantiates a THREE.Mesh, assigning the deserialized BufferGeometry and constructing the corresponding material.
  • Cameras and Lights: If defined (or supported via extensions like KHR_lights_punctual), these are instantiated as native Three.js camera and light objects.

4. Material and Texture Processing

glTF natively uses Physically Based Rendering (PBR). During deserialization, the engine parses material descriptions into Three.js materials, typically MeshStandardMaterial or MeshPhysicalMaterial:

  • PBR Properties: Values such as baseColorFactor, metallicFactor, and roughnessFactor are converted directly into material uniforms.
  • Textures: Embedded images extracted from binary chunks or external URLs are decoded asynchronously using native browser APIs (createImageBitmap or HTMLImageElement) and instantiated as THREE.Texture objects. These are linked to their corresponding slots, such as roughness-metallic maps, normal maps, or emissive maps.

5. GPU Allocation and Drawing

Once JavaScript has constructed the scene graph, the data must be transferred to the graphics hardware:

  1. Buffer Allocation: When the object enters the render frustum, Three.js binds vertex attributes via WebGL/WebGPU functions (e.g., gl.bindBuffer and gl.bufferData). The typed arrays generated by accessors are directly loaded into GPU memory as Vertex Buffer Objects (VBOs).
  2. Shader Generation: Three.js compiles specialized vertex and fragment shaders that match the material's properties, active lighting, and any vertex skinning requirements.
  3. Draw Calls: In the active animation loop, the engine traverses the scene, calculates the world transformation matrix for each mesh, binds the relevant textures and VBOs, and executes indexed drawing calls (such as gl.drawElements) to render the final model onto the canvas.