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, andShaderLibsystem - 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 OffscreenCanvascontext: Existing WebGL2RenderingContext or nulldepth,stencil,alpha,antialias: Buffer configurationspremultipliedAlpha,preserveDrawingBuffer: Alpha handlingpowerPreference: GPU selection hint ('default', 'high-performance', 'low-power')reversedDepthBuffer: Reverse-Z depth buffer supportoutputBufferType: Color buffer format (default UnsignedByteType)
Subsystem Initialization
The initGLContext() function src/renderers/WebGLRenderer.js423-538 instantiates all subsystems in dependency order:
Subsystem Dependency Graph
Subsystem Initialization Order src/renderers/WebGLRenderer.js426-458:
| Subsystem | Purpose | Dependencies |
|---|---|---|
| WebGLExtensions | Extension availability checking | GL context |
| WebGLUtils | Format/type conversion | Extensions |
| WebGLCapabilities | Feature detection (max textures, precision, etc.) | Extensions, Utils |
| WebGLState | State caching to minimize GL calls | Extensions |
| WebGLInfo | Render statistics tracking | GL context |
| WebGLProperties | WeakMap storage for object properties | None |
| WebGLTextures | Texture upload, mipmaps, disposal | State, Properties, Capabilities |
| WebGLAttributes | Buffer attribute management | GL context |
| WebGLBindingStates | VAO creation and binding | Attributes |
| WebGLGeometries | BufferGeometry → GL buffer mapping | Attributes, BindingStates |
| WebGLObjects | Object update and disposal tracking | Geometries, Attributes |
| WebGLPrograms | Shader compilation and caching | Capabilities, BindingStates |
| WebGLMaterials | Material property updates | Properties |
| WebGLRenderLists | Opaque/transparent render queues | None |
| WebGLRenderStates | Lighting state per render call | Extensions |
| WebGLBackground | Background/skybox rendering | State, Objects |
| WebGLShadowMap | Shadow map generation | Objects, 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
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:
- Get or create
WebGLRenderStatefor the scene - Push current state to stack
- Set as
currentRenderState - Setup lights via
currentRenderState.setupLights()
Render List Initialization src/renderers/WebGLRenderer.js1477-1485:
- Get or create
WebGLRenderListfor scene/camera pair - Push current list to stack
- 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:
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()andsetTransparentSort()
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_drawextension for multi-draw indirect rendering - Falls back to loop with
_gl_DrawIDuniform updates if extension unavailable - Stores
_multiDrawStarts,_multiDrawCountsarrays
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
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:
material.side = BackSide- back facesmaterial.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
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 = 2for 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 drawingrenderInstances(start, count, instances)for instanced drawing
WebGLIndexedBufferRenderer src/renderers/WebGLRenderer.js461:
- Indexed geometry (drawElements)
setIndex(attribute)to bind index bufferrender(start, count)andrenderInstances(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:
- Iterates over shadow-casting lights
- Renders scene from light's perspective
- Stores depth in shadow map textures
- 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:
- Identify objects with transmission materials
- Render opaque scene to transmission target
- Bind transmission texture
- 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.uuidto 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
BufferAttributetyped arrays - Upload data with
bufferDataorbufferSubData - Handle static vs dynamic usage hints
- Track buffer versions for update detection
- Support for
updateRangepartial updates
WebGLBindingStates
WebGLBindingStates src/renderers/WebGLRenderer.js445 manages Vertex Array Objects (VAOs) for efficient attribute binding.
VAO Caching Strategy:
| Component | Description |
|---|---|
| Cache Key | Hash of object.id, geometry.id, program.id, material.wireframe |
| VAO Contents | Vertex attribute bindings (position, normal, uv, etc.), index buffer binding, attribute enable states |
| Performance | Switching VAOs is ~10x faster than rebinding all attributes individually |
| Creation | Called from bindingStates.setup(object, material, program, geometry, index) src/renderers/WebGLRenderer.js1214 |
Binding Sequence:
bindingStates.setup()called before draw- Compute cache key from object/program/geometry
- If VAO exists, bind it; otherwise create and configure new VAO
- VAO stores all
gl.vertexAttribPointer()calls andgl.bindBuffer(ELEMENT_ARRAY_BUFFER)state - 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
requestAnimationFrameor 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:
- Detect context loss event
- Set
_isContextLost = trueflag - On restore event, call
initGLContext()to recreate subsystems - Restore previous shadow map settings
- 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 frameinfo.render.triangles- Triangle countinfo.render.points- Point countinfo.render.lines- Line countinfo.memory.geometries- Active geometry countinfo.memory.textures- Active texture countinfo.programs- Compiled shader programs
Auto-Reset Behavior:
renderer.info.autoReset = false; // Disable automatic reset
renderer.info.reset(); // Manual reset