Selectively writing to buffers

Hi,

I ran into something weird, or something that I don’t know how it works exactly.

What I want to do when rendering to multiple targets is that I selectively write to certain color buffers in my shader, but leave others alone.
for example

gl_FragData[3] = vec4(0,1,0,1);

without setting the fragment data for the first 3 buffers…

This doesn’t seem to go well. for example, if I don’t explicity set a value in the color buffer 4, it seems to copy the color value from the first color buffer for some reason instead of leaving it alone.

So I though: ok I HAVE to explicitly set all a value on all used color buffers, so then if I don’t want to touch one of the buffers, I just use blending and I write a value with an alpha of 0, so it doesn’t effect the buffer. This doesn’t work as expected.

It seems that the blending only looks at the color buffer 0 ? if I write a some color-value with an alpha of 0 to the color buffer 0, it doesn’t seem to matter what I write to the other buffers, it doesn’t write anything.

So not sure if you can follow what I’m explaining, but bottom line is: I would like to selectively write to certain color buffers in my shaders, and leave the other alone. Any ideas on how to do that properly? Or am I trying something impossible and do I need another solution?

Thank you!

Cheers,
Bram

It works as described in the specification.

For a fragment shader linked with some number of active outputs, any output not written to is undefined. You can’t depend on an unwritten buffer keeping the old value. So, within a single draw call, you can’t selectively write to some buffers, you must write to all buffers made active via glDrawBuffers().
Across multiple draw calls, of course you are free to change the set of active draw buffers (and then you might as well also change the current program.)

With a single program and set of draw buffers, you can use blending to effectively keep some old values, as you described. You will have to ensure the blend state is set up appropriately for each draw buffer. If you don’t need real blending for the buffers you do want to write to, you can simply enable and disable blending for each buffer (requires EXT_draw_buffers2, or GL3.0) with the same ZERO, ONE glBlendFunc for all draw buffers. On the other hand if you need some real blending for the buffers you want to write to, you’ll have to set the glBlendFunc and/or glBlendEquation for each buffer (requires ARB_draw_buffers_blend, or GL4.0).

The caveat with the blending approach is that you still can’t change the blend enable (or func/equation) within a single draw call. So no matter what, you’re limited to one set of active + “active but effectively disabled” buffers, per draw call. And you pay all the computation/bandwidth cost of writing something to a buffer just to leave it unchanged-- might as well change glDrawBuffers per draw call.

On GL4.2+ another option might be to use ARB_shader_image_load_store, and write directly to images, bypassing the raster blend state. Then you have complete control over which images are updated.

Hi arekkusu,

thanks for your reply, most of it makes sense now. Only thing is that don’t understand why the alpha I write to buffer 0 effects the other buffers as well. How exactly does that happen?

you can use blending to effectively keep some old values, as you described. You will have to ensure the blend state is set up appropriately for each draw buffer.

I’m not sure how to set up blending for all buffers appropriatly, apperently now it’s only set on the first buffer? How do you do that? I’m working in OSG btw, but I’m sure if I know how to do it in openGL I can make it happen with some help from others…

During blending, there is no way that the alpha of fragment output zero can affect other outputs. Blending is applied independently to each buffer.
However there are other operations (like alpha test, or alpha-to-coverage) where output zero’s alpha is special and can affect (i.e. discard) all outputs.

Read the documentation on glEnablei, or glBlendFunc/Equationi.

if that is so, which I think would be very logical, then I don’t understand this:

In a shader to draw water:

void main()
{
gl_FragData[0] = vec4(0,0,0,1);
gl_FragData[1] = vec4(0,0,0,0);
gl_FragData[2] = vec4(0,0,0,0);
gl_FragData[3] = vec4(0,1,0,1);
}

in the shade that renders the screen:

vec4 color = vec4(texture2DRect( colorsRect, gl_TexCoord[0].st ).rgb, 1);
vec4 waterColor = texture2DRect( waterRect, gl_TexCoord[0].st );

gl_FragColor=waterColor;
return;

if I change the first shader to

void main()
{
gl_FragData[0] = vec4(0,0,0,0);
gl_FragData[1] = vec4(0,0,0,0);
gl_FragData[2] = vec4(0,0,0,0);
gl_FragData[3] = vec4(0,1,0,1);
}

(as it needs to be), then there are no green pixels on my screen anymore (and I’m rendering only the buffer 3). so it really really seems like the value I write to buffer 0 effects buffer 3… so weird.

to be more precise:

this

void main()
{
gl_FragData[0] = vec4(0,0,0,0.1);
gl_FragData[1] = vec4(0,0,0,0);
gl_FragData[2] = vec4(0,0,0,0);
gl_FragData[3] = vec4(0,1,0,1);
}

will still give me bright green pixels (not 10% alpha)

while this

void main()
{
gl_FragData[0] = vec4(0,0,0,0.1);
gl_FragData[1] = vec4(0,0,0,0);
gl_FragData[2] = vec4(0,0,0,0);
gl_FragData[3] = vec4(0,1,0,0.5);
}

will give me 50% dark green pixels.

So it doesn’t seem to be that the alpha of fragdata0 is used, but it does seem like it need something different than 0, or else it won’t render anything in any buffer… weird

It’s very likely the alpha test, then. It’s a compatiblity mode test, and likely works off the first buffer, not the union of all of them. Try:

glDisable(GL_ALPHA_TEST);

before you render to your MRTs.

Thank you, it was indeed the alpha test!

were you testing with Intel\AMD card by any chance? because i’ve actually foolishly done the same thing, not setting output color for all the draw buffers for some draw calls and nVidia with it’s relaxed drivers makes this a deceptively reliable thing to do.

I am testing with AMD radeon 6800 here. I guess the tricky thing is that ‘undefined’ behavior does not produce any errors. So I was very surprised to see the terrain rendering in buffer 3, while I never drew any of those pixels to it. apparently with my card/drivers it used the output from buffer0 to fill buffers that were not written to. Probably makes sense when you know the inner workings of the drivers and hardware, but I don’t :slight_smile:

Undefined means undefined. It might do exactly what you want, or it might draw purple polka dots.

There are lots of undefined cases in OpenGL, and they aren’t errors. Some debug tools highlight undefined behavior when possible, but ultimately it’s your responsibility as a programmer to read the documentation and be aware.

Yeah I get the idea :slight_smile: I just looked in the manual / specifications, there are 107 instances of the word ‘undefined’ in there :slight_smile:

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.