WebGL|Three.js渲染管线核心技术解析

WebGL 渲染管线深度解析

基于 mrdoob/three.js 源码(src/renderers/WebGLRenderer.js 及其子系统)的完整技术博客


一、思维导图:WebGL 渲染管线全景

#mermaid-svg-tmP9FoKIH8XlXKH8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tmP9FoKIH8XlXKH8 .error-icon{fill:#552222;}#mermaid-svg-tmP9FoKIH8XlXKH8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tmP9FoKIH8XlXKH8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .marker.cross{stroke:#333333;}#mermaid-svg-tmP9FoKIH8XlXKH8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tmP9FoKIH8XlXKH8 p{margin:0;}#mermaid-svg-tmP9FoKIH8XlXKH8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster-label text{fill:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster-label span{color:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster-label span p{background-color:transparent;}#mermaid-svg-tmP9FoKIH8XlXKH8 .label text,#mermaid-svg-tmP9FoKIH8XlXKH8 span{fill:#333;color:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .node rect,#mermaid-svg-tmP9FoKIH8XlXKH8 .node circle,#mermaid-svg-tmP9FoKIH8XlXKH8 .node ellipse,#mermaid-svg-tmP9FoKIH8XlXKH8 .node polygon,#mermaid-svg-tmP9FoKIH8XlXKH8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .rough-node .label text,#mermaid-svg-tmP9FoKIH8XlXKH8 .node .label text,#mermaid-svg-tmP9FoKIH8XlXKH8 .image-shape .label,#mermaid-svg-tmP9FoKIH8XlXKH8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tmP9FoKIH8XlXKH8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .rough-node .label,#mermaid-svg-tmP9FoKIH8XlXKH8 .node .label,#mermaid-svg-tmP9FoKIH8XlXKH8 .image-shape .label,#mermaid-svg-tmP9FoKIH8XlXKH8 .icon-shape .label{text-align:center;}#mermaid-svg-tmP9FoKIH8XlXKH8 .node.clickable{cursor:pointer;}#mermaid-svg-tmP9FoKIH8XlXKH8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .arrowheadPath{fill:#333333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tmP9FoKIH8XlXKH8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tmP9FoKIH8XlXKH8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tmP9FoKIH8XlXKH8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster text{fill:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 .cluster span{color:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tmP9FoKIH8XlXKH8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tmP9FoKIH8XlXKH8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tmP9FoKIH8XlXKH8 .icon-shape,#mermaid-svg-tmP9FoKIH8XlXKH8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tmP9FoKIH8XlXKH8 .icon-shape p,#mermaid-svg-tmP9FoKIH8XlXKH8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tmP9FoKIH8XlXKH8 .icon-shape .label rect,#mermaid-svg-tmP9FoKIH8XlXKH8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tmP9FoKIH8XlXKH8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tmP9FoKIH8XlXKH8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tmP9FoKIH8XlXKH8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} WebGL 渲染管线

  1. CPU 阶段(场景准备)
  2. 渲染列表构建
  3. 阴影通道 Shadow Pass
  4. 主渲染通道 Main Pass
  5. GPU 固定管线阶段
  6. 资源管理子系统
    矩阵更新

updateMatrixWorld()
视锥体裁剪

Frustum Culling
场景图遍历

projectObject()
不透明队列 opaque

前→后排序 painterSortStable
透射队列 transmissive

后→前排序
透明队列 transparent

后→前排序 reversePainterSortStable
深度贴图生成

Depth Map
PCF / VSM 软阴影
背景渲染

WebGLBackground
光照设置

setupLights()
不透明物体绘制
透射通道

Transmission Pass
透明物体绘制
顶点着色器

Vertex Shader
图元装配

Primitive Assembly
光栅化

Rasterization
片元着色器

Fragment Shader
深度/模板测试

Depth & Stencil Test
混合

Blending
帧缓冲输出

Framebuffer
WebGLPrograms

着色器缓存
WebGLTextures

纹理管理
WebGLAttributes

VBO/EBO
WebGLState

GL状态缓存
WebGLShadowMap

阴影贴图


二、整体架构概览

WebGL 渲染管线可以分为两大部分:CPU 侧的 JavaScript 逻辑 (场景遍历、排序、状态设置)和 GPU 侧的硬件管线(顶点处理、光栅化、片元着色)。

Three.js 的 WebGLRenderer 是整个管线的总指挥,它协调超过 15 个子系统,将场景图数据转化为 GPU 绘制调用。 1

复制代码
用户代码
  │
  ▼
WebGLRenderer.render(scene, camera)
  │
  ├─► 矩阵更新 & 视锥体计算
  ├─► 场景图遍历 & 渲染列表构建
  ├─► 阴影通道(Shadow Pass)
  ├─► 光照设置
  ├─► 背景渲染
  ├─► 不透明物体渲染(前→后)
  ├─► 透射通道(Transmission Pass)
  └─► 透明物体渲染(后→前)

