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.binfiles 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:
buffers: Raw arrays of binary data.bufferViews: Slices of a buffer defining specific offsets, byte lengths, and optional stride intervals.accessors: Metadata defining how to read abufferView(e.g., component type likeFLOATorUNSIGNED_SHORT, and data type likeVEC3for coordinates orVEC2for 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.Object3DorTHREE.Groupinstances, 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 deserializedBufferGeometryand 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, androughnessFactorare converted directly into material uniforms. - Textures: Embedded images extracted from binary
chunks or external URLs are decoded asynchronously using native browser
APIs (
createImageBitmaporHTMLImageElement) and instantiated asTHREE.Textureobjects. 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:
- Buffer Allocation: When the object enters the
render frustum, Three.js binds vertex attributes via WebGL/WebGPU
functions (e.g.,
gl.bindBufferandgl.bufferData). The typed arrays generated by accessors are directly loaded into GPU memory as Vertex Buffer Objects (VBOs). - Shader Generation: Three.js compiles specialized vertex and fragment shaders that match the material's properties, active lighting, and any vertex skinning requirements.
- 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.