19-mini版WebGLRenderer

源码:github.com/buglas/thre...

1-WebGLRendererMini 的代码

src\renderers\WebGLRendererMini.js

ini 复制代码
import { Vector4 } from '../math/Vector4.js';
import { WebGLAttributesMini } from './webgl/WebGLAttributesMini.js';
import { WebGLBackgroundMini } from './webgl/WebGLBackgroundMini.js';
import { WebGLBindingStatesMini } from './webgl/WebGLBindingStatesMini.js';
import { WebGLBufferRendererMini } from './webgl/WebGLBufferRendererMini.js';
import { WebGLGeometriesMini } from './webgl/WebGLGeometriesMini.js';
import { WebGLObjectsMini } from './webgl/WebGLObjectsMini.js';
import { WebGLProgramsMini } from './webgl/WebGLProgramsMini.js';
import { WebGLProperties } from './webgl/WebGLProperties.js';
import { WebGLRenderListMini } from './webgl/WebGLRenderListsMini.js';
import { WebGLStateMini } from './webgl/WebGLStateMini.js';
import { WebGLUniformsMini } from './webgl/WebGLUniformsMini.js';
import { WebGLMaterialsMini } from './webgl/WebGLMaterialsMini.js';
import { createCanvasElement } from '../utils.js';
​
/**
 * WebGL 渲染器精简版:保留「场景遍历 → 渲染列表 → 着色器 → VAO → drawArrays」主链路。
 * 仅支持 WebGL2、不透明物体、MeshMiniMaterial;无透明排序、阴影、多 Pass 等。
 */