三、渲染管线各阶段详解

阶段 1:矩阵更新与视锥体裁剪

每帧渲染开始时,渲染器首先更新场景图中所有对象的世界矩阵,然后根据相机的投影矩阵构建视锥体(Frustum)。

js 复制代码
// src/renderers/WebGLRenderer.js
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();

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

视锥体(Frustum) 由 6 个平面组成(上/下/左/右/近/远),用于快速判断物体是否在相机可见范围内。不在视锥体内的物体直接跳过,不进入渲染列表,这是最重要的性能优化之一。 3


阶段 2:场景图遍历与渲染列表构建

projectObject() 函数递归遍历整个场景图,对每个可见对象执行视锥体裁剪,并将通过裁剪的对象加入渲染列表。

js 复制代码
function projectObject( object, camera, groupOrder, sortObjects ) {
    if ( object.visible === false ) return;
    const visible = object.layers.test( camera.layers );
    if ( visible ) {
        if ( object.isLight ) {
            currentRenderState.pushLight( object );
            if ( object.castShadow ) currentRenderState.pushShadow( object );
        } else if ( object.isMesh || object.isLine || object.isPoints ) {
            if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
                // 加入渲染列表
                currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null );
            }
        }
    }
    // 递归处理子节点
    const children = object.children;
    for ( let i = 0, l = children.length; i < l; i ++ ) {
        projectObject( children[ i ], camera, groupOrder, sortObjects );
    }
}

渲染列表分为三个队列:

队列 内容 排序策略 原因
opaque 不透明物体 前→后painterSortStable 利用 Early-Z 拒绝,减少过绘制
transmissive 透射物体(玻璃等) 后→前 需要正确的折射采样
transparent 透明物体 后→前reversePainterSortStable 保证 Alpha 混合正确性

阶段 3:阴影通道(Shadow Pass)

在主渲染之前,WebGLShadowMap 为每个投射阴影的光源渲染一张深度贴图(Shadow Map)。

复制代码
对每个投影光源:
  1. 切换渲染目标到深度贴图 FBO
  2. 从光源视角渲染场景(只写深度)
  3. 恢复主渲染目标

Three.js 支持三种阴影类型:

  • BasicShadowMap:硬阴影,性能最好
  • PCFShadowMap:百分比近似过滤,软阴影
  • VSMShadowMap:方差阴影贴图,更柔和的软阴影

阶段 4:光照设置

currentRenderState.setupLights() 将场景中收集到的光源数据(方向、颜色、强度、阴影贴图等)打包成 Uniform 数据,准备传入着色器。 7


阶段 5:主渲染通道

主渲染通道按顺序执行:

复制代码
background.render(scene)          ← 渲染背景(纯色/天空盒/环境贴图)
    ↓
renderObjects(opaque, ...)        ← 不透明物体(前→后)
    ↓
renderTransmissionPass(...)       ← 透射通道(将不透明场景渲染到临时RT供折射采样)
    ↓
renderObjects(transparent, ...)   ← 透明物体(后→前)

