1
$\begingroup$

I'm rendering transparent objects in separate framebuffer then compositing it with opaque one to get final image.

The transparent pass has additive blending:

glBlendFunci(0, GL_ONE,  GL_ONE);

so transpareny seems work perfect (I used weighted, blended OIT). Now I have problem if I set all surfaces' alpha to 1. Transparency works if alpha is between 0 and < 1 but not work as expected if alpha = 1. In this case (alpha=1) transparency surface seems opaque on opaque surface[s], but it also appears as semi-transparent on transparent surface[s].

I think additive blending causes this issue/artifact, how can I fix this? I want to transparent surfaces appear opaque if alpha is set to 1 in fragment shader. I can't disable blending because it will disable whole transparency process.

What I want to do is control blending in fragment shader or per-fragment blending; is this possible? Overriding old values of some fragments in framebuff e.g. to zero (0,0,0,0) could also help to fix/control blending, because I can put some values in blend equation to get what I want.

I'm asking this because maybe there is a way to simulate this which I don't know yet. Maybe I can fix this with another rendering passes/targets, I will give a try, any help would be appreciated

EDIT:

The first image is rendered with alpha = 0.5 and the second one is rendered with alpha = 1.0. As you can see transparency is still active among transparent objects. The pink plane overrides Suzanne mesh and bottom plane but not sphere :( Both sphere and pink plane are rendered in transparency pass.

Alpha = 0.5 Alpha = 1.0

$\endgroup$
  • $\begingroup$ Can you post an image showing what you mean? $\endgroup$ – user1118321 Feb 18 '18 at 18:52
  • $\begingroup$ @user1118321 sure, I updated question and added images $\endgroup$ – recp Feb 18 '18 at 19:18
  • $\begingroup$ @recp: Additive blending is not transparency. The math simply doesn't work out that way. $\endgroup$ – Nicol Bolas Feb 18 '18 at 20:42
  • $\begingroup$ @NicolBolas thanks for your comments; yep additive blending itself is not transparency but weighted, blended OIT algorithm requires additive blending ONE, ONE. I'm using GL_ONE_MINUS_SRC_ALPHA/GL_SRC_ALPHA in compositing pass $\endgroup$ – recp Feb 18 '18 at 20:47
  • $\begingroup$ I want to disable additive blending ( or additive blending LOOK ) in shader if alpha is 1. I want to do this for specific fragments/pixels, this is why I want to do it in shader ( or with helper framebuffs / buffs ... ) $\endgroup$ – recp Feb 18 '18 at 20:50
2
$\begingroup$

You seem to be using additive blending against its purpose. Additive blending is supposed to represent light from multiple sources being combined. It is not physically possible for one source of light to eclipse another.

Furthermore, even if you hack an alpha of exactly 1 to mean "opaque", you will get a strange circumstance where an alpha of 0.99 is quite far from "nearly opaque". So if you're animating "opacity", then you're going to get a sudden jump from appearing to be transparent to completely opaque.

Lastly, opacity with blending can only be achieved if you render the objects in the correct order. So even if you use this hack on the pink square, it will only eclipse the sphere if the sphere is rendered before the square. Using this hack will lose you the order-independency of additive blending.

That last part makes what you want essentially impossible.

However, if you wish to persist in this, there is a way to do what you've asked for. You need to use dual-source blending (which means you can only be rendering to a single render target). This is a technique whereby you output two values from the FS to the same render target:

layout(location = 0, index = 0) out vec4 outputColor0;
layout(location = 0, index = 1) out vec4 outputColor1;

Both values can be used in the blending equation. This means there are two source colors. The index zero source color is the source color used directly in the additive blending equation. The index one source color is how you turn off the destination color and achieve opacity:

glBlendFunci(0, GL_ONE,  GL_SRC1_ALPHA);

With this blend function, if you want a fragment shader's output to be opaque, you set outputColor1.a to 0. Otherwise you set it to 1. The other components are irrelevant.

Of course, you must still suffer the limitations of dual-source blending. Namely, that you can only render to a single render target on most hardware.

$\endgroup$
  • $\begingroup$ The OP said they are using weighted, blended OIT, which does use glBlendFunci(0, GL_ONE, GL_ONE) in a transparency accumulation pass. $\endgroup$ – Rahul Feb 19 '18 at 5:29
  • $\begingroup$ That is correct, plus the additive blending blends (RGB * A, A) * Weight. I don't know how dual source blending works I'll investigate it, it seems interesting! But on the other hand I need to two different render targets: accumulation (vec4: RGBA16F) and revelage (float) $\endgroup$ – recp Feb 19 '18 at 11:34
  • $\begingroup$ Maybe I can use three render targets (the first two for dual-source blending and last one for revelage), in the first pass dual-source is disabled and in other pass it is enabled (full screen quad), then compositing all passes? This would add extra pass only for dual-source blending but if it will fix the artifact I will implement it $\endgroup$ – recp Feb 19 '18 at 11:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.