【问题标题】:WebGL Read pixels from floating point render targetWebGL 从浮点渲染目标读取像素
【发布时间】:2013-07-31 21:17:13
【问题描述】:

e.g. 在 WebGL 中 渲染 到浮点纹理的支持级别方面存在一些混淆。根据Optional support for FLOAT textures as FBO attachments (deprecated),OES_texture_float 扩展本身似乎并没有强制要求,但看起来一些供应商继续实施它。因此,我的基本理解是渲染到浮点纹理实际上可以在非 ES 桌面环境中工作。我无法直接从浮点渲染目标中读取

我的问题是,是否有一种方法可以使用 WebGLContext::readPixels() 调用和 Float32Array 目标来读取浮点纹理?提前致谢。

附件是一个成功读取字节纹理但读取浮点纹理失败的脚本:

<html>
<head>
<script>
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');
}
</script>
</head>
<body onload='main()'>
</body>
</html>

【问题讨论】:

    标签: webgl


    【解决方案1】:

    不幸的是,将 RGBA 组件读取为字节似乎仍然是 WebGL 的唯一方法。如果您需要将浮点数编码为像素值,可以使用以下内容:

    在您的分形着色器 (GLSL/HLSL) 中:

    float shift_right (float v, float amt) { 
        v = floor(v) + 0.5; 
        return floor(v / exp2(amt)); 
    }
    float shift_left (float v, float amt) { 
        return floor(v * exp2(amt) + 0.5); 
    }
    float mask_last (float v, float bits) { 
        return mod(v, shift_left(1.0, bits)); 
    }
    float extract_bits (float num, float from, float to) { 
        from = floor(from + 0.5); to = floor(to + 0.5); 
        return mask_last(shift_right(num, from), to - from); 
    }
    vec4 encode_float (float val) { 
        if (val == 0.0) return vec4(0, 0, 0, 0); 
        float sign = val > 0.0 ? 0.0 : 1.0; 
        val = abs(val); 
        float exponent = floor(log2(val)); 
        float biased_exponent = exponent + 127.0; 
        float fraction = ((val / exp2(exponent)) - 1.0) * 8388608.0; 
        float t = biased_exponent / 2.0; 
        float last_bit_of_biased_exponent = fract(t) * 2.0; 
        float remaining_bits_of_biased_exponent = floor(t); 
        float byte4 = extract_bits(fraction, 0.0, 8.0) / 255.0; 
        float byte3 = extract_bits(fraction, 8.0, 16.0) / 255.0; 
        float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0)) / 255.0; 
        float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent) / 255.0; 
        return vec4(byte4, byte3, byte2, byte1); 
    }
    
     // (the following inside main(){}) return your float as the fragment color
     float myFloat = 420.420;
     gl_FragColor = encode_float(myFloat);
    

    然后回到 JavaScript 端,在进行绘图调用后,您可以使用以下内容提取每个像素的编码浮点值:

    var pixels = new Uint8Array(CANVAS.width * CANVAS.height * 4);
    gl.readPixels(0, 0, CANVAS.width, CANVAS.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    pixels = new Float32Array(pixels.buffer);
    
    // pixels now contains an array of floats, 1 float for each pixel
    

    【讨论】:

    • 谢谢阿德里安。我可以验证这是否有效。我在这项工作中使用了您的解决方案:github.com/stharding/function-plotter
    • 没问题,斯蒂芬,顺便说一句,干得好;如果您还有任何问题,请随时直接联系:adrian [at] gatosomina [dot] com
    • 非常感谢,它就像一个魅力,给了我模拟所需的精度 + 我不知道 Float32Array( ArrayBuffer ) 会将 Ints 转换为浮点数,这非常方便: ) 谢谢!
    【解决方案2】:

    readPixels 仅限于 RGBA 格式和 UNSIGNED_BYTE 类型 (WebGL specification)。然而,这里描述了一些将浮点数“打包”到 RGBA/UNSIGNED_BYTE 中的方法:

    http://concord-consortium.github.io/lab/experiments/webgl-gpgpu/webgl.html

    【讨论】:

    • 这不再正确。请参阅@nraynaud 的回答,以及 David Gilbertson 在其下方的评论。 :-)
    【解决方案3】:

    我正在添加我最近的发现:Chrome 将让您读取浮点数,作为实现定义格式 (search for "readPixels" in the spec) 的一部分,Firefox 实现了 WEBGL_color_buffer_float 扩展,因此您只需加载扩展并读取您的浮点数,我无法用 Safari 读取浮点数。

    【讨论】:

    • 你知道这是否意味着 webgl 2 中需要这个功能吗?
    • 非常感谢您的评论。我在chrome中看到我可以调用gl.getContext("experimental-webgl"),然后调用gl.readPixels(0,0,width,height,gl.RGB,gl.FLOAT,buf),而我有一个浮点FBO绑定,其中bufFloat32Array。但是如何在 Firefox 中使用扩展?我可以调用gl.getExtension("WEBGL_color_buffer_float"),它会返回一个WEBGL_color_buffer_float { },但我该怎么处理它?
    • 回答我自己的问题 - 在 Firefox 中似乎只有 gl.RGBA 支持 gl.FLOAT,但是一旦你调用 gl.getExtension("WEBGL_color_buffer_float") 这似乎确实有效。
    • FWIW,1.0.3版本的规范linked above没有提到FLOAT,而是latest version (of WebGL1) does
    【解决方案4】:

    自从 WebGL 发布以来,情况发生了变化。基本上,WebGL 要求您可以使用 format = RGBA 和 type = UNSIGNED_BYTE 调用 readPixels。否则,允许该实现为每个帧缓冲区附件类型定义一个其他实现定义的格式/类型组合。

    您可以查询该格式/类型组合是什么样的

    gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebuffer);
    const format = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
    const type = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
    

    不幸的是,它是实现定义的。例如,检查我的个人设备中的至少一个,我的 Nvidia Macbook Pro,在 Chrome 中报告 RGBA/UNSIGNED_BYTE。其他浏览器/设备报告 RGBA/FLOAT

    如果启用了EXT_color_buffer_float 扩展,WebGL2 要求能够将浮点纹理读取为 RGBA/FLOAT。

    WebGL1 中的解决方法可能包括将 RGBA/UNSIGNED_BYTE 作为 FLOAT 写入。见this。你可以改变你的着色器。或者您可以添加另一个通道,读取您的浮点结果纹理并写入 RGBA/UNSIGNED_BYTE 纹理,可能是获取所有 RGBA 值的 4 倍。

    【讨论】:

      猜你喜欢
      • 2011-09-04
      • 1970-01-01
      • 2012-11-08
      • 1970-01-01
      • 1970-01-01
      • 2016-02-15
      • 2012-11-17
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多