PDA

View Full Version : selectively write to one target with multi-render tagets



tonyo_au
06-06-2012, 11:39 PM
Does anyone know if it is possible the selectively write to render tagets?

I have 2 render targets attached to a frame buffer; but depending on the logic in the fragment shader, I only want to right to 1 of the 2 target. When I do this, I
seem to get some default data written to the other render target. I am not sure if what I am doing is illegal or I just have a debug that I
can't see in my logic.

Ilian Dinev
06-07-2012, 12:42 AM
Only via geometry shader, gl_Layer .
Or separate blend extension, iirc. (not too sure about this)

What you observed is the norm/spec.

Kopelrativ
06-07-2012, 01:53 AM
Does anyone know if it is possible the selectively write to render tagets?

I have 2 render targets attached to a frame buffer; but depending on the logic in the fragment shader, I only want to right to 1 of the 2 target. When I do this, I
seem to get some default data written to the other render target. I am not sure if what I am doing is illegal or I just have a debug that I
can't see in my logic.

I suppose you want to have something in both render targets, not just a random value. One way may be to use a pixmap input with the default value that will be used. Or maybe two default pixmaps, one for each render target. It is possible to use a pixmap from the current render target, at the same time, but it is usually not recommended. It might be possible in this case, though?

thokra
06-07-2012, 02:22 AM
If you know what value should be put out to the unwritten RT (I suppose you expected a vec4(0.0)) then simply write it manually. If some value is written anyway it's not a bandwidth concern - it only makes your shader more ugly.

Dark Photon
06-07-2012, 06:48 AM
Does anyone know if it is possible the selectively write to render tagets? ...depending on the login in the fragment shader, I only want to right to 1 of the 2 target.
Yeah, there are a few tricks you can use to do this. One of the easiest is to use alpha test to selectively throw out writes to selected render targets. For instance:



bool write_2nd_target = ( some expression );

my_FragData[0] = val1.rgba;
my_FragData[1] = vec4( color2.rgb, write_this_target );


If write_this_target is false, alpha test throws out the write for that render target.

aqnuep
06-07-2012, 08:29 AM
Alpha test won't help you as that disregards the alpha of all render targets except the first one's.
Besides that, alpha test discards pixels (that means if alpha test fails based on the render target 0's alpha value, all writes are discarded, including color buffer and depth writes). Also, alpha test disables early-Z optimization thus might perform much worse (depending on your use case).
Finally, and most importantly, alpha test is deprecated.

However, you can use blending to achieve the same thing.
Just use blend functions GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and write alpha of 1.0 to the render target you want to write and 0.0 to the render target you don't want to write (of course, the blend equation should be GL_FUNC_ADD, which is the default). This won't even cause you any performance with the usual render target formats (at least not with 32 bit ones, 64 or 128 bit ones may cause performance hit when used with blending).

One last tip: You've tested what happens if you only write to one fragment shader output, leaving the other undefined and that resulted in garbage, yes? I'm pretty sure you've done that on NVIDIA hardware. Have you tried AMD?

Dark Photon
06-07-2012, 09:54 AM
Alpha test won't help you as that disregards the alpha of all render targets except the first one's. ... However, you can use blending to achieve the same thing. Just use blend functions GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and write alpha of 1.0 to the render target you want to write and 0.0 to the render target you don't want to write

Hmmm. Didn't know the detail about alpha test being RT #0 only. I was doing exactly what you mentioned with blending, which is why it works.

aqnuep
06-07-2012, 10:37 AM
Hmmm. Didn't know the detail about alpha test being RT #0 only. I was doing exactly what you mentioned with blending, which is why it works.
I know, it's confusing. Due to the similarities, we tend to believe that alpha testing is something like a special version of blending, but in fact alpha testing more like depth or test testing as they drop the fragments that fail them and in case of multiple render targets dropping the fragments drops all writes to color outputs. Actually, alpha test is equivalent with fragment shader discard.

Kopelrativ
06-07-2012, 11:31 AM
However, you can use blending to achieve the same thing.
Just use blend functions GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and write alpha of 1.0 to the render target you want to write and 0.0 to the render target you don't want to write (of course, the blend equation should be GL_FUNC_ADD, which is the default). This won't even cause you any performance with the usual render target formats (at least not with 32 bit ones, 64 or 128 bit ones may cause performance hit when used with blending).


That was a neat trick. At least as long as no other blending functionality is needed, I suppose.

tonyo_au
06-07-2012, 05:47 PM
Alpha test won't help you as that disregards the alpha of all render targets except the first one's.

One last tip: You've tested what happens if you only write to one fragment shader output, leaving the other undefined and that resulted in garbage, yes? I'm pretty sure you've done that on NVIDIA hardware. Have you tried AMD?

It was on nVidia, but what ever I do it has to work on both.

Dark Photon
06-07-2012, 05:54 PM
That was a neat trick. At least as long as no other blending functionality is needed, I suppose.
Even then you can still use it, since you can have separate blend functions and blend equations per render target if needed. See ARB_draw_buffers_blend (http://www.opengl.org/registry/specs/ARB/draw_buffers_blend.txt).

Of course, if one blend function/equation suffices for all render targets then no need to mess with that.

tonyo_au
06-07-2012, 05:55 PM
However, you can use blending to achieve the same thing.
Just use blend functions GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and write alpha of 1.0 to the render target you want to write and 0.0 to the render target you don't want to write (of course, the blend equation should be GL_FUNC_ADD, which is the default). This won't even cause you any performance with the usual render target formats (at least not with 32 bit ones, 64 or 128 bit ones may cause performance hit when used with blending).



This will work for me as I don't use blending in this shader; but one of the targets is 128bits.

Thanks all for help

aqnuep
06-07-2012, 07:24 PM
It was on nVidia, but what ever I do it has to work on both.
If you want to make it work on both NVIDIA and AMD you have two options:

1. Use a geometry shader and output gl_Layer, plus use an array texture for your two "render target".
2. Use the blending based technique presented above.

For AMD only, two more options are available:

3. Do the same as option #1 but ouput gl_Layer from the vertex shader (GL_AMD_vertex_shader_layer (http://www.opengl.org/registry/specs/AMD/vertex_shader_layer.txt)) and save the cost of having a geometry shader.
4. Just output to a single render target as you wanted to (not 100% positive this works, but as I remember it did).

Alfonse Reinheart
06-07-2012, 09:44 PM
Even then you can still use it, since you can have separate blend functions and blend equations per render target if needed. See ARB_draw_buffers_blend.

That's unnecessary in this case. GL 3.x has glEnablei/Disablei (http://www.opengl.org/wiki/GLAPI/glEnable#Indexed_Capabilities) for blending state. So you can already enable blending for certain render targets and disable them for others. You only need draw_buffers_blend if you need to have different blend functions for different render targets.