WebGL2多输出缓冲区GPGPU开发:如何填充所有像素
解决WebGL2 GPGPU多输出缓冲区仅单个像素生效的问题
你的问题核心在于没有绘制覆盖整个视口的图元,导致只有屏幕中心的一个像素被片元着色器处理,其他像素都是默认的黑色(0,0,0,0)。我们来一步步修正:
问题根源拆解
- 顶点着色器逻辑错误:你把
gl_Position固定成了vec4(0.0, 0.0, 0.0, 1.0),不管输入的顶点位置是什么,所有顶点都会落在屏幕中心,绘制的图元只能覆盖一个像素。 - Draw调用参数错误:代码里用了
gl.TRIANGLE(这不是WebGL2的合法图元类型,正确的是gl.TRIANGLES),而且count参数和图元类型不匹配,进一步导致绘制范围异常。 - 缺少有效顶点数据:你没有给
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(); })
关键修改点说明
- 顶点着色器重构:通过
gl_VertexID生成三个顶点,这三个顶点的坐标会覆盖整个NDC空间(x从-1到1,y从-1到1),对应的就是整个视口区域。 - Draw调用修正:使用
gl.TRIANGLES作为图元类型,传入count=3,绘制一个全屏三角形,确保视口内的每个像素都被片元着色器处理一次。 - 移除冗余代码:不再需要
position属性和对应的顶点缓冲区,简化了GPGPU的初始化流程。
运行修正后的代码,每个输出缓冲区的所有像素都会被设置为你预期的颜色,比如第一个缓冲区的输出会是255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255,完全符合需求。
内容的提问来源于stack exchange,提问作者simone
相关产品推荐
相关产品推荐