每个物体的绘制最终调用 renderBufferDirect(),它负责:

  1. 选择/编译 GLSL 着色器程序(setProgram()
  2. 设置 GL 状态(state.setMaterial()
  3. 绑定 VAO(bindingStates.setup()
  4. 发出绘制调用(gl.drawArrays()gl.drawElements()9

四、GPU 固定管线阶段

#mermaid-svg-F0bVmdnSWagvV1lc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F0bVmdnSWagvV1lc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F0bVmdnSWagvV1lc .error-icon{fill:#552222;}#mermaid-svg-F0bVmdnSWagvV1lc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F0bVmdnSWagvV1lc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F0bVmdnSWagvV1lc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F0bVmdnSWagvV1lc .marker.cross{stroke:#333333;}#mermaid-svg-F0bVmdnSWagvV1lc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F0bVmdnSWagvV1lc p{margin:0;}#mermaid-svg-F0bVmdnSWagvV1lc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-F0bVmdnSWagvV1lc .cluster-label text{fill:#333;}#mermaid-svg-F0bVmdnSWagvV1lc .cluster-label span{color:#333;}#mermaid-svg-F0bVmdnSWagvV1lc .cluster-label span p{background-color:transparent;}#mermaid-svg-F0bVmdnSWagvV1lc .label text,#mermaid-svg-F0bVmdnSWagvV1lc span{fill:#333;color:#333;}#mermaid-svg-F0bVmdnSWagvV1lc .node rect,#mermaid-svg-F0bVmdnSWagvV1lc .node circle,#mermaid-svg-F0bVmdnSWagvV1lc .node ellipse,#mermaid-svg-F0bVmdnSWagvV1lc .node polygon,#mermaid-svg-F0bVmdnSWagvV1lc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F0bVmdnSWagvV1lc .rough-node .label text,#mermaid-svg-F0bVmdnSWagvV1lc .node .label text,#mermaid-svg-F0bVmdnSWagvV1lc .image-shape .label,#mermaid-svg-F0bVmdnSWagvV1lc .icon-shape .label{text-anchor:middle;}#mermaid-svg-F0bVmdnSWagvV1lc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-F0bVmdnSWagvV1lc .rough-node .label,#mermaid-svg-F0bVmdnSWagvV1lc .node .label,#mermaid-svg-F0bVmdnSWagvV1lc .image-shape .label,#mermaid-svg-F0bVmdnSWagvV1lc .icon-shape .label{text-align:center;}#mermaid-svg-F0bVmdnSWagvV1lc .node.clickable{cursor:pointer;}#mermaid-svg-F0bVmdnSWagvV1lc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-F0bVmdnSWagvV1lc .arrowheadPath{fill:#333333;}#mermaid-svg-F0bVmdnSWagvV1lc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F0bVmdnSWagvV1lc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F0bVmdnSWagvV1lc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F0bVmdnSWagvV1lc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-F0bVmdnSWagvV1lc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F0bVmdnSWagvV1lc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-F0bVmdnSWagvV1lc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F0bVmdnSWagvV1lc .cluster text{fill:#333;}#mermaid-svg-F0bVmdnSWagvV1lc .cluster span{color:#333;}#mermaid-svg-F0bVmdnSWagvV1lc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F0bVmdnSWagvV1lc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-F0bVmdnSWagvV1lc rect.text{fill:none;stroke-width:0;}#mermaid-svg-F0bVmdnSWagvV1lc .icon-shape,#mermaid-svg-F0bVmdnSWagvV1lc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F0bVmdnSWagvV1lc .icon-shape p,#mermaid-svg-F0bVmdnSWagvV1lc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-F0bVmdnSWagvV1lc .icon-shape .label rect,#mermaid-svg-F0bVmdnSWagvV1lc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F0bVmdnSWagvV1lc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-F0bVmdnSWagvV1lc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-F0bVmdnSWagvV1lc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 顶点数据

VBO/EBO
顶点着色器

Vertex Shader

GLSL
图元装配

Primitive Assembly

三角形/线/点
背面剔除

Face Culling

gl.CULL_FACE
光栅化

Rasterization

插值生成片元
片元着色器

Fragment Shader

GLSL
深度测试

Depth Test

gl.DEPTH_TEST
模板测试

Stencil Test
混合

Blending

gl.BLEND
帧缓冲

Framebuffer

最终像素

4.1 顶点着色器(Vertex Shader)

顶点着色器运行在 GPU 上,对每个顶点执行变换。Three.js 内置着色器的核心变换:

glsl 复制代码
// 典型的 Three.js 顶点着色器核心
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  • modelMatrix:模型空间 → 世界空间
  • viewMatrixmodelViewMatrix):世界空间 → 相机空间
  • projectionMatrix:相机空间 → 裁剪空间(NDC) 10

Three.js 使用 #include 机制将着色器代码模块化,例如 #include <project_vertex> 会被替换为实际的投影变换代码。 11

4.2 着色器编译与缓存(WebGLPrograms)

WebGLPrograms 是着色器的编译器和缓存系统。它根据材质参数(是否有法线贴图、是否使用蒙皮、光源数量等)生成唯一的缓存键 ,避免重复编译相同的着色器。

js 复制代码
// 编译流程
const vertexGlsl = versionString + prefixVertex + vertexShader;
const fragmentGlsl = versionString + prefixFragment + fragmentShader;

const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

gl.attachShader( program, glVertexShader );
gl.attachShader( program, glFragmentShader );
gl.linkProgram( program );

4.3 片元着色器(Fragment Shader)

片元着色器决定每个像素的最终颜色,包含光照计算、纹理采样、色调映射等:

glsl 复制代码
// 典型片元着色器输出链
diffuseColor → 光照计算 → 环境光遮蔽 → 自发光 → 色调映射 → 色彩空间转换 → gl_FragColor

4.4 深度测试、模板测试与混合(WebGLState)

WebGLState GL 状态的缓存层,避免重复调用昂贵的 GL 状态切换 API:

js 复制代码
function setMaterial( material, frontFaceCW ) {
    // 背面剔除
    material.side === DoubleSide ? disable( gl.CULL_FACE ) : enable( gl.CULL_FACE );
    // 混合模式
    ( material.blending === NormalBlending && material.transparent === false )
        ? setBlending( NoBlending )
        : setBlending( material.blending, ... );
    // 深度测试
    depthBuffer.setFunc( material.depthFunc );
    depthBuffer.setTest( material.depthTest );
    depthBuffer.setMask( material.depthWrite );
    // 模板测试
    stencilBuffer.setTest( material.stencilWrite );
}

深度测试函数depthFunc)支持:NEVERALWAYSLESSLEQUALEQUALGEQUALGREATERNOTEQUAL15

混合模式blending)支持:NoBlendingNormalBlending(标准 Alpha 混合)、AdditiveBlending(加法混合,用于粒子/光效)、SubtractiveBlendingMultiplyBlendingCustomBlending16


五、资源管理子系统

#mermaid-svg-U6sEPUp3dagqvO8Z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U6sEPUp3dagqvO8Z .error-icon{fill:#552222;}#mermaid-svg-U6sEPUp3dagqvO8Z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U6sEPUp3dagqvO8Z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U6sEPUp3dagqvO8Z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U6sEPUp3dagqvO8Z .marker.cross{stroke:#333333;}#mermaid-svg-U6sEPUp3dagqvO8Z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U6sEPUp3dagqvO8Z p{margin:0;}#mermaid-svg-U6sEPUp3dagqvO8Z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster-label text{fill:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster-label span{color:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster-label span p{background-color:transparent;}#mermaid-svg-U6sEPUp3dagqvO8Z .label text,#mermaid-svg-U6sEPUp3dagqvO8Z span{fill:#333;color:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z .node rect,#mermaid-svg-U6sEPUp3dagqvO8Z .node circle,#mermaid-svg-U6sEPUp3dagqvO8Z .node ellipse,#mermaid-svg-U6sEPUp3dagqvO8Z .node polygon,#mermaid-svg-U6sEPUp3dagqvO8Z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U6sEPUp3dagqvO8Z .rough-node .label text,#mermaid-svg-U6sEPUp3dagqvO8Z .node .label text,#mermaid-svg-U6sEPUp3dagqvO8Z .image-shape .label,#mermaid-svg-U6sEPUp3dagqvO8Z .icon-shape .label{text-anchor:middle;}#mermaid-svg-U6sEPUp3dagqvO8Z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U6sEPUp3dagqvO8Z .rough-node .label,#mermaid-svg-U6sEPUp3dagqvO8Z .node .label,#mermaid-svg-U6sEPUp3dagqvO8Z .image-shape .label,#mermaid-svg-U6sEPUp3dagqvO8Z .icon-shape .label{text-align:center;}#mermaid-svg-U6sEPUp3dagqvO8Z .node.clickable{cursor:pointer;}#mermaid-svg-U6sEPUp3dagqvO8Z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U6sEPUp3dagqvO8Z .arrowheadPath{fill:#333333;}#mermaid-svg-U6sEPUp3dagqvO8Z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U6sEPUp3dagqvO8Z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U6sEPUp3dagqvO8Z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U6sEPUp3dagqvO8Z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U6sEPUp3dagqvO8Z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U6sEPUp3dagqvO8Z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster text{fill:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z .cluster span{color:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U6sEPUp3dagqvO8Z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U6sEPUp3dagqvO8Z rect.text{fill:none;stroke-width:0;}#mermaid-svg-U6sEPUp3dagqvO8Z .icon-shape,#mermaid-svg-U6sEPUp3dagqvO8Z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U6sEPUp3dagqvO8Z .icon-shape p,#mermaid-svg-U6sEPUp3dagqvO8Z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U6sEPUp3dagqvO8Z .icon-shape .label rect,#mermaid-svg-U6sEPUp3dagqvO8Z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U6sEPUp3dagqvO8Z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U6sEPUp3dagqvO8Z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U6sEPUp3dagqvO8Z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 着色器管线
Material

(JS)
WebGLPrograms

参数哈希/缓存
WebGLProgram

GLSL编译链接
WebGLProgram

(GPU)
纹理管线
Texture

(JS)
WebGLTextures

格式转换/Mipmap
gl.texImage2D()

gl.texStorage2D()
GPU纹理单元
几何体管线
BufferGeometry

(JS)
WebGLGeometries

生命周期管理
WebGLAttributes

gl.bufferData()
VBO/EBO

(GPU显存)

WebGLAttributes:几何数据上传

WebGLAttributes 负责将 BufferAttribute 数据上传到 GPU,使用 gl.bufferData()gl.bufferSubData() 进行全量/增量更新。 17

WebGLTextures:纹理管理

WebGLTextures 处理纹理上传的复杂逻辑:

  • 超出硬件限制时自动缩放图像
  • 根据纹理格式、类型、色彩空间解析正确的 WebGL 内部格式
  • 自动生成 Mipmap 18

六、渲染调用链(Call Graph)

#mermaid-svg-fMmdHVesIiuYxpws{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fMmdHVesIiuYxpws .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fMmdHVesIiuYxpws .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fMmdHVesIiuYxpws .error-icon{fill:#552222;}#mermaid-svg-fMmdHVesIiuYxpws .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fMmdHVesIiuYxpws .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fMmdHVesIiuYxpws .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fMmdHVesIiuYxpws .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fMmdHVesIiuYxpws .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fMmdHVesIiuYxpws .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fMmdHVesIiuYxpws .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fMmdHVesIiuYxpws .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fMmdHVesIiuYxpws .marker.cross{stroke:#333333;}#mermaid-svg-fMmdHVesIiuYxpws svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fMmdHVesIiuYxpws p{margin:0;}#mermaid-svg-fMmdHVesIiuYxpws .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fMmdHVesIiuYxpws .cluster-label text{fill:#333;}#mermaid-svg-fMmdHVesIiuYxpws .cluster-label span{color:#333;}#mermaid-svg-fMmdHVesIiuYxpws .cluster-label span p{background-color:transparent;}#mermaid-svg-fMmdHVesIiuYxpws .label text,#mermaid-svg-fMmdHVesIiuYxpws span{fill:#333;color:#333;}#mermaid-svg-fMmdHVesIiuYxpws .node rect,#mermaid-svg-fMmdHVesIiuYxpws .node circle,#mermaid-svg-fMmdHVesIiuYxpws .node ellipse,#mermaid-svg-fMmdHVesIiuYxpws .node polygon,#mermaid-svg-fMmdHVesIiuYxpws .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fMmdHVesIiuYxpws .rough-node .label text,#mermaid-svg-fMmdHVesIiuYxpws .node .label text,#mermaid-svg-fMmdHVesIiuYxpws .image-shape .label,#mermaid-svg-fMmdHVesIiuYxpws .icon-shape .label{text-anchor:middle;}#mermaid-svg-fMmdHVesIiuYxpws .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fMmdHVesIiuYxpws .rough-node .label,#mermaid-svg-fMmdHVesIiuYxpws .node .label,#mermaid-svg-fMmdHVesIiuYxpws .image-shape .label,#mermaid-svg-fMmdHVesIiuYxpws .icon-shape .label{text-align:center;}#mermaid-svg-fMmdHVesIiuYxpws .node.clickable{cursor:pointer;}#mermaid-svg-fMmdHVesIiuYxpws .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fMmdHVesIiuYxpws .arrowheadPath{fill:#333333;}#mermaid-svg-fMmdHVesIiuYxpws .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fMmdHVesIiuYxpws .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fMmdHVesIiuYxpws .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fMmdHVesIiuYxpws .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fMmdHVesIiuYxpws .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fMmdHVesIiuYxpws .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fMmdHVesIiuYxpws .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fMmdHVesIiuYxpws .cluster text{fill:#333;}#mermaid-svg-fMmdHVesIiuYxpws .cluster span{color:#333;}#mermaid-svg-fMmdHVesIiuYxpws div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fMmdHVesIiuYxpws .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fMmdHVesIiuYxpws rect.text{fill:none;stroke-width:0;}#mermaid-svg-fMmdHVesIiuYxpws .icon-shape,#mermaid-svg-fMmdHVesIiuYxpws .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fMmdHVesIiuYxpws .icon-shape p,#mermaid-svg-fMmdHVesIiuYxpws .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fMmdHVesIiuYxpws .icon-shape .label rect,#mermaid-svg-fMmdHVesIiuYxpws .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fMmdHVesIiuYxpws .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fMmdHVesIiuYxpws .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fMmdHVesIiuYxpws :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} render(scene, camera)
renderScene(renderList, scene, camera)
renderObjects(renderList, scene, camera)
renderObject(object, scene, camera, ...)
renderBufferDirect(camera, scene, geometry, material, object, group)
setProgram() → 选择/编译着色器
state.setMaterial() → 设置GL状态
bindingStates.setup() → 绑定VAO
bufferRenderer.render() → gl.drawElements()


七、性能优化关键点总结

优化技术 实现位置 原理
视锥体裁剪 projectObject() + Frustum 跳过相机外的物体,减少 Draw Call
不透明前→后排序 painterSortStable 利用 Early-Z 拒绝,减少过绘制
着色器缓存 WebGLPrograms 相同参数的材质复用已编译的 GLSL 程序
GL 状态缓存 WebGLState 只在状态真正改变时调用 GL API
VAO 绑定状态缓存 WebGLBindingStates 减少顶点属性绑定的重复设置
增量纹理上传 WebGLTextures + bufferSubData 只上传变化的纹理区域
阴影贴图复用 WebGLShadowMap 静态场景可关闭 autoUpdate

八、完整渲染帧时序图


九、关键源码文件索引

文件 职责
src/renderers/WebGLRenderer.js 渲染器主入口,协调所有子系统
src/renderers/webgl/WebGLState.js GL 状态缓存(深度/模板/混合/剔除)
src/renderers/webgl/WebGLPrograms.js 着色器参数生成与缓存
src/renderers/webgl/WebGLProgram.js GLSL 编译、链接、Uniform 绑定
src/renderers/webgl/WebGLRenderLists.js 渲染列表(opaque/transmissive/transparent)
src/renderers/webgl/WebGLShadowMap.js 阴影贴图生成
src/renderers/webgl/WebGLTextures.js 纹理上传与格式管理
src/renderers/webgl/WebGLAttributes.js VBO/EBO 数据上传
src/renderers/webgl/WebGLBindingStates.js VAO 绑定状态管理
src/math/Frustum.js 视锥体裁剪数学

Citations

File: src/renderers/WebGLRenderer.js (L26-54)

javascript 复制代码
import { WebGLAnimation } from './webgl/WebGLAnimation.js';
import { WebGLAttributes } from './webgl/WebGLAttributes.js';
import { WebGLBackground } from './webgl/WebGLBackground.js';
import { WebGLBindingStates } from './webgl/WebGLBindingStates.js';
import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js';
import { WebGLCapabilities } from './webgl/WebGLCapabilities.js';
import { WebGLClipping } from './webgl/WebGLClipping.js';
import { WebGLEnvironments } from './webgl/WebGLEnvironments.js';
import { WebGLExtensions } from './webgl/WebGLExtensions.js';
import { WebGLGeometries } from './webgl/WebGLGeometries.js';
import { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer.js';
import { WebGLInfo } from './webgl/WebGLInfo.js';
import { WebGLMorphtargets } from './webgl/WebGLMorphtargets.js';
import { WebGLObjects } from './webgl/WebGLObjects.js';
import { WebGLOutput } from './webgl/WebGLOutput.js';
import { WebGLPrograms } from './webgl/WebGLPrograms.js';
import { WebGLProperties } from './webgl/WebGLProperties.js';
import { WebGLRenderLists } from './webgl/WebGLRenderLists.js';
import { WebGLRenderStates } from './webgl/WebGLRenderStates.js';
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
import { WebGLShadowMap } from './webgl/WebGLShadowMap.js';
import { WebGLState } from './webgl/WebGLState.js';
import { WebGLTextures } from './webgl/WebGLTextures.js';
import { WebGLUniforms } from './webgl/WebGLUniforms.js';
import { WebGLUtils } from './webgl/WebGLUtils.js';
import { WebXRManager } from './webxr/WebXRManager.js';
import { WebGLMaterials } from './webgl/WebGLMaterials.js';
import { WebGLUniformsGroups } from './webgl/WebGLUniformsGroups.js';
import { createCanvasElement, probeAsync, error, warn, log } from '../utils.js';

File: src/renderers/WebGLRenderer.js (L63-63)

javascript 复制代码
class WebGLRenderer {

File: src/renderers/WebGLRenderer.js (L451-452)

javascript 复制代码
			textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
			environments = new WebGLEnvironments( _this );

File: src/renderers/WebGLRenderer.js (L453-456)

javascript 复制代码
			attributes = new WebGLAttributes( _gl );
			bindingStates = new WebGLBindingStates( _gl, attributes );
			geometries = new WebGLGeometries( _gl, attributes, info, bindingStates );
			objects = new WebGLObjects( _gl, geometries, attributes, bindingStates, info );

File: src/renderers/WebGLRenderer.js (L1184-1242)

javascript 复制代码
		this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {

			if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)

			const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );

			const program = setProgram( camera, scene, geometry, material, object );

			state.setMaterial( material, frontFaceCW );

			//

			let index = geometry.index;
			let rangeFactor = 1;

			if ( material.wireframe === true ) {

				index = geometries.getWireframeAttribute( geometry );

				if ( index === undefined ) return;

				rangeFactor = 2;

			}

			//

			const drawRange = geometry.drawRange;
			const position = geometry.attributes.position;

			let drawStart = drawRange.start * rangeFactor;
			let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor;

			if ( group !== null ) {

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

			}

			if ( index !== null ) {

				drawStart = Math.max( drawStart, 0 );
				drawEnd = Math.min( drawEnd, index.count );

			} else if ( position !== undefined && position !== null ) {

				drawStart = Math.max( drawStart, 0 );
				drawEnd = Math.min( drawEnd, position.count );

			}

			const drawCount = drawEnd - drawStart;

			if ( drawCount < 0 || drawCount === Infinity ) return;

			//

			bindingStates.setup( object, material, program, geometry, index );

File: src/renderers/WebGLRenderer.js (L1635-1659)

javascript 复制代码
			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();

			// update camera matrices and frustum

			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();

			if ( xr.enabled === true && xr.isPresenting === true && ( output === null || output.isCompositing() === false ) ) {

				if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera );

				camera = xr.getCamera(); // use XR camera for rendering

			}

			//
			if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget );

			currentRenderState = renderStates.get( scene, renderStateStack.length );
			currentRenderState.init( camera );

			currentRenderState.state.textureUnits = textures.getTextureUnits();
			renderStateStack.push( currentRenderState );

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

