Webgl:不同格式的颜色附件在 windows 上不起作用

Webgl: color attachments with different formats do not work on windows

我正在尝试将 WEBGL_draw_buffers 与 OES_texture_float 一起使用,效果很好。但是,当使用两个不同类型的渲染目标时,这在我的 windows 机器 (amd) 上不起作用。但是,它确实适用于我的 linux 机器(开源 radeon 驱动程序)。

因此具有以下颜色附件的帧缓冲区不适用于 windows:

attachment 0 : rgb * unsigned byte

attachment 1 : rgb * float

但以下布局确实有效:

attachment 0 : rgb * float

attachment 1 : rgb * float

我写了一个小测试程序来说明这个问题:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <script type="text/javascript">
    var canvas = document.createElement('canvas');
    var gl = canvas.getContext("webgl");

    var WEBGL_draw_buffers = gl.getExtension("WEBGL_draw_buffers") || gl.getExtension("GL_EXT_draw_buffers") || gl.getExtension("EXT_draw_buffers");
    gl.getExtension("OES_texture_float");
    gl.getExtension("WEBGL_depth_texture");



    var result = "";
    result += "UNSIGNED_BYTE, FLOAT: " + test(gl.UNSIGNED_BYTE, gl.FLOAT) + "<br />";
    result += "FLOAT, FLOAT: " + test(gl.FLOAT, gl.FLOAT);

    var div = document.createElement('div');
    div.innerHTML = result;
    document.body.appendChild(div);


    function setParams() {
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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);
    }

    function test(type1, type2) {
      var w = 2, h = 2;

      var t1 = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, t1);
      setParams();
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, w, h, 0, gl.RGB, type1, null);


      var t2 = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, t2);
      setParams();
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, w, h, 0, gl.RGB, type2, null);


      var framebuffer = gl.createFramebuffer();
      gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

      WEBGL_draw_buffers.drawBuffersWEBGL([WEBGL_draw_buffers.COLOR_ATTACHMENT0_WEBGL, WEBGL_draw_buffers.COLOR_ATTACHMENT1_WEBGL]);


      gl.framebufferTexture2D(gl.FRAMEBUFFER, WEBGL_draw_buffers.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, t1, 0);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, WEBGL_draw_buffers.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, t2, 0);

      var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);


      gl.bindFramebuffer(gl.FRAMEBUFFER, null);


      switch (status) {
        case gl.FRAMEBUFFER_COMPLETE:
          return "FRAMEBUFFER_COMPLETE";
        case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
          return "FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
        case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
          return "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
        case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
          return "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
        case gl.FRAMEBUFFER_UNSUPPORTED:
          return "FRAMEBUFFER_UNSUPPORTED";
        default:
          return "Error: " + status;
      }
    }
  </script>
</body>

</html>

在 windows 上输出:

UNSIGNED_BYTE, FLOAT: FRAMEBUFFER_UNSUPPORTED
FLOAT, FLOAT: FRAMEBUFFER_COMPLETE

在 linux 上输出:

UNSIGNED_BYTE, FLOAT: FRAMEBUFFER_COMPLETE
FLOAT, FLOAT: FRAMEBUFFER_COMPLETE

我想知道为什么。请注意,我的结果在 firefox 和 chrome.

中是相同的

更新:当 windows 上禁用 angle 时,它将表现得像 linux。

我在 reddit 上回复了,但我会在这里复制相同的内容:

据我所知,'correct' 驱动程序拒绝所有颜色的位平面数量不同的多个颜色缓冲区(参见 https://www.khronos.org/registry/gles/extensions/EXT/EXT_draw_buffers.txt 问题 5),并且根据 OpenGLES2 规范的第 4.0 节: "all color buffers attached to an application-created framebuffer object must have the same number of bitplanes"

我读到,因为附加到片段输出的所有颜色都应该具有相同的位深度,如果一些有 8 位(无符号字节)而另一些有 32 位(浮点),情况就不一样了。

Linux 驱动程序可能不严格符合规范,或者实施了放宽此限制的扩展。换句话说 - 您不能依赖支持不同位深度的多种颜色输出的驱动程序。

WebGL 只需要 3 种附件组合即可工作。

From the spec

The following combinations of framebuffer object attachments, when all of the attachments are framebuffer attachment complete, non-zero, and have the same width and height, must result in the framebuffer being framebuffer complete:

  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer

WEBGL_draw_buffers 扩展增加了一些。 From that spec.

If: A framebuffer's color attachments are all textures allocated with format RGBA and type UNSIGNED_BYTE, and The framebuffer has either:

  • No depth or stencil attachment

  • A valid DEPTH or DEPTH_STENCIL attachment

Then a call to checkFramebufferStatus against this framebuffer must not return FRAMEBUFFER_UNSUPPORTED. (In other words, the implementation must support the use of RGBA/UNSIGNED_BYTE textures as color attachments, plus either a DEPTH or DEPTH_STENCIL attachment.)

Attaching n consecutive color attachments starting at COLOR_ATTACHMENT0_WEBGL, where n is between 1 and MAX_DRAW_BUFFERS_WEBGL, must not return FRAMEBUFFER_UNSUPPORTED from a call to checkFramebufferStatus. In other words, if MAX_DRAW_BUFFERS_WEBGL is 4, then the implementation is required to support the following combinations of color attachments:

  • COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE

  • COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE

  • COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT2_WEBGL = RGBA/UNSIGNED_BYTE

  • COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT2_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT3_WEBGL = RGBA/UNSIGNED_BYTE

ALL 其他组合取决于驱动程序。

正如@Kimixa 提到的规范列出了不起作用的组合,但相反,规范还说哪些组合起作用是 100% 依赖于驱动程序的。事实上,在 OpenGL ES 2.0 中,甚至上面提到的那些都不需要工作。 WebGL 在 OpenGL ES 2.0

之上添加了该要求

这也是某些 GPU 未启用 WEBGL_draw_buffers 的原因之一。 Chrome 在第一次初始化 WebGL 时测试所有必需的组合。如果驱动程序没有为每个需要的组合 return FRAMEBUFFER_COMPLETE 则 Chrome 不会启用 WEBGL_draw_buffers 扩展。

除了必需的组合之外,了解特定组合是否有效的唯一方法是设置它们,然后使用 gl.checkFramebufferStatus 进行检查。