13-mini版WebGLProgram

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

1-WebGLProgramMini的代码

src\renderers\webgl\WebGLPrograms.js

ini 复制代码
import { WebGLUniformsMini } from './WebGLUniformsMini.js';
import { WebGLShader } from './WebGLShader.js';
​
let programIdCount = 0;
​
/** 链接完成后,查询 shader 中所有 active attribute 的 location */
function fetchAttributeLocations( gl, program ) {
    const attributes = {};
    console.log( 'gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES )' );
    /** 获取 program 中所有 active attribute 的个数 */
    const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );
    for ( let i = 0; i < n; i ++ ) {
        // 获取 program 中第 i 个 active attribute 的信息
        const info = gl.getActiveAttrib( program, i );
        // 获取 active attribute 的名称
        const name = info.name;
        // 获取 active attribute 的 location 大小
        let locationSize = 1;
        if ( info.type === gl.FLOAT_MAT2 ) locationSize = 2;
        if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3;
        if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4;
        attributes[ name ] = {
            type: info.type,
            location: gl.getAttribLocation( program, name ),
            locationSize: locationSize
        };
    }
    return attributes;
}
​
function generatePrecision( parameters ) {
    return `precision ${parameters.precision} float;`;
}
​
/**
 * 单个 WebGL Program 的封装:拼接 GLSL → 编译 → 链接 → 缓存 uniform/attribute 位置。
 * prefix 注入 modelViewMatrix、projectionMatrix、position 等 Mini 版固定声明。
 * parameters: 编译 shader 所需的参数(源码、精度等)
 * {
 *  shaderID: shaderID,
 *  shaderType: material.type,
 *  shaderName: material.name,
 *  vertexShader: vertexShader,
 *  fragmentShader: fragmentShader,
 *  precision: precision,
 *  uniforms:{}
 * }
 */
function WebGLProgramMini( renderer, cacheKey, parameters ) {
    const gl = renderer.getContext();
    let vertexShader = parameters.vertexShader;
    let fragmentShader = parameters.fragmentShader;
    const program = gl.createProgram();
​
    // WebGL2 固定使用 GLSL 300 es
    let versionString = '#version 300 es\n';
​
    // 顶点 shader 前缀:精度 + 矩阵 uniform + position 输入
    const prefixVertex = [
        generatePrecision( parameters ),
        'uniform mat4 modelViewMatrix;',
        'uniform mat4 projectionMatrix;',
        'in vec3 position;',
    ].join( '\n' ) + '\n';
​
    // 片元 shader 前缀:指定 color attachment 0 的输出变量
    const prefixFragment = [
        'layout(location = 0) out highp vec4 pc_fragColor;',
        generatePrecision( parameters ),
    ].join( '\n' ) + '\n';
​
    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 );
​
    /** 首次访问 uniform/attribute 时懒加载 location 信息 */
    function onFirstUse( self ) {
        gl.deleteShader( glVertexShader );
        gl.deleteShader( glFragmentShader );
        cachedUniforms = new WebGLUniformsMini( gl, program );
        cachedAttributes = fetchAttributeLocations( gl, program );
    }
    let cachedUniforms;
    this.getUniforms = function () {
        if ( cachedUniforms === undefined ) {
            onFirstUse( this );
        }
        return cachedUniforms;
    };
    let cachedAttributes;
    this.getAttributes = function () {
        if ( cachedAttributes === undefined ) {
            onFirstUse( this );
        }
        return cachedAttributes;
    };
    this.destroy = function () {
        bindingStates.releaseStatesOfProgram( this );
        gl.deleteProgram( program );
        this.program = undefined;
​
    };
    function releaseProgram( program ) {
        if ( -- program.usedTimes === 0 ) {
            // Remove from unordered set
            const i = programs.indexOf( program );
            programs[ i ] = programs[ programs.length - 1 ];
            programs.pop();
            // Remove from map
            programsMap.delete( program.cacheKey );
            // Free WebGL resources
            program.destroy();
        }
    }
    this.type = parameters.shaderType;
    this.name = parameters.shaderName;
    this.id = programIdCount ++;
    this.cacheKey = cacheKey;
    this.usedTimes = 1;
    this.program = program;
    this.vertexShader = glVertexShader;
    this.fragmentShader = glFragmentShader;
    return this;
}
​
export { WebGLProgramMini };
​

2-WebGLProgramMini 的功能定位

WebGLProgram 是WebGL 渲染器里真正创建、编译、链接 GPU Shader Program 的核心模块。

WebGLProgram 不负责"选哪个材质用哪个 shader"(那是 WebGLPrograms 的事),而是把最终 GLSL 源码拼出来,交给 WebGL 编译成可执行的 gl.Program

在渲染管线中的位置:

css 复制代码
Material + Scene + Object + Lights
        ↓
WebGLPrograms.getParameters()   ← 收集参数、生成 cacheKey
        ↓
WebGLProgram                    ← 拼 GLSL、编译、链接(本文件)
        ↓
WebGLUniforms / attribute 缓存
        ↓
WebGLRenderer 绘制

WebGLProgramMini 亦是如此,只是具体功能做了删减。

WebGLProgramMini 中的依赖关系。

模块 作用
WebGLShader.js 创建 shader、注入源码、compileShader
WebGLUniformsMini.js 链接后解析 active uniform,提供 upload()
bindingStates 销毁 program 时释放 VAO / attribute 绑定状态

3-辅助函数

1. fetchAttributeLocations

在shader program 链接shader 成功后,可以根据webgl 的内置方法获取shader program 中的所有attribute 变量。