File: src/renderers/WebGLRenderer.js (L1702-1708)

javascript 复制代码
			if ( _clippingEnabled === true ) clipping.beginShadows();

			const shadowsArray = currentRenderState.state.shadowsArray;

			shadowMap.render( shadowsArray, scene, camera );

			if ( _clippingEnabled === true ) clipping.endShadows();

File: src/renderers/WebGLRenderer.js (L1718-1757)

javascript 复制代码
			if ( skipSceneRender === false ) {

				const opaqueObjects = currentRenderList.opaque;
				const transmissiveObjects = currentRenderList.transmissive;

				currentRenderState.setupLights();

				if ( camera.isArrayCamera ) {

					const cameras = camera.cameras;

					if ( transmissiveObjects.length > 0 ) {

						for ( let i = 0, l = cameras.length; i < l; i ++ ) {

							const camera2 = cameras[ i ];

							renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera2 );

						}

					}

					if ( _renderBackground ) background.render( scene );

					for ( let i = 0, l = cameras.length; i < l; i ++ ) {

						const camera2 = cameras[ i ];

						renderScene( currentRenderList, scene, camera2, camera2.viewport );

					}

				} else {

					if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera );

					if ( _renderBackground ) background.render( scene );

					renderScene( currentRenderList, scene, camera );

