# Looking for the right blend mode

I have a graphics problem that’s been bugging me. Perhaps someone here can point me to a solution.

When a part-translucent geometry (like a soft-edged white cloud) gets blended with an opaque geometry (like a blue sky) behind it, BlendMode.Alpha creates gray areas where the translucent object’s opacity is low (like the edges of the cloud):

Blending translucent white into blue should (I think) give light blue, not gray.

This happens because BlendMode.Alpha blends the source and destination alpha values the same way it blends the RGB values. Blending A=1.0 with A=0.5 yields A=0.75 (not A=1.0).

I’ve looked at the BlendMode values and experimented with alpha fall-off, but I haven’t found a combination that does quite what I want.

In my frag shaders, I achieve the desired effect (within a single geometry) by applying colors from the outside in, using Alpha blending for the RGB channels and a non-linear blend function for the Alpha channel:

result.rgb = mix(color0.rgb, color1.rgb, color1.a);
result.a = color0.a + color1.a * (1.0 - color0.a);

Is there some way to achieve this when blending colors from multiple geometries?

What you have in the above is always going to mix gray into the color, because the original image color values are not alpha (if I remember correctly), they are are an alpha mask.

So assume white as your rgb and use the alpha value only (EDIT: alpha being the R channel).

You could, at that point, try additive, or sum divided by total number and see what the results are.

EDIT 2: If the original image was not an alpha mask, I would suggest redoing the image as one. Or ignoring the rgb values and only using the .a with a value of 1 for rgb.

EDIT 3: Heh… sorry to ramble on about this, but I was thinking you could create some really nice looking clouds by adjusting the rgb value you use based on the value of a… for instance, it would be fairly easy to make the clouds look like storm clouds, etc.

1 Like
@sgold said: This happens because BlendMode.Alpha blends the source and destination alpha values the same way it blends the RGB values. Blending A=1.0 with A=0.5 yields A=0.75 (not A=1.0).

While your equation is accurate I don’t really think it’s the cause of the issue. The calculation you cite is how the framebuffer alpha is set… but it’s not how the colors themselves are applied. The alpha for that is used directly to mix the colors.

So for red:
(sRsA) + (dR(1-sA))

So let’s say that your source red is 1.0 and source alpha is 0.5 and your destination is 0. The resulting red will be 0.5 as expected.

The framebuffer alpha will end up as 0.75 but that’s expected and doesn’t affect how it’s rendered on screen… just how it would be mixed if you reapplied it with blend mode alpha to some other render.

Do your clouds have shading around the edges to darken them? The centers look a bit shaded so it’s hard to say from the picture.

1 Like
@sgold said: In my frag shaders, I achieve the desired effect (within a single geometry) by applying colors from the outside in, using Alpha blending for the RGB channels and a non-linear blend function for the Alpha channel: [code] result.rgb = mix(color0.rgb, color1.rgb, color1.a); result.a = color0.a + color1.a * (1.0 - color0.a); [/code]

I’m curious, what is color0 and color1 in this case?

Sorry to keep spamming the thread but it occurred to me an easy way to test if you are already hacking the shaders.

Set the rgb of the read texture color to 1, 1, 1 but leave the alpha the same. Your gray edges should disappear.

I guess you are looking for something like GL_ONE_MINUS_SRC_ALPHA, I don’t know what blend modes jME support but this is a great place to try out the OpenGL blend modes: Anders Riggelsen - Visual glBlendFunc and glBlendEquation Tool

Took me a while to get used to the UI on that site but after a while it sort of comes together

1 Like
@jmaasing said: I guess you are looking for something like GL_ONE_MINUS_SRC_ALPHA, I don't know what blend modes jME support but this is a great place to try out the OpenGL blend modes: http://www.andersriggelsen.dk/glblendfunc.php

Took me a while to get used to the UI on that site but after a while it sort of comes together

The javadoc says what the blend modes mean in OpenGL terms:

