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

WebGL2多输出缓冲区GPGPU开发:如何填充所有像素

解决WebGL2 GPGPU多输出缓冲区仅单个像素生效的问题

你的问题核心在于没有绘制覆盖整个视口的图元,导致只有屏幕中心的一个像素被片元着色器处理,其他像素都是默认的黑色(0,0,0,0)。我们来一步步修正:

问题根源拆解

  1. 顶点着色器逻辑错误:你把gl_Position固定成了vec4(0.0, 0.0, 0.0, 1.0),不管输入的顶点位置是什么,所有顶点都会落在屏幕中心,绘制的图元只能覆盖一个像素。
  2. Draw调用参数错误:代码里用了gl.TRIANGLE(这不是WebGL2的合法图元类型,正确的是gl.TRIANGLES),而且count参数和图元类型不匹配,进一步导致绘制范围异常。
  3. 缺少有效顶点数据:你没有给position属性绑定任何顶点缓冲区,顶点着色器的输入值都是默认的(0,0),加重了位置固定的问题。

修正方案(极简全屏图元实现)

最适合GPGPU场景的方式是用全屏三角形,不需要额外的顶点缓冲区,直接通过gl_VertexID生成覆盖整个NDC(规范化设备坐标)空间的顶点,确保视口内所有像素都被片元着色器处理。

修正后的完整代码

document.addEventListener("DOMContentLoaded", function() { 
  function main() { 
    const gl = document.querySelector('canvas').getContext('webgl2'); 
    if (!gl) { 
      return alert("需要WebGL2支持"); 
    } 
    gl.canvas.width = 2; 
    gl.canvas.height = 2; 

    // 修改后的顶点着色器:用gl_VertexID生成全屏三角形顶点
    const vs = ` 
#version 300 es
void main(void) {
  // 生成三个顶点,覆盖整个NDC空间(左-右,上-下)
  vec2 pos = vec2(gl_VertexID & 1, (gl_VertexID >> 1) & 1) * 2.0 - 1.0;
  gl_Position = vec4(pos, 0.0, 1.0);
}
`; 

    const fs = ` 
#version 300 es
precision mediump float;
layout(location = 0) out vec4 outColor0;
layout(location = 1) out vec4 outColor1;
layout(location = 2) out vec4 outColor2;
layout(location = 3) out vec4 outColor3;
layout(location = 4) out vec4 outColor4;
layout(location = 5) out vec4 outColor5;

void main() {
  outColor0 = vec4(1, 0, 0, 1);
  outColor1 = vec4(0, 1, 0, 1);
  outColor2 = vec4(0, 0, 1, 1);
  outColor3 = vec4(1, 1, 0, 1);
  outColor4 = vec4(1, 0, 1, 1);
  outColor5 = vec4(0, 1, 1, 1);
}
`; 

    const program = twgl.createProgram(gl, [vs, fs]); 

    const textures = []; 
    const fb = gl.createFramebuffer(); 
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 

    for (let i = 0; i < 6; ++i) { 
      const tex = gl.createTexture(); 
      textures.push(tex); 
      gl.bindTexture(gl.TEXTURE_2D, tex); 
      const width = 2; 
      const height = 2; 
      const level = 0; 
      gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, tex, level); 
    } 

    gl.viewport(0, 0, 2, 2); 
    gl.drawBuffers([ 
      gl.COLOR_ATTACHMENT0, 
      gl.COLOR_ATTACHMENT1, 
      gl.COLOR_ATTACHMENT2, 
      gl.COLOR_ATTACHMENT3, 
      gl.COLOR_ATTACHMENT4, 
      gl.COLOR_ATTACHMENT5, 
    ]); 

    // 修正Draw调用:绘制全屏三角形
    gl.useProgram(program); 
    gl.drawArrays(gl.TRIANGLES, 0, 3); 

    // 读取每个缓冲区的像素数据
    for (var l = 0; l < 6; l++) { 
      var pixels = new Uint8Array(gl.canvas.width * gl.canvas.height * 4); 
      gl.readBuffer(gl.COLOR_ATTACHMENT0 + l); 
      gl.readPixels(0, 0, gl.canvas.width, gl.canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 
      console.log(pixels.join(' ')); 
    } 
  } 
  main(); 
})

关键修改点说明

  1. 顶点着色器重构:通过gl_VertexID生成三个顶点,这三个顶点的坐标会覆盖整个NDC空间(x从-1到1,y从-1到1),对应的就是整个视口区域。
  2. Draw调用修正:使用gl.TRIANGLES作为图元类型,传入count=3,绘制一个全屏三角形,确保视口内的每个像素都被片元着色器处理一次。
  3. 移除冗余代码:不再需要position属性和对应的顶点缓冲区,简化了GPGPU的初始化流程。

运行修正后的代码,每个输出缓冲区的所有像素都会被设置为你预期的颜色,比如第一个缓冲区的输出会是255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255,完全符合需求。

内容的提问来源于stack exchange,提问作者simone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 21:12:39