WebGL切换点/线网格着色器失效问题求助
WebGL双着色器切换失效问题修复
问题描述
使用两个着色器分别绘制点网格和线网格背景,renderGridPoint和renderGridLine负责对应绘制逻辑。切换着色器时执行gl.useProgram切换程序,但无论先启用哪个着色器,第二个着色器始终无法生效。
核心原因
WebGL中顶点属性数组的启用状态是全局状态,不属于某个着色器程序。当切换到新程序后,之前启用的顶点属性数组仍处于激活状态,但新程序中对应的属性位置可能与旧程序不匹配,或者没有为新程序正确绑定属性与缓冲区的关联,导致渲染管线无法正确解析顶点数据,最终新着色器无法正常工作。
修复方案
1. 切换程序时重置顶点属性状态
在切换着色器程序后,先禁用所有可能处于激活状态的顶点属性数组,再只启用当前程序需要的属性,避免全局状态干扰新程序的渲染。
2. 确保属性操作前激活对应程序
所有获取uniform/attribute位置、设置属性指针、启用属性数组的操作,必须在gl.useProgram之后执行,保证操作针对当前激活的程序。
修改后的关键代码
调整renderGridPoint方法
renderGridPoint(){ const canvas = this.$refs.bgCanvas; const gl = canvas.getContext('webgl'); const gridSpace = 30; const gridSize = 2; // 激活当前程序 gl.useProgram(canvas.programPoint); canvas.grid = 'point'; if(canvas.clientWidth !== canvas.width || canvas.clientHeight !== canvas.height){ canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; gl.viewport(0, 0, canvas.width, canvas.height); const projectionMatrix = mat4.create(); mat4.ortho(projectionMatrix, 0, canvas.clientWidth, canvas.clientHeight, 0, -1, 1); const projectionMatrixLocation = gl.getUniformLocation(canvas.programPoint, 'uProjectionMatrix'); gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix); } const gridPoints = []; for (let i = 0; i <= canvas.clientWidth; i += gridSpace) { for (let j = 0; j <= canvas.clientHeight; j += gridSpace) { gridPoints.push(i, j); } } // 复用缓冲区,避免重复创建 if (!canvas.pointBuffer) { canvas.pointBuffer = gl.createBuffer(); } gl.bindBuffer(gl.ARRAY_BUFFER, canvas.pointBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(gridPoints), gl.STATIC_DRAW); const color = gl.getUniformLocation(canvas.programPoint, 'u_color'); gl.uniform4f(color, 194/255, 194/255, 197/255, 1); const aSize = gl.getAttribLocation(canvas.programPoint, 'a_Size'); gl.vertexAttrib1f(aSize, gridSize); const positionAttributeLocation = gl.getAttribLocation(canvas.programPoint, 'aPosition'); // 先禁用所有属性,再启用当前需要的 for(let i = 0; i < gl.getParameter(gl.MAX_VERTEX_ATTRIBS); i++){ gl.disableVertexAttribArray(i); } gl.enableVertexAttribArray(positionAttributeLocation); gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, gridPoints.length / 2); }
调整renderGridLine方法
renderGridLine(){ const canvas = this.$refs.bgCanvas; const gl = canvas.getContext('webgl'); const gridSpace = 100; const gridSize = 1; // 激活当前程序 gl.useProgram(canvas.programLine); canvas.grid = 'line'; if(canvas.clientWidth !== canvas.width || canvas.clientHeight !== canvas.height){ canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; gl.viewport(0, 0, canvas.width, canvas.height); const projectionMatrix = mat4.create(); mat4.ortho(projectionMatrix, 0, canvas.clientWidth, canvas.clientHeight, 0, -1, 1); const projectionMatrixLocation = gl.getUniformLocation(canvas.programLine, 'uProjectionMatrix'); gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix); } const gridPoints = []; for (let i = 0; i <= canvas.clientWidth; i += gridSpace) { gridPoints.push(i, 0); gridPoints.push(i, canvas.clientHeight); } for (let i = 0; i <= canvas.clientHeight; i += gridSpace) { gridPoints.push(0, i); gridPoints.push(canvas.clientWidth, i); } // 复用缓冲区 if (!canvas.lineBuffer) { canvas.lineBuffer = gl.createBuffer(); } gl.bindBuffer(gl.ARRAY_BUFFER, canvas.lineBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(gridPoints), gl.STATIC_DRAW); const color = gl.getUniformLocation(canvas.programLine, 'u_color'); gl.uniform4f(color, 235/255, 237/255, 241/255, 1); const aSize = gl.getAttribLocation(canvas.programLine, 'a_Size'); gl.vertexAttrib1f(aSize, gridSize); const positionAttributeLocation = gl.getAttribLocation(canvas.programLine, 'aPosition'); // 先禁用所有属性,再启用当前需要的 for(let i = 0; i < gl.getParameter(gl.MAX_VERTEX_ATTRIBS); i++){ gl.disableVertexAttribArray(i); } gl.enableVertexAttribArray(positionAttributeLocation); gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.LINES, 0, gridPoints.length / 2); }
额外优化点
- 复用缓冲区对象,避免每次渲染都创建新的
WebGLBuffer,减少性能开销; - 将顶点属性位置的获取提前到程序初始化阶段,避免每次渲染重复调用
gl.getAttribLocation。
内容的提问来源于stack exchange,提问作者YX J
相关产品推荐
相关产品推荐

