Skip to content

WebGL Rendering Pipeline

Overview

The WebGL rendering pipeline transforms Three.js scene graphs into rendered pixels via WebGL 2. The WebGLRenderer class orchestrates this process through initialization, render loop execution, scene traversal with frustum culling, render list construction, material program binding, and GPU draw call submission. The architecture uses specialized subsystems for resource management, state caching, and WebGL API interaction.

Related pages:

  • Page 3.2: Shader compilation, WebGLPrograms, WebGLProgram, and ShaderLib system
  • Page 3.3: WebGLState, WebGLTextures, WebGLGeometries, WebGLAttributes, WebGLBindingStates
  • Page 3.4: WebGLShadowMap, WebGLLights, shadow rendering
  • Page 3.5: WebGPU backend, node-based materials, TSL

WebGLRenderer Architecture

The WebGLRenderer class serves as the main entry point for WebGL rendering. It orchestrates a collection of specialized subsystems, each responsible for a specific aspect of GPU resource management and rendering.

Class Structure

The renderer is initialized with a WebGL 2 context and configuration parameters. As of r163, WebGL 1 is no longer supported src/renderers/WebGLRenderer.js104

Constructor Parameters:

  • canvas: HTMLCanvasElement or OffscreenCanvas
  • context: Existing WebGL2RenderingContext or null
  • depth, stencil, alpha, antialias: Buffer configurations
  • premultipliedAlpha, preserveDrawingBuffer: Alpha handling
  • powerPreference: GPU selection hint ('default', 'high-performance', 'low-power')
  • reversedDepthBuffer: Reverse-Z depth buffer support
  • outputBufferType: Color buffer format (default UnsignedByteType)

Subsystem Initialization

The initGLContext() function src/renderers/WebGLRenderer.js423-538 instantiates all subsystems in dependency order:

Subsystem Dependency Graph

SVG
100%

Subsystem Initialization Order src/renderers/WebGLRenderer.js426-458:

SubsystemPurposeDependencies
WebGLExtensionsExtension availability checkingGL context
WebGLUtilsFormat/type conversionExtensions
WebGLCapabilitiesFeature detection (max textures, precision, etc.)Extensions, Utils
WebGLStateState caching to minimize GL callsExtensions
WebGLInfoRender statistics trackingGL context
WebGLPropertiesWeakMap storage for object propertiesNone
WebGLTexturesTexture upload, mipmaps, disposalState, Properties, Capabilities
WebGLAttributesBuffer attribute managementGL context
WebGLBindingStatesVAO creation and bindingAttributes
WebGLGeometriesBufferGeometry → GL buffer mappingAttributes, BindingStates
WebGLObjectsObject update and disposal trackingGeometries, Attributes
WebGLProgramsShader compilation and cachingCapabilities, BindingStates
WebGLMaterialsMaterial property updatesProperties
WebGLRenderListsOpaque/transparent render queuesNone
WebGLRenderStatesLighting state per render callExtensions
WebGLBackgroundBackground/skybox renderingState, Objects
WebGLShadowMapShadow map generationObjects, Capabilities

Render Loop and Frame Execution

Main Render Function

The render(scene, camera) method src/renderers/WebGLRenderer.js1448-1647 executes the complete rendering pipeline.

Render Pipeline Flow

SVG
100%

Render State Management

Each render() call maintains isolated state using stacks src/renderers/WebGLRenderer.js136-143:

let currentRenderList = null;
let currentRenderState = null;
const renderListStack = [];
const renderStateStack = [];

This stack-based approach allows nested render() calls (e.g., during post-processing or portal rendering) without state interference src/renderers/WebGLRenderer.js139-140

Render State Initialization src/renderers/WebGLRenderer.js1463-1475:

  1. Get or create WebGLRenderState for the scene
  2. Push current state to stack
  3. Set as currentRenderState
  4. Setup lights via currentRenderState.setupLights()