class WebGLRendererMini {
    constructor( parameters = {} ) {
        const {
            canvas = createCanvasElement(),
        } = parameters;
​
        /** 当前帧的渲染项列表,仅在 render() 执行期间有效 */
        let currentRenderList = null;
​
        this.domElement = canvas;
        const _this = this;
        let _width = canvas.width;
        let _height = canvas.height;
        let _pixelRatio = 1;
        const _viewport = new Vector4( 0, 0, _width, _height );
        let _gl = canvas.getContext( 'webgl2', );
​
        // 各子系统实例,在 initGLContext 中初始化
        let state;
        let properties;
        let attributes, geometries, objects;
        let programCache, materials;
        let background, bufferRenderer;
        let bindingStates;
​
        /** 组装 WebGL 子系统,与完整版 WebGLRenderer 的 initGLContext 结构对应 */
        function initGLContext() {
            // 初始化 GL 状态
            state = new WebGLStateMini( _gl );
            // 初始化材质/对象运行时缓存
            properties = new WebGLProperties();
            // 初始化 BufferGeometry → GPU buffer 管线
            attributes = new WebGLAttributesMini( _gl );
            // 初始化 VAO 与顶点属性绑定
            bindingStates = new WebGLBindingStatesMini( _gl, attributes );
            // 初始化几何体管理,管理几何体缓存
            geometries = new WebGLGeometriesMini( _gl, attributes, bindingStates );
            // 初始化对象管理,管理对象缓存
            objects = new WebGLObjectsMini( geometries );
            // 初始化 shader 编译缓存
            programCache = new WebGLProgramsMini( _this, bindingStates );
            // 初始化材质 uniform 同步
            materials = new WebGLMaterialsMini();
            // 初始化清屏
            background = new WebGLBackgroundMini( _this, state );
            // 初始化底层 draw call
            bufferRenderer = new WebGLBufferRendererMini( _gl );
        }
​
        initGLContext();
​
        this.getContext = function () {
            return _gl;
        };
​
        // 设置画布大小
        this.setSize = function ( width, height ) {
            canvas.width = Math.floor( width * _pixelRatio );
            canvas.height = Math.floor( height * _pixelRatio );
            canvas.style.width = width + 'px';
            canvas.style.height = height + 'px';
            this.setViewport( 0, 0, width, height );
        };
​
        // 设置视口
        this.setViewport = function ( x, y, width, height ) {
            _viewport.set( x, y, width, height );
            state.viewport( _viewport );
        };
​
        // 清屏
        this.clear = function () {
            _gl.clear( _gl.COLOR_BUFFER_BIT );
        };
​
        /**
         * 单个几何体的直接绘制入口:选 program → 绑 VAO → gl.drawArrays。
         * 完整版 renderBufferDirect 还处理 Instancing、多 group、线框等,此处仅三角形。
         */
        this.renderBufferDirect = function ( camera, scene, geometry, material, object ) {
            const program = setProgram( camera, scene, geometry, material, object );
            const position = geometry.attributes.position;
            // 设置 VAO 与顶点属性绑定
            bindingStates.setup( program, geometry ); 
            // 设置绘制模式
            bufferRenderer.setMode( _gl.TRIANGLES );
            // 绘制几何体
            bufferRenderer.render( 0, position.count );
        };
​
        /** 主渲染入口:遍历场景 → 清屏 → 按 opaque 列表逐对象绘制 */
        this.render = function ( scene, camera ) {
            camera.updateMatrixWorld();
            currentRenderList = new WebGLRenderListMini();
            projectObject( scene, camera );
            background.render( scene );
            renderScene( currentRenderList, scene, camera );
        };
​
        /** 深度优先遍历场景树,将 Mesh 的 geometry/material 推入渲染列表 */
        function projectObject( object, camera ) {
            if ( object.isMesh ) {
                // 将 BufferGeometry 同步到 GPU(attributes 有变化时才上传)
                const geometry = objects.update( object );
                const material = object.material;
                currentRenderList.push( object, geometry, material );
            }
            const children = object.children;
            for ( let i = 0, l = children.length; i < l; i ++ ) {
                projectObject( children[ i ], camera );
            }
        }
​
        /** Mini 版仅 opaque 队列,无 transparent 排序与多 Pass */
        function renderScene( currentRenderList, scene, camera ) {
            const { opaque: opaqueObjects } = currentRenderList;
            renderObjects( opaqueObjects, scene, camera );
        }
​
        function renderObjects( renderList, scene, camera ) {
            for ( let i = 0, l = renderList.length; i < l; i ++ ) {
                const renderItem = renderList[ i ];
                const { object, geometry, material } = renderItem;
                renderObject( object, scene, camera, geometry, material );
            }
        }
​
        function renderObject( object, scene, camera, geometry, material ) {
            // modelView = view × model,供顶点着色器变换
            object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
            _this.renderBufferDirect( camera, scene, geometry, material, object );
        }
​
        /**
         * 按材质获取 WebGLProgramMini:先查材质级缓存,未命中则编译并写入全局 programsMap。
         * currentProgram 相同时直接返回,避免重复 useProgram。
         */
        function getProgram( material ) {
            const materialProperties = properties.get( material );
            const parameters = programCache.getParameters( material );
            const programCacheKey = programCache.getProgramCacheKey( parameters );
            let programs = materialProperties.programs;
​
            if ( programs === undefined ) {
                material.addEventListener( 'dispose', onMaterialDispose );
                programs = new Map();
                materialProperties.programs = programs;
            }
​
            let program = programs.get( programCacheKey );
​
            if ( program !== undefined && materialProperties.currentProgram === program ) {
                return program;
            }
​
            if ( program === undefined ) {
                parameters.uniforms = programCache.getUniforms( material );
                program = programCache.acquireProgram( parameters, programCacheKey );
                programs.set( programCacheKey, program );
                materialProperties.uniforms = parameters.uniforms;
                materialProperties.uniformsList = null; // 新 program 需重建 uniform 上传列表
            }
​
            materialProperties.currentProgram = program;
            return program;
        }
​
        /** 将 program 的 uniform 声明序列与材质 uniform 值配对,结果缓存供 upload 复用 */
        function getUniformList( materialProperties ) {
            if ( materialProperties.uniformsList === null ) {
                // 获取WebglProgram的uniforms
                const progUniforms = materialProperties.currentProgram.getUniforms();
                materialProperties.uniformsList = WebGLUniformsMini.seqWithValue( progUniforms.seq, materialProperties.uniforms );
            }
​
            return materialProperties.uniformsList;
        }
​
        /** 切换 shader、上传 projectionMatrix / modelViewMatrix 及材质 uniform 
         * material: Material
        */
        function setProgram( camera, scene, geometry, material, object ) {
            const materialProperties = properties.get( material );
            // 并向materialProperties.uniforms 写入WebglProgram的uniforms
            const program = getProgram( material );
            // 从program中获取uniforms,p_uniforms:WebGLUniformsMini
            const p_uniforms = program.getUniforms();
            // 自定义的uniforms,如ShaderLibMini.mini.uniforms
            const m_uniforms = materialProperties.uniforms;
            state.useProgram( program.program );
            // 设置projectionMatrix
            p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
            // 刷新材质的uniforms,将material的uniforms同步到program的m_uniforms
            materials.refreshMaterialUniforms( m_uniforms, material );
            // 上传材质的uniforms,将program的uniforms同步到GPU
            // getUniformList( materialProperties ) 获取program的uniforms列表
            WebGLUniformsMini.upload( _gl, getUniformList( materialProperties ), m_uniforms );
            // 设置modelViewMatrix
            p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
            // 返回program
            return program;
        }
​
        /** 材质销毁时,释放 program 引用 */
        function onMaterialDispose( event ) {
            console.log('onMaterialDispose', event);
            const material = event.target;
            material.removeEventListener( 'dispose', onMaterialDispose );
            deallocateMaterial( material );
        }
​
        function deallocateMaterial( material ) {
            releaseMaterialProgramReferences( material );
            properties.remove( material );
        }
​
        function releaseMaterialProgramReferences( material ) {
            const materialProperties = properties.get( material );
            const programs = materialProperties.programs;
            if ( programs !== undefined ) {
                for ( const program of programs.values() ) {
                    programCache.releaseProgram( program );
                }
            }
        }
    }
}
​
export { WebGLRendererMini };
​

