Multiply blending with exception of the alpha channel?

Started by
3 comments, last by SuperVGA 11 years, 2 months ago

Hi Guys!

After having used simple blending techniques together with fragment shader effects that could easily accomplish this,
it somehow seems silly to ask. Although, I'd appreciate if i could, for once (biggrin.png), accomplish something simple with a simple approach.

I'm trying to create an effect identical to the blend mode in photoshop called multiply.

I use (GL_DST_COLOR, GL_ZERO) or (GL_ZERO, GL_SRC_COLOR) with Equation GL_FUNC_ADD to accomplish a basic multiply effect.

I thought all channels would be multiplied, maybe they do, but in that case it's like i see the clear color without anything on it,

and as that isn't really stored anywhere, there must be something else I'm doing wrong.

If I try to compute the result myself. (given that the transparent regions of src are black with an alpha value of 0.00)


(Rs * Sr + Rd * Dr, Gs * Sg + Gd * Dg, Bs * Sb + Bd * Db, As * Sa + Ad * Da) // - All channels should be treated the same.
// My resulting colour in the transparent region of src, on the grass (Let's say it's 0.5, 0.5, 0.25)
 
// would be
(0.00 * 0.50 + 0.50 * 0.00, 0.00 * 0.50 + 0.50 * 0.00, 0.00 * 0.25 + 0.25 * 0.00, 0.00 * 1.00 + 1.00 * 0.00)
// effectively
(0.00, 0.00, 0.00, 0.00) // - which as i see it should be transparent. It comes out black, however.

I read somewhere that it's just not doable with ordinary blending, and I guess a nice thing to have here would be a
per-channel glBlendFunc. After not getting the desired result, I immediately resolved to shotgun surgery and tried out all combinations, with no luck.

If I cannot manage to do this, I guess I'd just leave this part as it is, it's not a gamestopper, but it does irritate me a bit. :-)

These images should illustrate what I'm going for, - and I must admit, i haven't tried with actual textures yet,
I merely change the alpha component when setting glColour4f(). I expect the result to be similar with texturing.


Src. (What I draw to the colour buffer)

src_small.png

Dst. (What's already in the colour buffer)

dst_small.png

Desired. (I'd really like to see this result)

wanted_small.png

Achieved. (I'm getting this result. sad.png )

unwanted_small.png

Have any of you been past this? I guess a common use for texture multiplications would be for lightmaps,
and here using alpha probably won't make much sense, as revealing more of the previous layer only

means approaching a white lightmap value (at which point the surface appears unlit).

I intend to use this for my GUI, however. For simple overlay effects, dialogs and HUDs. (Should become nice and generic. smile.png )

Without lerping between any colour and white, I'd like to be able to have the overlayed GUI element fade out, while still keeping the effect.

Say, I have a red light filter taking up the entire screen, resembling the player having been shot, fading out slowly, and things gradually come back to their original colours.

Advertisement

I don't think you can achieve the effect you want by tinkering with the blend function. I think instead you need to modify your fragment shader or tex env (depending on whether you're shader or fixed function respectively) so that as the alpha channel goes toward zero, your RGB lerps up toward one.

You could try turning on alpha testing to discard the 'transparent' areas.

I guess a nice thing to have here would be a per-channel glBlendFunc.

By default you get one function for RGBA, but with glBlendFuncSeparate you get one for RGB and one for A... which doesn't actually help you, because what you want is the RGB blend function to take into account 3 factors -- src.RGB, src.A and dst.RGB -- which as mentioned by C0lumbo, would have to be done partially in the pixel shader.

(0.00, 0.00, 0.00, 0.00) // - which as i see it should be transparent. It comes out black, however.

This is the pixel value that's being written into the frame-buffer, not one that's being blended with the frame-buffer value (you already blended something with that value - 'D' - and are now writing out the final result).

Src. (What I draw to the colour buffer)
src_small.png

That picture is a red herring. The checkerboard pattern tells us that the area is 'transparent', so hopefully that means the alpha channel in that area is 0... but what values do the RGB channels have in those areas? Most image editors don't let you know, which is fine for most digital art, like web-pages/etc, but isn't fine for games. As you're seeing, the RGB values of transparent pixels have a big effect on your code. If your image editor has a mode where you can edit an alpha channel without that checker-board pattern appearing (so you can still set the appropriate RGB values in transparent areas), then I recommend you use this mode. In your case, painting the transparent areas white would fix your issue (and an alpha channel isn't actually required at all).

I was also going to point out that the OP should just remove the alpha channel from the source texture, but on a careful rereading of his post, I realised the actual problem is not that he wants his texture to have alpha, but that he wants to be able to use vertex alpha to dynamically fade out his overlays.

Actually writing that has made me think of another solution which doesn't require pixel shader or texenv changes. First, invert your texture's RGB values (offline, ideally in your export step if you have one), then instead of using vertex alpha to fade out, you put your transparency value into the vertex RGB, then use a blend mode like (GL_ZERO, GL_ONE_MINUS_SRC_COLOR). That should achieve your desired effect.

Probably simpler to modify your pixel shader though :)