Edit: and yeah, that’s a great link. I use it all the time just to have it tell me the equations.

1 Like
@pspeed said: Sorry to keep spamming the thread but it occurred to me an easy way to test if you are already hacking the shaders.

Set the rgb of the read texture color to 1, 1, 1 but leave the alpha the same. Your gray edges should disappear.

Heh… I must be invisible…

@t0neg0d said: If the original image was not an alpha mask, I would suggest redoing the image as one. Or ignoring the rgb values and only using the .a with a value of 1 for rgb.

I made the original image in this case.

@t0neg0d said: Heh... I must be invisible...

So weird. A completely empty post.

I slept in, and now I’m eight posts behind here in GMT-0800. My thanks to everyone who responded to my inquiry.

@t0neg0d said: What you have in the above is always going to mix gray into the color, because the original image color values are not alpha (if I remember correctly), they are are an alpha mask.

In this instance, the clouds and the blue background are on different geometries, so they both have complete RGBA colors for each pixel. Neither of them is a mask (though the cloud material was generated using with a mask).

The shader code I posted does what I want: it gives solid light blue (0.5, 0.5, 1, 1) when color0 is solid dark blue (0, 0, 1, 1) and color1 is semi-translucent white (1, 1, 1, 0.5). I’m trying to figure out how to get OpenGL to do this when blending colors from different geometries.

You could, at that point, try additive, or sum divided by total number and see what the results are.

Additive blending happens to work for the specific case of white clouds, but gives the wrong result when the clouds turn orange at dusk. The densest parts of the clouds (1, 0.3, 0, 1) ought to somehow block light coming from behind them.

I was thinking you could create some really nice looking clouds by adjusting the rgb value you use based on the value of a... for instance, it would be fairly easy to make the clouds look like storm clouds, etc.

Yes! I can do that in TestSkyMaterial (which puts everything on a single geometry), but I need accurate color blending for this to work with a separate cloud dome.

@pspeed said: While your equation is accurate I don't really think it's the cause of the issue. The calculation you cite is how the framebuffer alpha is set... but it's not how the colors themselves are applied. The alpha for that is used directly to mix the colors.

So for red:
(sRsA) + (dR(1-sA))

So let’s say that your source red is 1.0 and source alpha is 0.5 and your destination is 0. The resulting red will be 0.5 as expected.

The framebuffer alpha will end up as 0.75 but that’s expected and doesn’t affect how it’s rendered on screen… just how it would be mixed if you reapplied it with blend mode alpha to some other render.

That’s a good point. I’d been implicitly assuming that the darkening was due to black background bleeding through, which come to think of it is unlikely. Still, <span style=“text-decoration:underline;”>something</span> is darkening those edges.

Do your clouds have shading around the edges to darken them? The centers look a bit shaded so it's hard to say from the picture.

I believe the clouds in that shot had RGB=(1,1,1) throughout; only their alpha varied. But perhaps a simple test is in order.

@sgold said: The shader code I posted does what I want: it gives solid light blue (0.5, 0.5, 1, 1) when color0 is solid dark blue (0, 0, 1, 1) and color1 is semi-translucent white (1, 1, 1, 0.5). I'm trying to figure out how to get OpenGL to do this when blending colors from different geometries.

I think if you looked at the color of the edges of your clouds you would discover that they are not white. That is the issue we are trying to explain, I think. They look gray because they are gray. This is not really a blend mode problem since BlendMode.Alpha will do exactly as above with the same source and destination colors.

@jmaasing said: I guess you are looking for something like GL_ONE_MINUS_SRC_ALPHA, I don't know what blend modes jME support but this is a great place to try out the OpenGL blend modes: http://www.andersriggelsen.dk/glblendfunc.php

Thanks for the link: it helped. I now suspect I want to use glBlendFuncSeparate instead of glBlendFunc.