2-WebGLRendererMini 的功能定位

WebGLRenderer 是 Three.js 的核心 WebGL 2 渲染器(约 3700 行)。

WebGLRenderer 把场景图(Scene Graph)转成 GPU 上的 draw call(绘制调用),是你在应用里最常接触的类之一:

scss 复制代码
const renderer = new WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
​
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}

WebGLRenderer 的整体定位:编排器,而非"全部实现"。

WebGLRenderer 本身不直接写大量 GLSL 或底层 WebGL 调用,而是作为总调度器,组合 webgl/ 目录下约 20 个子模块:

模块 职责
WebGLPrograms Shader 编译、程序缓存
WebGLState 深度测试、混合、视口等 GL 状态
WebGLGeometries / WebGLAttributes 几何体、顶点缓冲
WebGLTextures 纹理上传与管理
WebGLRenderLists 渲染队列(opaque / transmissive / transparent)
WebGLRenderStates 每帧灯光、阴影等渲染状态
WebGLShadowMap 阴影贴图
WebGLBackground 背景色 / 天空盒 / 环境贴图
WebGLBindingStates VAO、顶点属性绑定
WebXRManager VR/AR 支持

可以理解为:Three.js 的高层 API → WebGLRenderer → 各 WebGL 子系统 → 原生 WebGL 2 API。

WebGLRendererMini 保留了WebGLRenderer 的核心更能,但具体功能做了删减。

WebGLRendererMini 保留了最核心的 WebGL 渲染链路:

场景遍历 → 渲染列表 → 着色器程序 → VBO 写入 → VAO 绑定 → drawArrays

3-WebGLRendererMini 的限制

支持 不支持
WebGL2 WebGL1
不透明 Mesh 透明物体排序
MeshMiniMaterial 多种材质类型
三角形 drawArrays 实例化、线框、阴影、多 Pass

4-WebGLRendererMini 的整体架构

5-构造函数:初始化 WebGL 上下文与子系统