File: src/renderers/WebGLRenderer.js (L1831-1885)

javascript 复制代码
		function projectObject( object, camera, groupOrder, sortObjects ) {

			if ( object.visible === false ) return;

			const visible = object.layers.test( camera.layers );

			if ( visible ) {

				if ( object.isGroup ) {

					groupOrder = object.renderOrder;

				} else if ( object.isLOD ) {

					if ( object.autoUpdate === true ) object.update( camera );

				} else if ( object.isLightProbeGrid ) {

					currentRenderState.pushLightProbeGrid( object );

				} else if ( object.isLight ) {

					currentRenderState.pushLight( object );

					if ( object.castShadow ) {

						currentRenderState.pushShadow( object );

					}

				} else if ( object.isSprite ) {

					if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {

						if ( sortObjects ) {

							_vector4.setFromMatrixPosition( object.matrixWorld )
								.applyMatrix4( _projScreenMatrix );

						}

						const geometry = objects.update( object );
						const material = object.material;

						if ( material.visible ) {

							currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null );

						}

					}

				} else if ( object.isMesh || object.isLine || object.isPoints ) {

					if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {

File: src/math/Frustum.js (L18-37)

javascript 复制代码
class Frustum {

	/**
	 * Constructs a new frustum.
	 *
	 * @param {Plane} [p0] - The first plane that encloses the frustum.
	 * @param {Plane} [p1] - The second plane that encloses the frustum.
	 * @param {Plane} [p2] - The third plane that encloses the frustum.
	 * @param {Plane} [p3] - The fourth plane that encloses the frustum.
	 * @param {Plane} [p4] - The fifth plane that encloses the frustum.
	 * @param {Plane} [p5] - The sixth plane that encloses the frustum.
	 */
	constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) {

		/**
		 * This array holds the planes that enclose the frustum.
		 *
		 * @type {Array<Plane>}
		 */
		this.planes = [ p0, p1, p2, p3, p4, p5 ];

File: src/renderers/webgl/WebGLRenderLists.js (L162-168)

javascript 复制代码
	function sort( customOpaqueSort, customTransparentSort ) {

		if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );
		if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable );
		if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );

	}

File: src/renderers/shaders/ShaderLib/meshnormal.glsl.js (L20-47)

javascript 复制代码
void main() {

	#include <uv_vertex>
	#include <batching_vertex>

	#include <beginnormal_vertex>
	#include <morphinstance_vertex>
	#include <morphnormal_vertex>
	#include <skinbase_vertex>
	#include <skinnormal_vertex>
	#include <defaultnormal_vertex>
	#include <normal_vertex>

	#include <begin_vertex>
	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <displacementmap_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>

#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )

	vViewPosition = - mvPosition.xyz;

#endif

}

File: src/renderers/webgl/WebGLProgram.js (L790-799)

javascript 复制代码
	vertexShader = resolveIncludes( vertexShader );
	vertexShader = replaceLightNums( vertexShader, parameters );
	vertexShader = replaceClippingPlaneNums( vertexShader, parameters );

	fragmentShader = resolveIncludes( fragmentShader );
	fragmentShader = replaceLightNums( fragmentShader, parameters );
	fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );

	vertexShader = unrollLoops( vertexShader );
	fragmentShader = unrollLoops( fragmentShader );

