Skip to content

WebGL 渲染管线

概述

WebGL 渲染管线通过 WebGL 2 将 Three.js 场景图转换为渲染像素。WebGLRenderer 类通过初始化、渲染循环执行、带视锥剔除的场景遍历、渲染列表构建、材质程序绑定和 GPU 绘制调用提交来编排此过程。该架构使用专门的子系统进行资源管理、状态缓存和 WebGL API 交互。

相关页面:

  • 第 3.2 页:着色器编译、WebGLProgramsWebGLProgramShaderLib 系统
  • 第 3.3 页WebGLStateWebGLTexturesWebGLGeometriesWebGLAttributesWebGLBindingStates
  • 第 3.4 页WebGLShadowMapWebGLLights、阴影渲染
  • 第 3.5 页:WebGPU 后端、基于节点的材质、TSL

WebGLRenderer 架构

WebGLRenderer 类作为 WebGL 渲染的主要入口点。它编排专门子系统的集合,每个子系统负责 GPU 资源管理和渲染的特定方面。

类结构

渲染器使用 WebGL 2 上下文和配置参数初始化。自 r163 起不再支持 WebGL 1 src/renderers/WebGLRenderer.js104

构造参数:

  • canvas:HTMLCanvasElement 或 OffscreenCanvas
  • context:现有的 WebGL2RenderingContext 或 null
  • depthstencilalphaantialias:缓冲区配置
  • premultipliedAlphapreserveDrawingBuffer:alpha 处理
  • powerPreference:GPU 选择提示('default'、'high-performance'、'low-power')
  • reversedDepthBuffer:反向 Z 深度缓冲区支持
  • outputBufferType:颜色缓冲区格式(默认 UnsignedByteType)

子系统初始化

initGLContext() 函数 src/renderers/WebGLRenderer.js423-538 按依赖顺序实例化所有子系统:

子系统依赖图

SVG
100%

子系统初始化顺序 src/renderers/WebGLRenderer.js426-458

子系统用途依赖项
WebGLExtensions扩展可用性检查GL 上下文
WebGLUtils格式/类型转换Extensions
WebGLCapabilities特性检测(最大纹理、精度等)Extensions, Utils
WebGLState状态缓存以最小化 GL 调用Extensions
WebGLInfo渲染统计跟踪GL 上下文
WebGLPropertiesWeakMap 存储对象属性
WebGLTextures纹理上传、mipmap、处置State, Properties, Capabilities
WebGLAttributes缓冲区属性管理GL 上下文
WebGLBindingStatesVAO 创建和绑定Attributes
WebGLGeometriesBufferGeometry → GL 缓冲区映射Attributes, BindingStates
WebGLObjects对象更新和处置跟踪Geometries, Attributes
WebGLPrograms着色器编译和缓存Capabilities, BindingStates
WebGLMaterials材质属性更新Properties
WebGLRenderLists不透明/透明渲染队列
WebGLRenderStates每渲染调用的光照状态Extensions
WebGLBackground背景/天空盒渲染State, Objects
WebGLShadowMap阴影图生成Objects, Capabilities

渲染循环与帧执行

主要渲染函数

render(scene, camera) 方法 src/renderers/WebGLRenderer.js1448-1647 执行完整渲染管线。

渲染管线流

SVG
100%

渲染状态管理

每个 render() 调用使用栈维护隔离状态 src/renderers/WebGLRenderer.js136-143

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

这种基于栈的方法允许嵌套 render() 调用(如在后处理或门户渲染期间)而无状态干扰 src/renderers/WebGLRenderer.js139-140

渲染状态初始化 src/renderers/WebGLRenderer.js1463-1475

  1. 获取或为场景创建 WebGLRenderState
  2. 将当前状态压入栈
  3. 设置为 currentRenderState
  4. 通过 currentRenderState.setupLights() 设置光照

渲染列表初始化 src/renderers/WebGLRenderer.js1477-1485

  1. 获取或为场景/相机对创建 WebGLRenderList
  2. 将当前列表压入栈
  3. 设置为 currentRenderList

渲染列表与状态

WebGLRenderLists

WebGLRenderLists 管理不透明和透明对象的单独渲染队列。每个场景/相机组合获得自己的渲染列表 src/renderers/WebGLRenderer.js454

通过场景遍历的渲染列表填充

projectObject() 函数 src/renderers/WebGLRenderer.js1650-1848 递归遍历场景图:

SVG
100%

排序策略 src/renderers/WebGLRenderer.js1498-1507

  • 不透明对象:从前到后排序(默认 painterSortStable)以最大化早期 Z 剔除
  • 透明对象:从后到前排序(默认 reversePainterSortStable)以获得正确的 alpha 混合
  • 可通过 setOpaqueSort()setTransparentSort() 设置自定义排序函数

WebGLRenderStates

WebGLRenderStates 管理每个渲染调用的光照状态。它存储:

  • 活动光源数组
  • 用于缓存验证的光源哈希值
  • 投射阴影的光源
  • 光源状态(位置、颜色、方向)

