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 );

	}
相关推荐
lcreek7 小时前
Java安全:理解JNDI注入与Fastjson反序列化漏洞
java·安全
EnCi Zheng7 小时前
01-如何监听接口调用情况?
java·spring boot·后端
请为小H留灯8 小时前
IDEA / PyCharm 如何实现“一个项目一个窗口”?多项目并行开发设置
java·pycharm·intellij-idea·实战项目
苦逼的猿宝8 小时前
宠物咖啡馆平台的设计与实现(源码+论文)
java·毕业设计·springboot·计算机毕业设计
程序员buddha8 小时前
Spring Boot框架,类注入成 Bean的方式
java·spring boot·后端
城管不管8 小时前
什么是Prompt?
android·java·数据库·语言模型·llm·prompt
AI大模型8 小时前
被AI抢饭碗的Java程序员,后来都怎样了?
java·后端·ai编程
苦逼的猿宝8 小时前
医院管理系统.(源码+论文)
java·毕业设计·springboot·计算机毕业设计
東雪木8 小时前
JVM 与 Java 内存模型 专属复习笔记
java·jvm·笔记·java面试