Render List Initialization src/renderers/WebGLRenderer.js1477-1485:

  1. Get or create WebGLRenderList for scene/camera pair
  2. Push current list to stack
  3. Set as currentRenderList

Render Lists and States

WebGLRenderLists

WebGLRenderLists manages separate rendering queues for opaque and transparent objects. Each scene/camera combination gets its own render list src/renderers/WebGLRenderer.js454

Render List Population via Scene Traversal

The projectObject() function src/renderers/WebGLRenderer.js1650-1848 recursively traverses the scene graph:

SVG
100%

Sorting Strategy src/renderers/WebGLRenderer.js1498-1507:

  • Opaque objects: Front-to-back sorting (default painterSortStable) to maximize early-Z rejection
  • Transparent objects: Back-to-front sorting (default reversePainterSortStable) for correct alpha blending
  • Custom sort functions can be set via setOpaqueSort() and setTransparentSort()

WebGLRenderStates

WebGLRenderStates manages lighting state for each render call. It stores:

  • Active lights array
  • Light hashes for cache validation
  • Shadow casting lights
  • Light state (positions, colors, directions)

Light Setup src/renderers/WebGLRenderer.js1471-1472:

currentRenderState.setupLights();

This computes light uniforms once per frame, which are then shared across all materials in the scene.

Scene Traversal

Frustum Culling

The projectObject() function performs view frustum culling before adding objects to render lists src/renderers/WebGLRenderer.js1650-1848

Frustum Construction src/renderers/WebGLRenderer.js331:

const _frustum = new Frustum();

The frustum is updated from the camera's projection matrix:

_projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
_frustum.setFromProjectionMatrix(_projScreenMatrix);

Culling Decision src/renderers/WebGLRenderer.js1714-1722:

  • If object.frustumCulled === false, skip culling
  • Otherwise, test object's bounding sphere against frustum planes
  • If outside frustum, skip object and its children

Override Material

Scenes can force all objects to render with a single material via scene.overrideMaterial src/renderers/Scene.js113

Override Logic src/renderers/WebGLRenderer.js1686-1691:

const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
if (overrideMaterial !== null && material.allowOverride === true) {
    material = overrideMaterial;
}

This is commonly used for depth pre-pass, shadow map generation, or outline rendering.

Batched and Instanced Meshes

Special handling for high-performance rendering modes:

BatchedMesh src/renderers/WebGLRenderer.js1278-1308:

  • Uses WEBGL_multi_draw extension for multi-draw indirect rendering
  • Falls back to loop with _gl_DrawID uniform updates if extension unavailable
  • Stores _multiDrawStarts, _multiDrawCounts arrays

InstancedMesh src/renderers/WebGLRenderer.js1310-1320:

  • Uses renderInstances(drawStart, drawCount, instanceCount)
  • Instancing data uploaded via instance attributes (matrix, color)

Material Program Setup

Program Acquisition

The setProgram() function src/renderers/WebGLRenderer.js1850-1979 retrieves the compiled shader program for a given material/scene/object.

Program Acquisition Flow

SVG
100%

Program Caching

WebGLPrograms maintains a program cache keyed by material properties src/renderers/WebGLRenderer.js452:

Cache Key Components:

  • Material type and shader source
  • Light configuration (directional, point, spot, hemi counts)
  • Shadow settings
  • Fog type
  • Tone mapping
  • Clipping planes
  • Skinning and morphing attributes
  • Instance attributes

Double-Sided Material Handling src/renderers/WebGLRenderer.js1331-1350:

For transparent double-sided materials without forceSinglePass, two programs are compiled:

  1. material.side = BackSide - back faces
  2. material.side = FrontSide - front faces

This ensures correct transparency rendering at the cost of additional draw calls.

Material Uniform Updates

After program acquisition, material uniforms are refreshed src/renderers/WebGLRenderer.js1939-1967:

materials.refreshFogUniforms(m_uniforms, scene);
materials.refreshMaterialUniforms(m_uniforms, material, pixelRatio, height, transmissionRenderTarget);