光照设置 src/renderers/WebGLRenderer.js1471-1472

currentRenderState.setupLights();

这每帧计算一次光源 uniform,然后在场景中的所有材质间共享。

场景遍历

视锥剔除

projectObject() 函数在将对象添加到渲染列表前执行视图视锥剔除 src/renderers/WebGLRenderer.js1650-1848

视锥构造 src/renderers/WebGLRenderer.js331

const _frustum = new Frustum();

视锥从相机的投影矩阵更新:

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

剔除决策 src/renderers/WebGLRenderer.js1714-1722

  • 如果 object.frustumCulled === false,跳过剔除
  • 否则,测试对象的边界球与视锥平面
  • 如果在视锥外,跳过对象及其子对象

覆盖材质

场景可通过 scene.overrideMaterial 强制所有对象使用单一材质渲染 src/renderers/Scene.js113

覆盖逻辑 src/renderers/WebGLRenderer.js1686-1691

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

这通常用于深度预通道、阴影图生成或轮廓渲染。

批处理与实例化网格

高性能渲染模式的特殊处理:

BatchedMesh src/renderers/WebGLRenderer.js1278-1308

  • 使用 WEBGL_multi_draw 扩展进行多绘制间接渲染
  • 如果扩展不可用则回退到带 _gl_DrawID uniform 更新的循环
  • 存储 _multiDrawStarts_multiDrawCounts 数组

InstancedMesh src/renderers/WebGLRenderer.js1310-1320

  • 使用 renderInstances(drawStart, drawCount, instanceCount)
  • 通过实例属性(矩阵、颜色)上传实例化数据

材质程序设置

程序获取

setProgram() 函数 src/renderers/WebGLRenderer.js1850-1979 为给定材质/场景/对象检索已编译着色器程序。

程序获取流

SVG
100%

程序缓存

WebGLPrograms 维护按键为材质属性的程序缓存 src/renderers/WebGLRenderer.js452

缓存键组件:

  • 材质类型和着色器源
  • 光照配置(方向光、点光、聚光、半球光数量)
  • 阴影设置
  • 雾效类型
  • 色调映射
  • 剪切平面
  • 蒙皮和形态属性
  • 实例属性

双面材质处理 src/renderers/WebGLRenderer.js1331-1350

对于没有 forceSinglePass 的透明双面材质,编译两个程序:

  1. material.side = BackSide - 背面
  2. material.side = FrontSide - 正面

这确保正确的透明度渲染,代价是额外的绘制调用。

材质 Uniform 更新

程序获取后,刷新材质 uniform src/renderers/WebGLRenderer.js1939-1967

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

WebGLMaterials 子系统处理:

  • 雾效 uniform(颜色、近平面、远平面、密度)
  • 常见材质属性(不透明度、漫反射、自发光)
  • 纹理采样参数
  • PBR 材质属性(粗糙度、金属度、清漆)

缓冲区渲染

renderBufferDirect 函数

renderBufferDirect() 函数 src/renderers/WebGLRenderer.js1156-1324 为单个几何体/材质/对象执行 GPU 绘制调用。

绘制调用执行流

SVG
100%

绘制范围计算

基础绘制范围 src/renderers/WebGLRenderer.js1186-1209

  • geometry.drawRange.start 开始
  • 结束于 geometry.drawRange.start + geometry.drawRange.count
  • 对线框模式应用 rangeFactor = 2(线对)

组范围应用 src/renderers/WebGLRenderer.js1192-1196:如果提供了 group 参数(用于多材质网格):

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

WebGLBindingStates

bindingStates.setup() 调用 src/renderers/WebGLRenderer.js1217 建立顶点数组对象(VAO)绑定:

VAO 创建策略:

  • 每个唯一组合一个 VAO:对象 + 材质 + 程序 + 几何体 + 索引
  • VAO 缓存完整顶点属性绑定状态
  • 切换 VAO 比重新绑定所有属性快得多

缓冲区渲染器

两个渲染器类处理实际的 WebGL 绘制调用:

WebGLBufferRenderer src/renderers/WebGLRenderer.js460

  • 非索引几何体(drawArrays)
  • render(start, count) 用于基本绘制
  • renderInstances(start, count, instances) 用于实例化绘制

WebGLIndexedBufferRenderer src/renderers/WebGLRenderer.js461

  • 索引几何体(drawElements)
  • setIndex(attribute) 绑定索引缓冲区
  • render(start, count)renderInstances(start, count, instances)
  • renderMultiDraw(starts, counts, drawCount) 用于多绘制间接

多通道渲染

阴影通道

阴影图在主场景通道前渲染 src/renderers/WebGLRenderer.js1520-1528

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

WebGLShadowMap 子系统:

  1. 遍历投射阴影的光源
  2. 从光源视角渲染场景
  3. 在阴影图纹理中存储深度
  4. 支持 VSM(方差阴影图)模式

详情见阴影映射

背景渲染

场景背景在阴影后但在场景对象前渲染 src/renderers/WebGLRenderer.js1530-1538

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

