WebGL 渲染管线
概述
WebGL 渲染管线通过 WebGL 2 将 Three.js 场景图转换为渲染像素。WebGLRenderer 类通过初始化、渲染循环执行、带视锥剔除的场景遍历、渲染列表构建、材质程序绑定和 GPU 绘制调用提交来编排此过程。该架构使用专门的子系统进行资源管理、状态缓存和 WebGL API 交互。
相关页面:
- 第 3.2 页:着色器编译、
WebGLPrograms、WebGLProgram和ShaderLib系统 - 第 3.3 页:
WebGLState、WebGLTextures、WebGLGeometries、WebGLAttributes、WebGLBindingStates - 第 3.4 页:
WebGLShadowMap、WebGLLights、阴影渲染 - 第 3.5 页:WebGPU 后端、基于节点的材质、TSL
WebGLRenderer 架构
WebGLRenderer 类作为 WebGL 渲染的主要入口点。它编排专门子系统的集合,每个子系统负责 GPU 资源管理和渲染的特定方面。
类结构
渲染器使用 WebGL 2 上下文和配置参数初始化。自 r163 起不再支持 WebGL 1 src/renderers/WebGLRenderer.js104
构造参数:
canvas:HTMLCanvasElement 或 OffscreenCanvascontext:现有的 WebGL2RenderingContext 或 nulldepth、stencil、alpha、antialias:缓冲区配置premultipliedAlpha、preserveDrawingBuffer:alpha 处理powerPreference:GPU 选择提示('default'、'high-performance'、'low-power')reversedDepthBuffer:反向 Z 深度缓冲区支持outputBufferType:颜色缓冲区格式(默认 UnsignedByteType)
子系统初始化
initGLContext() 函数 src/renderers/WebGLRenderer.js423-538 按依赖顺序实例化所有子系统:
子系统依赖图
子系统初始化顺序 src/renderers/WebGLRenderer.js426-458:
| 子系统 | 用途 | 依赖项 |
|---|---|---|
| WebGLExtensions | 扩展可用性检查 | GL 上下文 |
| WebGLUtils | 格式/类型转换 | Extensions |
| WebGLCapabilities | 特性检测(最大纹理、精度等) | Extensions, Utils |
| WebGLState | 状态缓存以最小化 GL 调用 | Extensions |
| WebGLInfo | 渲染统计跟踪 | GL 上下文 |
| WebGLProperties | WeakMap 存储对象属性 | 无 |
| WebGLTextures | 纹理上传、mipmap、处置 | State, Properties, Capabilities |
| WebGLAttributes | 缓冲区属性管理 | GL 上下文 |
| WebGLBindingStates | VAO 创建和绑定 | Attributes |
| WebGLGeometries | BufferGeometry → 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 执行完整渲染管线。
渲染管线流
渲染状态管理
每个 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:
- 获取或为场景创建
WebGLRenderState - 将当前状态压入栈
- 设置为
currentRenderState - 通过
currentRenderState.setupLights()设置光照
渲染列表初始化 src/renderers/WebGLRenderer.js1477-1485:
- 获取或为场景/相机对创建
WebGLRenderList - 将当前列表压入栈
- 设置为
currentRenderList
渲染列表与状态
WebGLRenderLists
WebGLRenderLists 管理不透明和透明对象的单独渲染队列。每个场景/相机组合获得自己的渲染列表 src/renderers/WebGLRenderer.js454
通过场景遍历的渲染列表填充
projectObject() 函数 src/renderers/WebGLRenderer.js1650-1848 递归遍历场景图:
排序策略 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_DrawIDuniform 更新的循环 - 存储
_multiDrawStarts、_multiDrawCounts数组
InstancedMesh src/renderers/WebGLRenderer.js1310-1320:
- 使用
renderInstances(drawStart, drawCount, instanceCount) - 通过实例属性(矩阵、颜色)上传实例化数据
材质程序设置
程序获取
setProgram() 函数 src/renderers/WebGLRenderer.js1850-1979 为给定材质/场景/对象检索已编译着色器程序。
程序获取流
程序缓存
WebGLPrograms 维护按键为材质属性的程序缓存 src/renderers/WebGLRenderer.js452:
缓存键组件:
- 材质类型和着色器源
- 光照配置(方向光、点光、聚光、半球光数量)
- 阴影设置
- 雾效类型
- 色调映射
- 剪切平面
- 蒙皮和形态属性
- 实例属性
双面材质处理 src/renderers/WebGLRenderer.js1331-1350:
对于没有 forceSinglePass 的透明双面材质,编译两个程序:
material.side = BackSide- 背面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 绘制调用。
绘制调用执行流
绘制范围计算
基础绘制范围 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 子系统:
- 遍历投射阴影的光源
- 从光源视角渲染场景
- 在阴影图纹理中存储深度
- 支持 VSM(方差阴影图)模式
详情见阴影映射。
背景渲染
场景背景在阴影后但在场景对象前渲染 src/renderers/WebGLRenderer.js1530-1538:
if (_renderBackground === true) {
background.render(currentRenderList, scene);
}背景类型 src/scenes/Scene.js31-40:
- 纯色
- 纹理(平面)
- 立方体纹理(天空盒)
- 等距矩形纹理
WebGLBackground 子系统处理背景特定渲染和环境贴图设置。
透射渲染
对于具有 transmission > 0 的材质(玻璃、液体),单独的透射渲染目标捕获透明对象后的场景:
透射流:
- 识别具有透射材质的对象
- 将不透明场景渲染到透射目标
- 绑定透射纹理
- 使用折射采样渲染透明对象
这为 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.js448 为 BufferGeometry 实例创建和管理 GPU 缓冲区:
缓冲区管理:
- 映射
BufferGeometry.uuid到 WebGL 缓冲区对象 - 处理交错缓冲区属性
- 生成线框索引缓冲区
- 通过
updateRange更新动态属性 - 计算边界体积
详细几何体系统文档见几何体系统。
WebGLAttributes
WebGLAttributes src/renderers/WebGLRenderer.js446 管理单个缓冲区属性:
属性操作:
- 从
BufferAttribute类型化数组创建 WebGL 缓冲区 - 使用
bufferData或bufferSubData上传数据 - 处理静态与动态使用提示
- 跟踪缓冲区版本以进行更新检测
- 支持
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 |
绑定序列:
- 绘制前调用
bindingStates.setup() - 从 object/program/geometry 计算缓存键
- 如果 VAO 存在则绑定;否则创建和配置新 VAO
- VAO 存储所有
gl.vertexAttribPointer()调用和gl.bindBuffer(ELEMENT_ARRAY_BUFFER)状态 - 具有相同组合的后续绘制复用缓存的 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:
- 检测上下文丢失事件
- 设置
_isContextLost = true标志 - 在恢复事件上,调用
initGLContext()重新创建子系统 - 恢复先前阴影图设置
- 恢复渲染
上下文恢复后必须重新创建所有 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(); // 手动重置