webGL 2 readPixels on framebuffers with FLOAT textures
Asked Answered
B

1

8

is it possible to retrieve pixels value as a float on framebuffer with multiple attachments ? (WebGL 2)

I tried this :

var framebuffer = _gl.createFramebuffer();

_gl.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer);

_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, texture1, 0);
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT1, _gl.TEXTURE_2D, texture2, 0);

_gl.drawBuffers([_gl.COLOR_ATTACHMENT0, _gl.COLOR_ATTACHMENT1]);

With float textures setup as follow :

_gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA32F, 256, 256, 0, _gl.RGBA, _gl.FLOAT, null);

Then i bind the framebuffer and call readPixels to get values for the first attachment :

_gl.readPixels(0, 0, 1, 256, _gl.RGBA, _gl.FLOAT, 0);

Without float textures, this work but with float textures, the framebuffer stay incomplete.

The WebGL 2 spec seem to say that this should work, I now have some doubt however, it seem that _gl.RGBA32F seem to be the problem, with an internal format of _gl.RGBA, it generate incompatible type error.

Beghtol answered 8/8, 2017 at 14:46 Comment(4)
"the framebuffer stay incomplete" What is the specific reason for the incomplete framebuffer? Framebuffer completeness has a plethora of reasons for failure.Hyozo
I am using github.com/vorg/webgl-debug and no errors are thrown but still the checkFramebufferStatus function report that it is not complete, without float, the framebuffer is created without problems with the same calls but with different parameters (_gl.RGBA for internal format and _gl.UNSIGNED_BYTE for format).Beghtol
"the checkFramebufferStatus function report that it is not complete" No, it doesn't. It reports a specific reason why the framebuffer is not complete. I'm asking you want that reason is.Hyozo
Indeed, it report an incomplete attachment.Beghtol
F
18

First off, rendering to floating point requires an extension in WebGL2, EXT_color_buffer_float.

You can see in the table here copied from the spec section 3.8.3.2 that floating point textures are not renderable in WebGL2 by default.

Otherwise you call gl.readBuffer to set the buffer to read from before calling gl.readPixels

Example:

function main() {
  const gl = document.createElement("canvas").getContext("webgl2");
  if (!gl) {
    alert("need WebGL2");
    return;
  }
  const ext = gl.getExtension("EXT_color_buffer_float");
  if (!ext) {
    alert("need EXT_color_buffer_float");
    return;
  }
  
  const tex1 = createTexture(gl, [12, 0, 0, 34]);
  const tex2 = createTexture(gl, [0, 56, 78, 0]);
  
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex1, 0);

  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, tex2, 0);

  gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
  
  readPixelsFromBuffer(gl, gl.COLOR_ATTACHMENT0);
  readPixelsFromBuffer(gl, gl.COLOR_ATTACHMENT1);
}

function readPixelsFromBuffer(gl, attachment) {
  gl.readBuffer(attachment);
  const data = new Float32Array(4);
  const x = 0;
  const y = 0;
  const width = 1;
  const height = 1;
  const format = gl.RGBA;
  const type = gl.FLOAT;
  gl.readPixels(x, y, width, height, format, type, data);
  log(glEnumToString(gl, attachment), data);
}

function createTexture(gl, color) {
  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  const level = 0;
  const internalFormat = gl.RGBA32F;
  const width = 1;
  const height = 1;
  const border = 0;
  const format = gl.RGBA;
  const type = gl.FLOAT;
  const data = new Float32Array(color);
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
                format, type, data);
  // unless we get `OES_texture_float_linear` we can not filter floating point
  // textures
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  
  return tex;
}

function glEnumToString(gl, value) {
  for (let key in gl) {
    if (gl[key] === value) {
      return key;
    }
  }
  return `0x${value.toString(16)}`;
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}

main();
Fardel answered 8/8, 2017 at 16:14 Comment(3)
Once to read from the first buffer, again to read from the second.Fardel
Sorry, I see how that could be confusing. I edited the function name. Hopefully it's less confusing nowFardel
Thank you, this work and learned few things from that code as well, i did not know about the need of EXT_color_buffer_float.Beghtol

© 2022 - 2024 — McMap. All rights reserved.