ini 复制代码
function fetchAttributeLocations( gl, program ) {
    const attributes = {};
    // ...
    const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );
    for ( let i = 0; i < n; i ++ ) {
        const info = gl.getActiveAttrib( program, i );
        // ...
        attributes[ name ] = {
            type: info.type,
            location: gl.getAttribLocation( program, name ),
            locationSize: locationSize
        };
    }
    return attributes;
}

locationSize 用于矩阵型 attribute(mat2/mat3/mat4 占多个 location),普通 vec3 position 为 1。

2-generatePrecision

生成精度声明字符串。

javascript 复制代码
function generatePrecision( parameters ) {
    return `precision ${parameters.precision} float;`;
}

3-WebGLProgramMini 构造函数

参数

参数 含义
renderer 渲染器,用于取 WebGL context
cacheKey shader program 缓存键(如 "mini"
parameters 顶点/片元源码、精度、材质类型等参数

第一步:拼接完整 GLSL

Mini 版固定使用 WebGL2 / GLSL 300 es:

ini 复制代码
let versionString = '#version 300 es\n';
​
// 顶点 shader 前缀:精度 + 矩阵 uniform + position 输入
const prefixVertex = [
    generatePrecision( parameters ),
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'in vec3 position;',
].join( '\n' ) + '\n';
​
// 片元 shader 前缀:指定 color attachment 0 的输出变量
const prefixFragment = [
    'layout(location = 0) out highp vec4 pc_fragColor;',
    generatePrecision( parameters ),
].join( '\n' ) + '\n';
​
const vertexGlsl = versionString + prefixVertex + vertexShader;
const fragmentGlsl = versionString + prefixFragment + fragmentShader;

设计要点:

  • 顶点 shader 前缀注入 MVP 所需声明:modelViewMatrixprojectionMatrixposition
  • 片元 shader 前缀声明 pc_fragColor 作为 color attachment 0 的输出(GLSL 300 es 要求显式 out)。
  • 业务 shader(如 meshmini.glsl.js)只写 main() 逻辑,不必重复声明这些固定变量。

第二步:编译与链接

ini 复制代码
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 );

标准 WebGL 流程:createProgram → 编译两个 shader → attach → linkProgram

懒加载:首次使用时才解析 uniform/attribute

ini 复制代码
function onFirstUse( self ) {
    gl.deleteShader( glVertexShader );
    gl.deleteShader( glFragmentShader );
    cachedUniforms = new WebGLUniformsMini( gl, program );
    cachedAttributes = fetchAttributeLocations( gl, program );
}
let cachedUniforms;
this.getUniforms = function () {
    if ( cachedUniforms === undefined ) {
        onFirstUse( this );
    }
    return cachedUniforms;
};
let cachedAttributes;
this.getAttributes = function () {
    if ( cachedAttributes === undefined ) {
        onFirstUse( this );
    }
    return cachedAttributes;
};

首次调用 getUniforms()getAttributes() 时:

  1. 删除已 link 的 shader 对象(program 仍保留,可继续用)。
  2. 创建 WebGLUniformsMini,解析所有 active uniform 的 location。
  3. 调用 fetchAttributeLocations 缓存 attribute 信息。

这样 program 创建后若从未渲染,就不会做 uniform / attribute 查询,减少无效开销。

4-实例上的公开属性

ini 复制代码
    this.type = parameters.shaderType;
    this.name = parameters.shaderName;
    this.id = programIdCount ++;
    this.cacheKey = cacheKey;
    this.usedTimes = 1;
    this.program = program;
    this.vertexShader = glVertexShader;
    this.fragmentShader = glFragmentShader;
属性 / 方法 含义
id 全局递增 ID,用于调试
cacheKey 缓存键(如 "selectProgram 时区分 program)
type / name 材质类型与名称
usedTimes 引用计数(WebGLProgramsMini.acquireProgram 维护)
program 链接后的 WebGL program 句柄
getUniforms() 返回 WebGLUniformsMini 实例
getAttributes() 返回 attribute location 映射
destroy() 释放 binding state 并 deleteProgram

5-与 WebGLProgramsMini 的配合

ini 复制代码
function acquireProgram( parameters, cacheKey ) {
	let program = programsMap.get( cacheKey );
	if ( program !== undefined ) {
		++ program.usedTimes;
	} else {
		program = new WebGLProgramMini( renderer, cacheKey, parameters,bindingStates );
		programs.push( program );
		programsMap.set( cacheKey, program );
	}
	return program;
}
  • 相同 cacheKey(当前仅 shaderID,如 "mini")共享同一个 WebGLProgramMini
  • 渲染时 WebGLRendererMini 会调用 program.getUniforms(),再通过 WebGLUniformsMini.upload() 把材质 uniform(如 diffuse、矩阵)上传到 GPU。

6-与完整版 Three.js WebGLProgram 的对比

完整版 Mini 版
大量 #include、宏、特性开关 固定 prefix,shader 极简
支持 WebGL1 / WebGL2 分支 固定 GLSL 300 es
复杂 cacheKey(灯光、贴图、雾等) cacheKey 只有 shaderID
完整 uniform 类型支持 WebGLUniformsMinivec3mat4

数据流小结

scss 复制代码
Material (MeshMiniMaterial)
    ↓ WebGLProgramsMini.getParameters()
ShaderLibMini.mini(vertex + fragment 源码)
    ↓ WebGLProgramMini
完整 GLSL(version + prefix + 业务代码)
    ↓ compile + link
WebGL Program
    ↓ getUniforms() / getAttributes()
GPU location 缓存
    ↓ 每帧 render
WebGLUniformsMini.upload() + attribute 绑定 → draw
相关推荐
李伟_Li慢慢1 小时前
14-mini版WebGLPrograms
前端·three.js
李伟_Li慢慢1 小时前
19-mini版WebGLRenderer
前端·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打印