【问题标题】:Sampling integer texture in WebGL returns weird values在 WebGL 中对整数纹理进行采样会返回奇怪的值
【发布时间】:2020-04-12 17:58:02
【问题描述】:

我正在尝试通过在片段着色器中应用 window leveling 从 WebGL2 中的 16 位数组缓冲区渲染灰度图像。我生成的纹理如下:

let typedArray = new Int16Array(data);
gl.texImage2D(
        gl.TEXTURE_2D,
        0,
        gl.R16I,
        w, h,
        0,
        gl.RED_INTEGER,
        gl.SHORT,
        typedArray);

并尝试使用以下片段着色器中的数据:

let fragmentShaderSource = `#version 300 es
    precision highp float;
    precision highp int;
    precision highp isampler2D;

    // our texture
    uniform isampler2D u_image;

    uniform highp float u_windowWidth;
    uniform highp float u_windowCenter;

    in vec2 v_texCoord;
    out vec4 outColor;

    void main() {
        highp float f = float(texture(u_image, v_texCoord).r);
        f = (f - (u_windowCenter - 0.5)) / max(u_windowWidth - 1.0, 1.0) + 0.5;
        f = min(max(f, 0.0), 1.0);
        outColor = vec4(vec3(f), 1.0);
    }
    `;

但这只会呈现黑屏。实际上,经过一些调试,我发现texture(u_image, v_texCoord) 在所有像素中的rgb 中的值为零,而a (alpha) 字段的值非常大(2^29 ~ 2^30)。我试过改变着色器的精度,但结果是一样的。

为了缩小问题范围,我尝试了一种不同的方法,将 16 位整数拆分为 gl.RGBA4,每个 RGBA 通道中包含 4 位:

let typedArray = new Uint16Array(data);
gl.texImage2D(
        gl.TEXTURE_2D,
        0,
        gl.RGBA4,
        w, h,
        0,
        gl.RGBA,
        gl.UNSIGNED_SHORT_4_4_4_4,
        typedArray);

并在片段着色器中将 RGBA 值组合回 16 位整数。

let fragmentShaderSource = `#version 300 es
    precision highp float;
    precision highp int;
    precision highp sampler2D;

    // our texture
    uniform sampler2D u_image;

    uniform highp float u_windowWidth;
    uniform highp float u_windowCenter;

    in vec2 v_texCoord;
    out vec4 outColor;

    void main() {
        highp vec4 rgba_map = texture(u_image, v_texCoord);
        // Combining rgba4 back into int16
        highp f = rgba_map.r * 65536.0 + rgba_map.g * 4096.0 + rgba_map.b * 256.0 + rgba_map.a * 16.0;
        // signed value
        if (f > 32768.0) {
            f = 65536.0 - f;
        }
        f = (f - (u_windowCenter - 0.5)) / max(u_windowWidth - 1.0, 1.0) + 0.5;
        f = min(max(f, 0.0), 1.0);
        outColor = vec4(vec3(f), 1.0);
    }
    `;

这个版本很好地呈现了预期的图像,尽管由于转换导致结果有点嘈杂。我也尝试过其他一些格式,浮点类型的很好,整数类型的格式都不起作用。所以我认为程序的其他部分都很好。我想知道我的程序出了什么问题。

【问题讨论】:

    标签: image-processing webgl fragment-shader webgl2 glteximage2d


    【解决方案1】:

    你还没有真正发布足够的代码来调试,所以让我们做一些可以工作的东西。

    function main() {
      const gl = document.querySelector('canvas').getContext('webgl2');
      if (!gl) {
        return alert('need WebGL2');
      }
      const vs = `#version 300 es
      void main() {
        gl_PointSize = 300.0;
        gl_Position = vec4(0, 0, 0, 1);
      }
      `;
      const fs = `#version 300 es
      precision highp float;
      precision highp int;
      precision highp isampler2D;
    
      // our texture
      uniform isampler2D u_image;
    
      out vec4 color;
      
      void main() {
        ivec4 intColor = texture(u_image, gl_PointCoord.xy);
        color = vec4(vec3(intColor.rrr) / 10000.0, 1);
      }
      `;
      
      const program = twgl.createProgram(gl, [vs, fs]);
      const tex = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, tex);
      gl.texImage2D(
          gl.TEXTURE_2D,
          0,               // mip level
          gl.R16I,         // internal format
          10,              // width
          1,               // height
          0,               // border
          gl.RED_INTEGER,  // source format
          gl.SHORT,        // source type
          new Int16Array([
            1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000
          ]));
      // can't filter integer textures
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      
      gl.useProgram(program);
      
      // no need to set any attributes or
      // uniforms as we're not using attributes
      // and uniforms default to zero so will use
      // texture unit zero
      gl.drawArrays(gl.POINTS, 0, 1);
      
      console.log('max point size:', gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1]);
    }
    main();
    canvas {
      border: 1px solid black;
      background: red;
    }
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas></canvas>

    应该是这样的

    但如果您的 GPU 最大点大小

    一些想法

    • 您检查 JavaScript 控制台是否有错误?

    • 你是否关闭了纹理过滤?

      无法过滤整数纹理

    • 你的纹理宽度是偶数吗?

      如果不是,您可能需要设置 gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1),但我预计您会在此处出现错误,除非您的 Int16Array 大于宽度 * 高度

    【讨论】:

    • 我没有关闭纹理,通过添加这两行我得到了完美的结果!感谢您的深刻见解,并为未添加足够信息而道歉。
    • 我很惊讶您没有在 JavaScript 控制台中遇到错误。如果我不设置过滤铬打印纹理不可渲染的错误
    • @gman 是否可以使用 gl.LINEAR 对 Int16 或 Uint16 进行整数纹理过滤?我真的不能让它工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    • 2021-03-05
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多