背景类型 src/scenes/Scene.js31-40

  • 纯色
  • 纹理(平面)
  • 立方体纹理(天空盒)
  • 等距矩形纹理

WebGLBackground 子系统处理背景特定渲染和环境贴图设置。

透射渲染

对于具有 transmission > 0 的材质(玻璃、液体),单独的透射渲染目标捕获透明对象后的场景:

透射流:

  1. 识别具有透射材质的对象
  2. 将不透明场景渲染到透射目标
  3. 绑定透射纹理
  4. 使用折射采样渲染透明对象

这为 PBR 材质实现真实的折射效果。

渲染目标管理

渲染器维护渲染目标栈 src/renderers/WebGLRenderer.js1990-2099

this.setRenderTarget = function(renderTarget, activeCubeFace, activeMipmapLevel) {
    _currentRenderTarget = renderTarget;
    // ... 绑定帧缓冲区,更新视口等
}

渲染目标特性:

  • 多颜色附件(MRT 支持)
  • 深度/模板附件
  • 立方体面渲染
  • Mipmap 级别选择
  • 自动视口调整

子系统详情

WebGLObjects

WebGLObjects src/renderers/WebGLRenderer.js449 跟踪对象更新并管理更新队列:

职责:

  • 检测需要 GPU 更新的几何体/材质变更
  • 为动态几何体调度缓冲区更新
  • 处理对象处置和清理
  • 管理形态目标更新

WebGLGeometries

WebGLGeometries src/renderers/WebGLRenderer.js448BufferGeometry 实例创建和管理 GPU 缓冲区:

缓冲区管理:

  • 映射 BufferGeometry.uuid 到 WebGL 缓冲区对象
  • 处理交错缓冲区属性
  • 生成线框索引缓冲区
  • 通过 updateRange 更新动态属性
  • 计算边界体积

详细几何体系统文档见几何体系统

WebGLAttributes

WebGLAttributes src/renderers/WebGLRenderer.js446 管理单个缓冲区属性:

属性操作:

  • BufferAttribute 类型化数组创建 WebGL 缓冲区
  • 使用 bufferDatabufferSubData 上传数据
  • 处理静态与动态使用提示
  • 跟踪缓冲区版本以进行更新检测
  • 支持 updateRange 部分更新

WebGLBindingStates

WebGLBindingStates src/renderers/WebGLRenderer.js445 管理顶点数组对象(VAO)以实现高效属性绑定。

VAO 缓存策略:

组件描述
缓存键object.id、geometry.id、program.id、material.wireframe 的哈希
VAO 内容顶点属性绑定(位置、法线、uv 等)、索引缓冲区绑定、属性启用状态
性能切换 VAO 比单独重新绑定所有属性快约 10 倍
创建从 bindingStates.setup(object, material, program, geometry, index) 调用 src/renderers/WebGLRenderer.js1214

绑定序列:

  1. 绘制前调用 bindingStates.setup()
  2. 从 object/program/geometry 计算缓存键
  3. 如果 VAO 存在则绑定;否则创建和配置新 VAO
  4. VAO 存储所有 gl.vertexAttribPointer() 调用和 gl.bindBuffer(ELEMENT_ARRAY_BUFFER) 状态
  5. 具有相同组合的后续绘制复用缓存的 VAO

状态变更优化

所有子系统协调以最小化冗余 WebGL 状态变更:

缓存状态:

  • 当前程序绑定
  • 纹理单元绑定(跟踪最后 8+ 个单元)
  • 混合模式、深度测试、模板测试
  • 视口、剪裁矩形
  • VAO 绑定
  • 帧缓冲区绑定

WebGLState 子系统在发出 GL 调用前比较请求状态与缓存状态,提供显著的性能优势。

渲染循环集成

动画循环

渲染器提供动画循环助手 src/renderers/WebGLRenderer.js1363-1380

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

动画系统:

  • requestAnimationFrame 或 WebXR 帧回调集成
  • 以高精度时间戳调用用户回调
  • 标签页不可见时自动暂停
  • 处理 XR 会话生命周期

上下文丢失恢复

WebGL 上下文丢失处理 src/renderers/WebGLRenderer.js383-385

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

恢复过程 src/renderers/WebGLRenderer.js1086-1106

  1. 检测上下文丢失事件
  2. 设置 _isContextLost = true 标志
  3. 在恢复事件上,调用 initGLContext() 重新创建子系统
  4. 恢复先前阴影图设置
  5. 恢复渲染

上下文恢复后必须重新创建所有 GPU 资源(缓冲区、纹理、程序)。

性能监控

info 对象 src/renderers/WebGLRenderer.js538 跟踪渲染统计:

跟踪指标:

  • info.render.calls - 每帧绘制调用
  • info.render.triangles - 三角形计数
  • info.render.points - 点计数
  • info.render.lines - 线计数
  • info.memory.geometries - 活动几何体计数
  • info.memory.textures - 活动纹理计数
  • info.programs - 已编译着色器程序

自动重置行为:

renderer.info.autoReset = false; // 禁用自动重置
renderer.info.reset();           // 手动重置