title: WebGL Read pixels from floating point render target
body: <p>There is some confusion <a href="https://www.khronos.org/webgl/public-mailing-list/archives/1210/msg00090.html" rel="noreferrer" title="1">e.g.</a> in terms of support levels for <em>rendering</em> to floating point textures in WebGL. The OES_texture_float extension does not seem to mandate it per se, as per <a href="http://www.khronos.org/registry/webgl/extensions/OES_texture_float/" rel="noreferrer" title="Optional support for FLOAT textures as FBO attachments &#40;deprecated&#41;.">Optional support for FLOAT textures as FBO attachments (deprecated)</a>, but it looks like some vendors went ahead and implement it. Therefore my basic understanding is that rendering to floating point textures actually works in non-ES desktop environments. I have not been able to <em>read</em> from the floating point render target directly though.</p>
  
  <p>My question is whether there is a way to read from a floating point texture using a WebGLContext::readPixels() call and a Float32Array destination? Thanks in advance.</p>
  
  <p>Attached is a script that succeeds reading from a byte texture, but fails for a float texture:</p>
  
  <pre><code>&lt;html&gt;
  &lt;head&gt;
  &lt;script&gt;
  function run_test(use_float) {
      // Create canvas and context
      var canvas = document.createElement('canvas');
      document.body.appendChild(canvas);
      var gl = canvas.getContext("experimental-webgl");
  
      // Decide on types to user for texture
      var texType, bufferFmt;
      if (use_float) {
          texType = gl.FLOAT;
          bufferFmt = Float32Array;
      } else {
          texType = gl.UNSIGNED_BYTE;
          bufferFmt = Uint8Array;
      }
  
      // Query extension
      var OES_texture_float = gl.getExtension('OES_texture_float');
      if (!OES_texture_float) {
          throw new Error("No support for OES_texture_float");
      }
  
      // Clear
      gl.viewport(0, 0, canvas.width, canvas.height);
      gl.clearColor(1.0, 0.0, 0.0, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
  
      // Create texture
      var texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, texType, null);
  
      // Create and attach frame buffer
      var fbo = gl.createFramebuffer();
      gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
      gl.bindTexture(gl.TEXTURE_2D, null);
      if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
          throw new Error("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
      }
  
      // Clear
      gl.viewport(0, 0, 512, 512);
      gl.clear(gl.COLOR_BUFFER_BIT);
      var pixels = new bufferFmt(4 * 512 * 512);
      gl.readPixels(0, 0, 512, 512, gl.RGBA, texType, pixels);
  
      if (pixels[0] !== (use_float ? 1.0 : 255)) {
          throw new Error("pixels[0] === " + pixels[0].toString());
      }
  }
  
  function main() {
      run_test(false);
      console.log('Test passed using GL_UNSIGNED_BYTE');
      run_test(true);
      console.log('Test passed using GL_FLOAT');
  }
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body onload='main()'&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>