The WebGLMaterials subsystem handles:

  • Fog uniforms (color, near, far, density)
  • Common material properties (opacity, diffuse, emissive)
  • Texture sampling parameters
  • PBR material properties (roughness, metalness, clearcoat)

Buffer Rendering

renderBufferDirect Function

The renderBufferDirect() function src/renderers/WebGLRenderer.js1156-1324 executes GPU draw calls for a single geometry/material/object.

Draw Call Execution Flow

SVG
100%

Draw Range Calculation

Base Draw Range src/renderers/WebGLRenderer.js1186-1209:

  • Start from geometry.drawRange.start
  • End at geometry.drawRange.start + geometry.drawRange.count
  • Apply rangeFactor = 2 for wireframe mode (line pairs)

Group Range Application src/renderers/WebGLRenderer.js1192-1196: If a group parameter is provided (for multi-material meshes):

drawStart = Math.max(drawStart, group.start * rangeFactor);
drawEnd = Math.min(drawEnd, (group.start + group.count) * rangeFactor);

WebGLBindingStates

The bindingStates.setup() call src/renderers/WebGLRenderer.js1217 establishes the Vertex Array Object (VAO) binding:

VAO Creation Strategy:

  • One VAO per unique combination of: object + material + program + geometry + index
  • VAOs cache the complete vertex attribute binding state
  • Switching VAOs is much faster than rebinding all attributes

Buffer Renderers

Two renderer classes handle the actual WebGL draw calls:

WebGLBufferRenderer src/renderers/WebGLRenderer.js460:

  • Non-indexed geometry (drawArrays)
  • render(start, count) for basic drawing
  • renderInstances(start, count, instances) for instanced drawing

WebGLIndexedBufferRenderer src/renderers/WebGLRenderer.js461:

  • Indexed geometry (drawElements)
  • setIndex(attribute) to bind index buffer
  • render(start, count) and renderInstances(start, count, instances)
  • renderMultiDraw(starts, counts, drawCount) for multi-draw indirect

Multi-Pass Rendering

Shadow Pass

Shadow maps are rendered before the main scene pass src/renderers/WebGLRenderer.js1520-1528:

if (shadowMap.enabled === true) {
    shadowMap.render(shadowsArray, scene, camera);
}

The WebGLShadowMap subsystem:

  1. Iterates over shadow-casting lights
  2. Renders scene from light's perspective
  3. Stores depth in shadow map textures
  4. Supports VSM (Variance Shadow Maps) mode

For details, see Shadow Mapping.

Background Rendering

Scene backgrounds are rendered after shadows but before scene objects src/renderers/WebGLRenderer.js1530-1538:

if (_renderBackground === true) {
    background.render(currentRenderList, scene);
}

Background Types src/scenes/Scene.js31-40:

  • Solid color
  • Texture (flat)
  • Cube texture (skybox)
  • Equirectangular texture

The WebGLBackground subsystem handles background-specific rendering and environment map setup.

Transmission Rendering

For materials with transmission > 0 (glass, liquids), a separate transmission render target captures the scene behind transparent objects:

Transmission Flow:

  1. Identify objects with transmission materials
  2. Render opaque scene to transmission target
  3. Bind transmission texture
  4. Render transparent objects with refraction sampling

This enables realistic refraction effects for PBR materials.

Render Target Management

The renderer maintains a stack of render targets src/renderers/WebGLRenderer.js1990-2099:

this.setRenderTarget = function(renderTarget, activeCubeFace, activeMipmapLevel) {
    _currentRenderTarget = renderTarget;
    // ... bind framebuffer, update viewport, etc.
}

Render Target Features:

  • Multiple color attachments (MRT support)
  • Depth/stencil attachments
  • Cube face rendering
  • Mipmap level selection
  • Automatic viewport adjustment

Subsystem Details

WebGLObjects

WebGLObjects src/renderers/WebGLRenderer.js449 tracks object updates and manages the update queue:

Responsibilities:

  • Detect geometry/material changes requiring GPU updates
  • Schedule buffer updates for dynamic geometries
  • Handle object disposal and cleanup
  • Manage morph target updates

WebGLGeometries

WebGLGeometries src/renderers/WebGLRenderer.js448 creates and manages GPU buffers for BufferGeometry instances:

Buffer Management:

  • Maps BufferGeometry.uuid to WebGL buffer objects
  • Handles interleaved buffer attributes
  • Generates wireframe index buffers
  • Updates dynamic attributes via updateRange
  • Computes bounding volumes

For detailed geometry system documentation, see Geometry System.

WebGLAttributes

WebGLAttributes src/renderers/WebGLRenderer.js446 manages individual buffer attributes:

Attribute Operations:

  • Create WebGL buffers from BufferAttribute typed arrays
  • Upload data with bufferData or bufferSubData
  • Handle static vs dynamic usage hints
  • Track buffer versions for update detection
  • Support for updateRange partial updates

WebGLBindingStates

WebGLBindingStates src/renderers/WebGLRenderer.js445 manages Vertex Array Objects (VAOs) for efficient attribute binding.

VAO Caching Strategy:

ComponentDescription
Cache KeyHash of object.id, geometry.id, program.id, material.wireframe
VAO ContentsVertex attribute bindings (position, normal, uv, etc.), index buffer binding, attribute enable states
PerformanceSwitching VAOs is ~10x faster than rebinding all attributes individually
CreationCalled from bindingStates.setup(object, material, program, geometry, index) src/renderers/WebGLRenderer.js1214

Binding Sequence:

  1. bindingStates.setup() called before draw
  2. Compute cache key from object/program/geometry
  3. If VAO exists, bind it; otherwise create and configure new VAO
  4. VAO stores all gl.vertexAttribPointer() calls and gl.bindBuffer(ELEMENT_ARRAY_BUFFER) state
  5. Subsequent draws with same combination reuse cached VAO

State Change Optimization

All subsystems coordinate to minimize redundant WebGL state changes:

Cached State:

  • Current program binding
  • Texture unit bindings (tracks last 8+ units)
  • Blend mode, depth test, stencil test
  • Viewport, scissor rect
  • VAO binding
  • Framebuffer binding

The WebGLState subsystem compares requested state against cached state before issuing GL calls, providing significant performance benefits.

Render Loop Integration

Animation Loop

The renderer provides an animation loop helper src/renderers/WebGLRenderer.js1363-1380:

this.setAnimationLoop = function(callback) {
    animation.setAnimationLoop(callback);
}

Animation System:

  • Integrates with requestAnimationFrame or WebXR frame callbacks
  • Calls user callback with high-precision timestamp
  • Automatically suspends when tab is not visible
  • Handles XR session lifecycle

Context Loss Recovery

WebGL context loss handling src/renderers/WebGLRenderer.js383-385:

canvas.addEventListener('webglcontextlost', onContextLost, false);
canvas.addEventListener('webglcontextrestored', onContextRestore, false);

Recovery Process src/renderers/WebGLRenderer.js1086-1106:

  1. Detect context loss event
  2. Set _isContextLost = true flag
  3. On restore event, call initGLContext() to recreate subsystems
  4. Restore previous shadow map settings
  5. Resume rendering

All GPU resources (buffers, textures, programs) must be recreated after context restore.

Performance Monitoring

The info object src/renderers/WebGLRenderer.js538 tracks render statistics:

Tracked Metrics:

  • info.render.calls - Draw calls per frame
  • info.render.triangles - Triangle count
  • info.render.points - Point count
  • info.render.lines - Line count
  • info.memory.geometries - Active geometry count
  • info.memory.textures - Active texture count
  • info.programs - Compiled shader programs

Auto-Reset Behavior:

renderer.info.autoReset = false; // Disable automatic reset
renderer.info.reset();           // Manual reset