You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 23:54:54