Hyunan Kwon
Hyunan Kwon

Reputation: 575

Framebuffer with multiple draw buffers

I'm using the framebuffer with multiple color attachments(draw buffers), and writing colors with several shader programs.

Each shader programs use different render targets.
e.g. SHADER1 only uses the 1st draw buffer but SHADER2 uses the 2nd and 3rd draw buffer.

But if I only specify the render target in fragment shader like this:

// SHADER1.frag
layout(location = 0) out vec4 color;
void main() { color = vec4(1.0); }

This results in writing colors at every draw buffers, so I have to clear the 2nd and 3rd draw buffer.

Is this the default behavior?
And should I update glDrawbuffers state for each shader programs like this?

glUseProgram(SHADER1);
GLenum drawBuffers1[] = { GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE};
glDrawBuffers(3, drawBuffers1);

...

glUseProgram(SHADER2);
GLenum drawBuffers2[] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
glDrawBuffers(3, drawBuffers2);

...

Upvotes: 2

Views: 6132

Answers (2)

Reto Koradi
Reto Koradi

Reputation: 54652

The state set with glDrawBuffers() is part of the framebuffer object (FBO) state. So as long as you use the same FBO for all your rendering, you'll have to call glDrawBuffers() every time you want to draw to different buffers.

Based on what you describe, I think it would be much easier for you to use multiple FBOs. Using similar pseudo-notation as the one you use in your question, you could make these calls once during setup:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO1);
glFramebufferTexture2D(..., GL_COLOR_ATTACHMENT0, ..., BUFFER1, ...);
GLenum drawBuffers1[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawBuffers1);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO2);
glFramebufferTexture2D(..., GL_COLOR_ATTACHMENT0, ..., BUFFER2, ...);
glFramebufferTexture2D(..., GL_COLOR_ATTACHMENT1, ..., BUFFER3, ...);
GLenum drawBuffers2[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, drawBuffers2);

Then every time you render:

glUseProgram(SHADER1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO1);
...

glUseProgram(SHADER2);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO2);
...

To make this work, your SHADER1 will have one output (with location 0), SHADER2 has two outputs (with locations 0 and 1).

If for some reason you wanted to stick to one FBO, and make your approach work, you'll have to be careful to get the result you want:

GLenum drawBuffers2[] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
glDrawBuffers(3, drawBuffers2);

With these settings, and using a shader with two outputs, you'll have to set the locations of the outputs to 1 and 2 (using glBindFragDataLocation(), or location directives in the shader code).

You could also use this instead:

GLenum drawBuffers2[] = {GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
glDrawBuffers(2, drawBuffers2);

with locations 0 and 1 for the fragment shader outputs. One downside of this is that it will not work in case you ever want to port your code to OpenGL ES. In ES, the ith buffer can only be GL_NONE or GL_COLOR_ATTACHMENTi.

Upvotes: 3

Nicol Bolas
Nicol Bolas

Reputation: 474506

Even if your fragment shader does not output to a specific output location, that fragment still technically has outputs for those locations. Unwritten locations simply have an undefined value.

So if your draw buffer setting says "take fragment output location 1 and write it to color attachment 1", then that is what it will do. Always. Until you change it.

You could use write masking to turn writes off for specific color buffers. But really, it'd probably be better to set glDrawBuffers appropriately.

Upvotes: 2

Related Questions