为何未调用gl.useProgram时WebGL会报uniform相关警告?
问题现象
- 初始调用
gl.useProgram时程序运行正常; - 未调用
gl.useProgram时,画面无显示,控制台出现两类警告:- 第一类警告:
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program - 第二类警告:
WebGL: INVALID_OPERATION: drawElements: no valid shader program in use
- 第一类警告:
已明确的逻辑
第二类警告的原因很清晰:drawElements的签名为
drawElements(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr): void;
绘制调用需要明确运行的着色器程序,但该方法未指定程序参数,因此必须依赖全局激活的程序状态。在gl.drawElements前调用gl.useProgram后,绘制恢复正常,但第一类警告依然存在,对此存在困惑。
核心疑问
查看uniform相关API:
- 获取uniform位置的方法:
getUniformLocation(program: WebGLProgram, name: string): WebGLUniformLocation | null; - 设置uniform的方法(以
vertexAttrib4fv为例):vertexAttrib4fv(index: GLuint, values: Iterable<GLfloat>): void;
既然WebGLUniformLocation是通过指定程序获取的,为何设置uniform时仍要求当前全局激活的程序必须是该位置所属的程序?是否存在容易忽略的场景会引发这类问题?
原因解析
WebGL采用状态机模型,所有操作都依赖当前的全局状态上下文,包括uniform的设置。即使你通过getUniformLocation拿到了某个程序的uniform位置,这个位置本质上是该程序内部的索引标识——不同程序可能会有相同的位置值,但对应完全不同的uniform变量。
当你调用gl.uniformXXX系列方法时,WebGL需要明确你要把值写入到哪个激活程序的uniform中:因为代码中可能同时存在多个编译好的程序,但同一时间只能有一个处于激活状态。如果当前激活的程序和你获取uniform位置时的程序不匹配,WebGL无法确认你要修改的目标,因此抛出location is not from current program的错误。
一个容易忽略的触发场景:如果代码中创建了多个WebGLProgram,获取其中一个程序的uniform位置后,切换激活了另一个程序,此时再用之前的位置设置uniform,就会触发该警告。哪怕最后在绘制前切回了正确的程序,之前设置uniform时的状态不匹配已经导致了错误。
正确的操作流程应该是:
- 调用
gl.useProgram(program)激活目标程序; - 调用
gl.getUniformLocation(program, name)获取该程序下的uniform位置(也可提前获取,但设置时必须确保程序处于激活状态); - 调用
gl.uniformXXX(location, ...)设置uniform值; - 执行绘制调用。
内容的提问来源于stack exchange,提问作者Guichi