ini 复制代码
class WebGLRendererMini {
    constructor( parameters = {} ) {
        // ...
        let _gl = canvas.getContext( 'webgl2', );
​
        function initGLContext() {
            state = new WebGLStateMini( _gl );
            properties = new WebGLProperties();
            attributes = new WebGLAttributesMini( _gl );
            bindingStates = new WebGLBindingStatesMini( _gl, attributes );
            geometries = new WebGLGeometriesMini( _gl, attributes, bindingStates );
            objects = new WebGLObjectsMini( geometries );
            programCache = new WebGLProgramsMini( _this, bindingStates );
            materials = new WebGLMaterialsMini();
            background = new WebGLBackgroundMini( _this, state );
            bufferRenderer = new WebGLBufferRendererMini( _gl );
        }

各子模块职责:

模块 作用
WebGLStateMini 管理 GL 状态(viewport、useProgram 等)
WebGLProperties 材质/对象的运行时缓存(program、uniforms)
WebGLAttributesMini BufferAttribute → GPU buffer
WebGLBindingStatesMini VAO 与顶点属性绑定
WebGLGeometriesMini 几何体 GPU 缓存
WebGLObjectsMini 对象级几何体更新
WebGLProgramsMini shader 编译与 program 池
WebGLMaterialsMini 材质 uniform 同步
WebGLBackgroundMini 清屏/背景色
WebGLBufferRendererMini 底层 gl.drawArrays

结构与完整版 WebGLRenderer.initGLContext() 一一对应,只是每个模块都换成了 Mini 版。

6. 对外 API

setSize / setViewport

设置 canvas 物理像素尺寸、CSS 尺寸,并更新 WebGL viewport。

clear

只清颜色缓冲:gl.clear(COLOR_BUFFER_BIT)

render(scene, camera) --- 主入口

ini 复制代码
this.render = function ( scene, camera ) {
    camera.updateMatrixWorld();
    currentRenderList = new WebGLRenderListMini();
    projectObject( scene, camera );
    background.render( scene );
    renderScene( currentRenderList, scene, camera );
};

每帧流程:

  1. 更新相机世界矩阵
  2. 新建渲染列表
  3. 遍历场景,收集所有 Mesh
  4. 清屏
  5. 按 opaque 列表逐个绘制

7-场景遍历:projectObject

ini 复制代码
function projectObject( object, camera ) {
    if ( object.isMesh ) {
        const geometry = objects.update( object );
        const material = object.material;
        currentRenderList.push( object, geometry, material );
    }
    const children = object.children;
    for ( let i = 0, l = children.length; i < l; i ++ ) {
        projectObject( children[ i ], camera );
    }
}
  • 递归遍历场景树
  • 遇到 isMesh 时:把 geometry 同步到 GPU,推入渲染列表
  • 不做 视锥剔除、透明排序、层级排序

8-渲染列表:WebGLRenderListMini

只有一个 opaque 数组,每项包含 { object, geometry, material }

完整版还有 transparent 队列和按深度排序,这里全部省略。

9-单对象绘制:renderObjectrenderBufferDirect

php 复制代码
function renderObject( object, scene, camera, geometry, material ) {
    object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
    _this.renderBufferDirect( camera, scene, geometry, material, object );
}
ini 复制代码
this.renderBufferDirect = function ( camera, scene, geometry, material, object ) {
    const program = setProgram( camera, scene, geometry, material, object );
    const position = geometry.attributes.position;
    bindingStates.setup( program, geometry ); 
    bufferRenderer.setMode( _gl.TRIANGLES );
    bufferRenderer.render( 0, position.count );
};

三步完成一次 draw call:

  1. setProgram --- 选 shader、上传 uniform
  2. bindingStates.setup --- 绑定 VAO / 顶点属性
  3. bufferRenderer.render --- gl.drawArrays(TRIANGLES, 0, count)

10-Shader program管理:getProgram

ini 复制代码
function getProgram( material ) {
    const materialProperties = properties.get( material );
    const parameters = programCache.getParameters( material );
    const programCacheKey = programCache.getProgramCacheKey( parameters );
    // ...
    let program = programs.get( programCacheKey );
​
    if ( program !== undefined && materialProperties.currentProgram === program ) {
        return program;
    }
​
    if ( program === undefined ) {
        parameters.uniforms = programCache.getUniforms( material );
        program = programCache.acquireProgram( parameters, programCacheKey );
        programs.set( programCacheKey, program );
        // ...
    }
    materialProperties.currentProgram = program;
    return program;
}

缓存策略(与完整版一致):

  • 在WebGLProgramsMini的programsMap 中缓存WebGLProgramMini 对象。

  • programsMap 会以Material 中的特定属性为鉴,存储WebGLProgramMini 对象。

    以此可以根据相应的键,从WebGLProgramsMini的programsMap 中获取相应的WebGLProgramMini 对象。

    ini 复制代码
    const materialProperties = properties.get( material );
    const parameters = programCache.getParameters( material );
    const programCacheKey = programCache.getProgramCacheKey( parameters );
  • currentProgram 是当前的WebGLProgramMini,若WebGLProgramMini 未变,则跳过useProgram。

11-Uniform 上传:setProgram

ini 复制代码
function setProgram( camera, scene, geometry, material, object ) {
    const materialProperties = properties.get( material );
    const program = getProgram( material );
    const p_uniforms = program.getUniforms();
    const m_uniforms = materialProperties.uniforms;
    state.useProgram( program.program );
    p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
    materials.refreshMaterialUniforms( m_uniforms, material );
    WebGLUniformsMini.upload( _gl, getUniformList( materialProperties ), m_uniforms );
    p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
    return program;
}

在上传uniform 之前需要先useProgram,从而确定要上传到哪个shader program。

projectionMatrix 和modelViewMatrix 是通用的uniform,通过p_uniforms.setValue 方法单独上传的。

跟具体材质相关uniform 是通过WebGLUniformsMini.upload() 方法上传的。

getUniformList( materialProperties ) 方法可以获取响应材质所对应的WebglProgram的uniforms。

12-资源释放

scss 复制代码
function onMaterialDispose( event ) {
    const material = event.target;
    material.removeEventListener( 'dispose', onMaterialDispose );
    deallocateMaterial( material );
}
​
function deallocateMaterial( material ) {
    releaseMaterialProgramReferences( material );
    properties.remove( material );
}
​
function releaseMaterialProgramReferences( material ) {
    const materialProperties = properties.get( material );
    const programs = materialProperties.programs;
    if ( programs !== undefined ) {
        for ( const program of programs.values() ) {
            programCache.releaseProgram( program );
        }
    }
}

材质触发 dispose 时:

  • 释放关联的 WebGL program
  • properties 中移除材质缓存

13-使用示例

examples/webgl_renderer_mini.html

xml 复制代码
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - geometry - triangle</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
    </head>
    <body>
        <script type="importmap">
            {
                "imports": {
                    "three": "../build/three.module.js",
                    "three/addons/": "./jsm/"
                }
            }
        </script>
        <script type="module">
            import * as THREE from 'three';
​
            const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
            camera.position.z = 4;
            const scene = new THREE.Scene();
​
            const geometry = new THREE.BufferGeometry();
            const vertices = new Float32Array( [
                - 1.0, - 1.0, 0.0,
                1.0, - 1.0, 0.0,
                0.0, 1.0, 0.0
            ] );
            geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
​
            const material = new THREE.MeshMiniMaterial( { color: 0x00acec } );
            const mesh = new THREE.Mesh( geometry, material );
            scene.add( mesh );
​
            const renderer = new THREE.WebGLRendererMini( { antialias: true } );
            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.render( scene, camera );
            document.body.appendChild( renderer.domElement );
​
            window.addEventListener( 'resize', onWindowResize );
​
            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize( window.innerWidth, window.innerHeight );
                renderer.render( scene, camera );
            }
        </script>
    </body>
</html>
​

效果如下:

14-与完整版 WebGLRenderer 的对比

环节 完整版 Mini 版
场景遍历 剔除、排序、多 Pass 简单 DFS,只收集 Mesh
渲染队列 opaque + transparent 仅 opaque
材质 几十种 MeshMiniMaterial
绘制 drawArrays / drawElements / Instanced drawArrays(TRIANGLES)
阴影/后处理/MSAA

总结

WebGLRendererMini 是 Three.js 渲染管线的最小可读实现,核心就一条链:

复制代码
Scene 树 → Mesh 收集 → 清屏 → 对每个 Mesh:
  编译/缓存 Program → 上传 Matrix + Material Uniform → 绑 VAO → drawArrays

现在大家应该给已经对three.js 整体的架构原理有了一个基本认知,接下来我们便可以以此为锲口,深入研究其中我们尚未说过的内容。

相关推荐
李伟_Li慢慢1 小时前
10-mini版bindingStates
前端·three.js
李伟_Li慢慢1 小时前
08-BufferAttribute
前端·three.js
李伟_Li慢慢1 小时前
09-mini版WebGLAttributes
前端·three.js
李伟_Li慢慢2 小时前
11-mini版WebGLGeometries
前端·three.js
李伟_Li慢慢2 小时前
18-mini版WebGLRenderList
前端·three.js
李伟_Li慢慢2 小时前
15-mini版WebGLMaterials
前端·three.js
ji_shuke2 小时前
Vue3 前端批量打印 PDF/图片踩坑记:跨域、合并打印、对话框闪退与按钮一直 loading
前端·pdf·状态模式·pdf打印
李伟_Li慢慢2 小时前
06-mini版WebGLState
前端·three.js
李伟_Li慢慢2 小时前
04-three.js的渲染流程概览
前端·three.js