I don't think you can achieve the effect you want by tinkering with the blend function. I think instead you need to modify your fragment shader or tex env (depending on whether you're shader or fixed function respectively) so that as the alpha channel goes toward zero, your RGB lerps up toward one.

Yeah, that's what I thought... I may have to go with just remembering that I just get all channels multiplied without involving 3 values per blend,

as Hodgman knowingly points out that I would need.

You could try turning on alpha testing to discard the 'transparent' areas.

I guess a nice thing to have here would be a per-channel glBlendFunc.

By default you get one function for RGBA, but with glBlendFuncSeparate you get one for RGB and one for A... which doesn't actually help you, because what you want is the RGB blend function to take into account 3 factors -- src.RGB, src.A and dst.RGB -- which as mentioned by C0lumbo, would have to be done partially in the pixel shader.
>

(0.00, 0.00, 0.00, 0.00) // - which as i see it should be transparent. It comes out black, however.

This is the pixel value that's being written into the frame-buffer, not one that's being blended with the frame-buffer value (you already blended something with that value - 'D' - and are now writing out the final result).

Thanks for informing me of glBlendFuncSeparate - I didn't know that existed. I completely forgot that I was setting the value in the buffer rather than setting what I blend with, it does make things a little more complicated. - At least with a simple approach. Also, yeah, I might have to resort to changing my shader.

It just doesn't become as general as I had wished.


Src. (What I draw to the colour buffer)
src_small.png

That picture is a red herring. The checkerboard pattern tells us that the area is 'transparent', so hopefully that means the alpha channel in that area is 0... but what values do the RGB channels have in those areas? Most image editors don't let you know, which is fine for most digital art, like web-pages/etc, but isn't fine for games. As you're seeing, the RGB values of transparent pixels have a big effect on your code. If your image editor has a mode where you can edit an alpha channel without that checker-board pattern appearing (so you can still set the appropriate RGB values in transparent areas), then I recommend you use this mode. In your case, painting the transparent areas white would fix your issue (and an alpha channel isn't actually required at all).

No it isn't. I composed it myself, and I'm very well aware that the RGB values are in fact all 0's in the transparent regions. I read those out before trying to create a visual for you guys, but I guess I should've written "known that..." rather than "given that..." in my original post.

Also, although I stated that I do colouring on a vertex level with glColor4f (C0lumbo noted that in his later post), I've mislead you into thinking in textures by creating those images.

I should've stuck to drawing sharp rectangles on my sample images. I just wanted to explain myself visually. :-/

I was also going to point out that the OP should just remove the alpha channel from the source texture, but on a careful rereading of his post, I realised the actual problem is not that he wants his texture to have alpha, but that he wants to be able to use vertex alpha to dynamically fade out his overlays.

Actually writing that has made me think of another solution which doesn't require pixel shader or texenv changes. First, invert your texture's RGB values (offline, ideally in your export step if you have one), then instead of using vertex alpha to fade out, you put your transparency value into the vertex RGB, then use a blend mode like (GL_ZERO, GL_ONE_MINUS_SRC_COLOR). That should achieve your desired effect.

Probably simpler to modify your pixel shader though smile.png

You're right. I've been stubborn changing the glBlendFunc around believing I could pull off the trick. I see your point with the inversion of the colours...

This topic is closed to new replies.

Advertisement