Get some trounble when using drawBuffers in WebGL2

2019-06-24 02:30发布

问题:

I want to combine my deferred renderer and forward renderer together. In order to share the same depth buffer, I use a single frame buffer object with 4 color attachments, COLOR_ATTACHMENT0-2 for G-buffer rendering, COLOR_ATTACHMENT3 for deferred shading and forward rendering, here's the pesudo code:

//**Gbufffer part**
Bind G-Buffer FBO
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
draw the G buffer

//**Lighting part**
Bind Lighting buffer FBO

//**Shading part**
Bind G-Buffer FBO
gl.drawBuffers([gl.COLOR_ATTACHMENT3]);

//**Forward rendring part**
//Still use the G-Buffer FBO and COLOR_ATTACHMENT3
draw forward material

when using this, I got a mistake in firefox:

Error: WebGL: drawBuffers: buffers[i] must be NONE or COLOR_ATTACHMENTi.

when lauching in Chrome, I got this:

FrameBufferObject.ts:151 WebGL: INVALID_OPERATION: drawBuffers: COLOR_ATTACHMENTi_EXT or NONE

What's wrong with my code? This really confuse me...THX.

回答1:

If I remember correctly you must use color attachment 0 for the first buffer, color attachment 1 for the second, color attachment 2 for the 3rd etc..

In otherwords this is ok

gl.drawBuffers([
  gl.COLOR_ATTACHMENT0,  // color attachment 0 to draw buffer 0
  gl.COLOR_ATTACHMENT1,  // color attachment 1 to draw buffer 1
  gl.COLOR_ATTACHMENT2,  // color attachment 2 to draw buffer 2
]);

This is also ok

gl.drawBuffers([
  gl.COLOR_ATTACHMENT0,  // color attachment 0 to draw buffer 0
  gl.NONE,               // NONE to draw buffer 1
  gl.COLOR_ATTACHMENT2,  // color attachment 2 to draw buffer 2
]);

This is not!!

gl.drawBuffers([
  gl.COLOR_ATTACHMENT0,  // color attachment 0 to draw buffer 0
  gl.COLOR_ATTACHMENT2,  // color attachment 2 to draw buffer 1 ERROR!
  gl.COLOR_ATTACHMENT1,  // color attachment 1 to draw buffer 2 ERROR!
]);

So in your case.

gl.drawBuffers([
   gl.COLOR_ATTACHMENT3,  // color attachment 3 to draw buffer 0 ERROR!
]);

The must always match 0 to 0, 1 to 1, 2 to 2, etc.

If that's really what you're doing you should make 3 framebuffer objects. One with the first 3 buffers, one with the 4th buffer, and one with all 4 buffers. Then you'd do something like

gl.bindFramebuffer(gl.FRAMEBUFFER, threeAttachmentFB);
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);

.. draw stuff ..

gl.bindFramebuffer(gl.FRAMEBUFFER, oneAttachmentFB);
gl.drawBuffers([gl.COLOR_ATTACHMENT0]);

.. draw stuff ..

gl.bindFramebuffer(gl.FRAMEBUFFER, fourAttachmentFB);
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1,
                gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3]);

.. draw stuff ..

In edition to that, various combos are not guaranteed to work. It's unclear what the limits are in WebGL2 but in WebGL1 only these combinations are guaranteed to work

  • one color attachment (with or without depth or depth_stencil)

    COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE
    
  • two color attachments (with or without depth or depth_stencil)

    COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE
    COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE
    
  • three color attachments (with or without depth or depth_stencil)

    COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE
    COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE
    COLOR_ATTACHMENT2_WEBGL = RGBA/UNSIGNED_BYTE
    
  • four color attachments (with or without depth or depth_stencil)

    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 other combinations may or may not work depending on the GPU/Driver/Browser/OS



回答2:

I would not be surprised if your actual code is slightly different from what you are posting here.

Take a look at the Firefox sources for WebGLContext::DrawBuffers over at https://dxr.mozilla.org/mozilla-central/source/dom/canvas/WebGLContextFramebufferOperations.cpp:

for (size_t i = 0; i < buffers.Length(); i++) {
    // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
    //  bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order,
    //  BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of
    // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION.

    // WEBGL_draw_buffers:
    // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
    //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
    // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
    // be larger than MaxColorAttachments.
    if (buffers[i] != LOCAL_GL_NONE &&
        buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i)
    {
        ErrorInvalidOperation("%s: `buffers[i]` must be NONE or COLOR_ATTACHMENTi.",
                              funcName);
        return;
    }
}

Your code is failing that check. Maybe you've got the order mixed up?

[Edit] As gman points out, your second call to drawbuffers, gl.drawBuffers([gl.COLOR_ATTACHMENT3]);, doesn't comply with this validation [/Edit]