2

I am using MRT (Multiple Render Targets, drawBuffers, etc) using WebGL 1.0 (extensions) and in WebGL 2.0.

What is the best way to readPixels() from a specific bound color attachment?

All I can think is to make another FBO with my desired Texture set as COLOR_ATTACHMENT0 to read from it.

Wondering if there's another approach or a best approach that I'm not seeing?

Diniden
  • 1,005
  • 6
  • 14

1 Answers1

2

I don't think there is a best way. In WebGL2 you can use gl.readBuffer, In WebGL1 and WebGL2 you can make multiple framebuffers, one for each texture.

Here's reading them by setting readBuffer.

function main() {
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) {
    return alert("need WebGL2");
  }
  
  const textures = [];
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let i = 0; i < 4; ++i) {
    const tex = gl.createTexture();
    textures.push(tex);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    const width = 1;
    const height = 1;
    const level = 0;
    const data = new Uint8Array(4);
    data[i] = 255;
    gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, 
                  gl.RGBA, gl.UNSIGNED_BYTE, data);
    // attach texture to framebuffer
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
                            gl.TEXTURE_2D, tex, level);
  }
  
  // now try to read them
  for (let i = 0; i < textures.length; ++i) {
    gl.readBuffer(gl.COLOR_ATTACHMENT0 + i);
    const pixel = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    console.log(`${i}: ${pixel}`);
  }
}
main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

And reading them by framebuffer

function main() {
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) {
    return alert("need WebGL2");
  }
  
  const textures = [];
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let i = 0; i < 4; ++i) {
    const tex = gl.createTexture();
    textures.push(tex);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    const width = 1;
    const height = 1;
    const level = 0;
    const data = new Uint8Array(4);
    data[i] = 255;
    gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, 
                  gl.RGBA, gl.UNSIGNED_BYTE, data);
    // attach texture to framebuffer
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
                            gl.TEXTURE_2D, tex, level);
  }
  
  const fbs = textures.map(tex => {
    const fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
                            gl.TEXTURE_2D, tex, 0);
    return fb;
  });
  
  // now try to read them
  for (let i = 0; i < fbs.length; ++i) {
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbs[i]);
    const pixel = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    console.log(`${i}: ${pixel}`);
  }
}
main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
gman
  • 100,619
  • 31
  • 269
  • 393
  • Thanks! Read buffer is definitely the piece I was missing :) Gotta love the downvotes for a valid unasked question... – Diniden Jun 20 '20 at 14:31
  • Sadly, readBuffer is not supported across Safari. So I'll use readBuffer as the start and then fallback to an additional FBO solution. For anyone wondering about a fallback for readBuffer, it looks like there is a post about that: https://stackoverflow.com/questions/52427194/equivalent-of-gl-readbuffergl-color-attachmentx-in-webgl-1-0 – Diniden Jun 20 '20 at 14:34
  • 1
    Safari doesn't support WebGL2 at all. It has a flag, all the flag does is allow #version 300 es shaders. All other WebGL2 API functions are not implemented, at as of June 2020. See [the source](https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/html/canvas/WebGL2RenderingContext.cpp) and search for "NOT IMPELEMENTED" and you'll see all 80+ WebGL2 are not implemented. – gman Jun 20 '20 at 15:38