4

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.

Skyfeiyun
  • 71
  • 1
  • 4

2 Answers2

7

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

gman
  • 83,286
  • 25
  • 191
  • 301
  • "If you remember correctly" ... I think you do, seeing how that is exactly the validation performed by the source fragment I posted :) +1 for adding clear and practical samples of what *is* and *isn't* ok. – Paul-Jan May 24 '16 at 07:06
  • Oh, even more +1 for pointing out the actual `gl.drawBuffers([gl.COLOR_ATTACHMENT3]);` bug in the sample code. – Paul-Jan May 24 '16 at 07:09
  • THX a lot, does that mean if I code like this: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 ]); I would just draw the color_attachment0 and 2, and keep attachment1? – Skyfeiyun May 25 '16 at 16:43
1

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]

Paul-Jan
  • 16,057
  • 58
  • 87