As far as I can tell, jME doesn’t support glBlendFuncSeparate at this time. Is there any reason I couldn’t add it and contribute my diffs?

@pspeed said: I think if you looked at the color of the edges of your clouds you would discover that they are not white. That is the issue we are trying to explain, I think. They look gray because they are gray. This is not really a blend mode problem since BlendMode.Alpha will do exactly as above with the same source and destination colors.

I’ll write a simple test app in which I can directly control the colors, and then we’ll see…

I think you’d get prettier clouds if you skip the translucency altogether. Make the clouds white at the edges and darker on the inside and just draw them (without additive blending). Maybe mix in some light blue for clouds near the horizon to simulate some atmosphere scattering.

@sgold said: That's a good point. I'd been implicitly assuming that the darkening was due to black background bleeding through, which come to think of it is unlikely. Still, <span style="text-decoration:underline;">something</span> is darkening those edges.

I believe the clouds in that shot had RGB=(1,1,1) throughout; only their alpha varied. But perhaps a simple test is in order.

I can state almost 100% that your clouds are not 100% white. And if you are using tonegod’s original clouds then she as much as confirmed it, too. Since the colors and alpha sounded like they were always the same. So 0.5 alpha means 0.5, 0.5, 0.5 color.

You can see from that app what the actual equations are and manually run the calculations to see that they create the exact mix you are doing manually in a shader. More to the point, I render things clouds on blue skies and do not have your issue.

@t0neg0d, I’ve understood all along that Clouds_L.png is a monochrome alpha map with many gray pixels. Since I was in fact using it as an alpha map (not as a color map) that didn’t explain where the gray was coming from.

I was seeing the same effect with other alpha maps besides Clouds_L.png, including ones I’d generated myself, but only with a clouds-only dome. When I combined blue and the clouds in a single geometry, I got the blending I expected. That’s why assumed it was a problem with blending between geometries.

I tried to repro the bug using Unshaded.jm3d and failed, which led me to take a closer look at my custom frag shader.

It turned out I was sampling alpha maps correctly to generate each cloud layer, but when I went to combine clouds layers together, I multiplied the new layer by alpha, which caused its RGB components to get grayed. Mystery solved.

I’m not sure yet how to fix this. One solution would be to eliminate the cloud dome and implement cloud flattening in the vertex shader, but the math for that looks hairy. I suspect there’s a better way to combine cloud layers in my frag shaded, I just need to find it.

Thanks again to all three of you (@t0neg0d, @pspeed, and @jmaasing) for your help.

Just a quick last question (This whole discussion sounds familiar… I think I tried to resolve this before but moved on as it wasn’t critical at the time):

You are currently using:

[java]
color + vec4(vec3(1.0),color2.r);
[/java]

as a method of blending clouds? I’m assuming not, as the above is additive and the gray shouldn’t show up. Otherwise, you are blending and averaging colors which will always produce gray.

EDIT: Ugh… I just remembered the cloud layer is on it’s own geometry isn’t it?

When you say “flattening the clouds in the vert shader”… what do you mean exactly? I can interpret “flatten” two different ways.

@t0neg0d said: Just a quick last question (This whole discussion sounds familiar... I think I tried to resolve this before but moved on as it wasn't critical at the time):

You are currently using:

[java]
color + vec4(vec3(1.0),color2.r);
[/java]

as a method of blending clouds? I’m assuming not, as the above is additive and the gray shouldn’t show up. Otherwise, you are blending and averaging colors which will always produce gray.

EDIT: Ugh… I just remembered the cloud layer is on it’s own geometry isn’t it?

SkyControl offers developers a choice: separate geometries or a single geometry, so my shaders support both approaches.

Since my code is open-source, you can examine the shaders directly and see what they do. For instance: https://code.google.com/p/jme3-utilities/source/browse/trunk/assets/Shaders/skies/dome22/dome22.frag

Note that I changed the blending method on 8 February. I had a graying issue in my single dome test case prior to 8 Feb.