File: src/renderers/webgl/WebGLProgram.js (L832-857)

javascript 复制代码
	const vertexGlsl = versionString + prefixVertex + vertexShader;
	const fragmentGlsl = versionString + prefixFragment + fragmentShader;

	// log( '*VERTEX*', vertexGlsl );
	// log( '*FRAGMENT*', fragmentGlsl );

	const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
	const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

	gl.attachShader( program, glVertexShader );
	gl.attachShader( program, glFragmentShader );

	// Force a particular attribute to index 0.

	if ( parameters.index0AttributeName !== undefined ) {

		gl.bindAttribLocation( program, 0, parameters.index0AttributeName );

	} else if ( parameters.morphTargets === true ) {

		// programs with morphTargets displace position out of attribute 0
		gl.bindAttribLocation( program, 0, 'position' );

	}

	gl.linkProgram( program );

File: src/renderers/webgl/WebGLOutput.js (L74-106)

javascript 复制代码
		fragmentShader: /* glsl */`
			precision highp float;

			uniform sampler2D tDiffuse;

			varying vec2 vUv;

			#include <tonemapping_pars_fragment>
			#include <colorspace_pars_fragment>

			void main() {
				gl_FragColor = texture2D( tDiffuse, vUv );

				#ifdef LINEAR_TONE_MAPPING
					gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb );
				#elif defined( REINHARD_TONE_MAPPING )
					gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb );
				#elif defined( CINEON_TONE_MAPPING )
					gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb );
				#elif defined( ACES_FILMIC_TONE_MAPPING )
					gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb );
				#elif defined( AGX_TONE_MAPPING )
					gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb );
				#elif defined( NEUTRAL_TONE_MAPPING )
					gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb );
				#elif defined( CUSTOM_TONE_MAPPING )
					gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb );
				#endif

				#ifdef SRGB_TRANSFER
					gl_FragColor = sRGBTransferOETF( gl_FragColor );
				#endif
			}`,

