跳到主要内容

Uniform与着色器编译

着色器 编译和 link 是 CPU 尖刺,只在启动、热更新或上下文恢复时做一次。热路径只 useProgram + 写 uniform / UBO。WebGL2 用 location 或 uniform block;不要每 draw getUniformLocation

1. 编译一次,缓存 location

编译骨架见 渲染管线与着色器。包装层把 program 和 location 绑在一起:

export type ProgramLocs = {
program: WebGLProgram;
uViewProj: WebGLUniformLocation;
uSampler: WebGLUniformLocation;
};

export function locate(
gl: WebGL2RenderingContext,
program: WebGLProgram,
name: string,
): WebGLUniformLocation {
const loc = gl.getUniformLocation(program, name);
if (!loc) throw new Error(`uniform missing: ${name}`);
return loc;
}

export function bindProgram(gl: WebGL2RenderingContext, p: ProgramLocs): void {
gl.useProgram(p.program);
}

getUniformLocation 在 link 之后、删除 program 之前有效。被优化掉的 unused uniform 返回 null——当成错误抛,避免静默 uniform* 空操作。采样器只设纹理 单元号(整数),不是纹理句柄。

适用:自己的 pass、后处理。
不适用:把 GLSL 字符串拼进业务事件、指针移动时 compileShader

2. Uniform block(std140)

每网格 16 个 uniform4fv 会把 API 调用打满。WebGL2 用 UBO:CPU 填 ArrayBufferbindBufferBase(UNIFORM_BUFFER, binding, ubo)

export function createUbo(
gl: WebGL2RenderingContext,
bytes: number,
): WebGLBuffer {
const buf = gl.createBuffer();
if (!buf) throw new Error("ubo alloc failed");
gl.bindBuffer(gl.UNIFORM_BUFFER, buf);
gl.bufferData(gl.UNIFORM_BUFFER, bytes, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
return buf;
}

export function writeUbo(
gl: WebGL2RenderingContext,
ubo: WebGLBuffer,
data: ArrayBufferView,
binding: number,
): void {
gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, data);
gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, ubo);
}

std140 对齐:vec3 后常垫成 16 字节,mat4 按列 16 字节对齐。用 getActiveUniformBlockParameter / 手工 pad,不要按 JS 对象字段顺序硬塞 Float32Array。block binding 在 GLSL 用 layout(std140, binding = 0),或 uniformBlockBinding 一次。

3. WGSL 一句对照

WebGPU 没有 getUniformLocation。资源是 @group(n) @binding(m) var<uniform> u: Uniforms;,和 pipeline 的 bind group layout 对死;改字段只改 WGSL 结构体和 CPU 写入偏移。编译同样是 createShaderModule + createRenderPipeline 一次,缓存 pipeline。

4. 丢失与热更新

上下文恢复后 program 作废,按源字符串重编译,location / block index 全部重查。开发期改 GLSL:删旧 program,换新对象,不要对已删句柄 useProgram

5. 失败形态

症状原因
第一帧卡几百毫秒热路径里 compileShader / linkProgram
颜色不变location 缓存自旧 program,或 unused 被优化成 null
UBO 数值错位std140 把 vec3 当成 12 字节紧排
采样全黑uniform1i 传了纹理 id 而不是 unit
真机 link 失败#version 300 es、精度、扩展宏,见 01

权威资料

核对日期:2026-08-26