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 填 ArrayBuffer,bindBufferBase(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