File: src/renderers/webgl/WebGLState.js (L135-191)

javascript 复制代码
			setFunc: function ( depthFunc ) {

				if ( currentReversed ) depthFunc = ReversedDepthFuncs[ depthFunc ];

				if ( currentDepthFunc !== depthFunc ) {

					switch ( depthFunc ) {

						case NeverDepth:

							gl.depthFunc( gl.NEVER );
							break;

						case AlwaysDepth:

							gl.depthFunc( gl.ALWAYS );
							break;

						case LessDepth:

							gl.depthFunc( gl.LESS );
							break;

						case LessEqualDepth:

							gl.depthFunc( gl.LEQUAL );
							break;

						case EqualDepth:

							gl.depthFunc( gl.EQUAL );
							break;

						case GreaterEqualDepth:

							gl.depthFunc( gl.GEQUAL );
							break;

						case GreaterDepth:

							gl.depthFunc( gl.GREATER );
							break;

						case NotEqualDepth:

							gl.depthFunc( gl.NOTEQUAL );
							break;

						default:

							gl.depthFunc( gl.LEQUAL );

					}

					currentDepthFunc = depthFunc;

				}

File: src/renderers/webgl/WebGLState.js (L609-627)

javascript 复制代码
	function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) {

		if ( blending === NoBlending ) {

			if ( currentBlendingEnabled === true ) {

				disable( gl.BLEND );
				currentBlendingEnabled = false;

			}

			return;

		}

		if ( currentBlendingEnabled === false ) {

			enable( gl.BLEND );
			currentBlendingEnabled = true;

File: src/renderers/webgl/WebGLState.js (L754-790)

javascript 复制代码
	function setMaterial( material, frontFaceCW ) {

		material.side === DoubleSide
			? disable( gl.CULL_FACE )
			: enable( gl.CULL_FACE );

		let flipSided = ( material.side === BackSide );
		if ( frontFaceCW ) flipSided = ! flipSided;

		setFlipSided( flipSided );

		( material.blending === NormalBlending && material.transparent === false )
			? setBlending( NoBlending )
			: setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );

		depthBuffer.setFunc( material.depthFunc );
		depthBuffer.setTest( material.depthTest );
		depthBuffer.setMask( material.depthWrite );
		colorBuffer.setMask( material.colorWrite );

		const stencilWrite = material.stencilWrite;
		stencilBuffer.setTest( stencilWrite );
		if ( stencilWrite ) {

			stencilBuffer.setMask( material.stencilWriteMask );
			stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
			stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );

		}

		setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );

		material.alphaToCoverage === true
			? enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
			: disable( gl.SAMPLE_ALPHA_TO_COVERAGE );

	}
相关推荐
m0_5873830016 分钟前
智慧场馆解决方案实战指南:从系统架构到落地部署全解析
java·spring boot·架构·系统架构
ZC跨境爬虫21 分钟前
LeetCode 13. 罗马数字转整数(多解法详解 + Java Python 实现)
java·python·leetcode
省长26 分钟前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·后端·开源
LayZhangStrive1 小时前
融360 一面
java·面试·后端开发
二级小助手1 小时前
二级Web前端选择题高频真题20道与考场避坑笔记
javascript·css3·html5·web前端·计算机二级·二级web·web真题
城管不管1 小时前
重生——第十次面试之开源中国一面挂
java·linux·开发语言·算法·面试·职场和发展·开源
Java小白笔记1 小时前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程
爱敲键盘的猴子1 小时前
深入理解 Java HashMap
java·hashmap
霸道流氓气质1 小时前
Java开发者AI编程常用MCP、Skills、Rules全景汇总
java·开发语言·ai编程
前端小卡拉2 小时前
用了大半年 AI 编程工具,我才搞懂 Skill 到底是什么
前端·javascript