Blending mode for semi-transparent texture and transparent buffer

Hello everyone!

I have a transparent RenderTarget2D, that is, with the background color Color(0, 0, 0, 0). (As you can immediately notice, this transparent color has black color and full transparency).

There is also a semi-transparent texture (Color(255, 255, 255, 128)) that is drawn on this render target. That is, a white texture with half transparency.

Expected:

That as a result of mixing there will be an image of the original texture, as if it was not mixed with anything. Because the buffer is transparent. Picture + transparent color = picture.

Problem:

Blending takes into account the backbuffer color, which is black, and blends half of it into the result.

Attempts:

Here’s an example of setting up blending:

new BlendState(){
    ColorSourceBlend = Blend.SourceAlpha,
    AlphaSourceBlend = Blend.One,
    ColorDestinationBlend = Blend.InverseSourceAlpha,
    AlphaDestinationBlend = Blend.One,
    ColorBlendFunction = BlendFunction.Add,
    AlphaBlendFunction = BlendFunction.Add,
};

As you can see, the contribution of the buffer color to the resulting color is equal to “1 - alpha channel of the texture”. This would be correct if the buffer had no transparency. That is, its color would contribute its 50% to the result, and we would see color mixing. This works well if something has already been drawn on this buffer. Then the two textures blend well (and this feature should be preserved).

There should be an opportunity to make something like this:
ColorDestinationBlend = Blend.InverseSourceAlpha * DestinationAlpha

“Layers” of this kind mix well in all graphics programs and browsers. Accordingly, I can optimistically assume that this is something trivial.

What blending setting should I use to get the right result?

This seems to be working as exactly as you’ve defined it.

Thank about it mathematically, and only for the one channel.

(sR x sA)+(dR x dA)=resultR
sR=255(from white)
sA=0.5 (from 128)
dR=0 (from black)
dA=0.5 (from 255-128)

255 x 0.5 + 0 x 0.5=128=darker than original

So my intuition is that because you want the backbuffer alpha to matter so much, you want your Source and Dest blend to based off that:
ColorSourceBlend = Blend.InverseDestinationAlpha
ColorDestinationBlend= Blend.DestinationAlpha
AlphaBlendFunction=BlendFunction.Add

This approach requires you to be very careful about the order you render things, and will also start to violate your intuition about what “transparency” means very quickly.

Thanks for the answer.

I tried to apply these settings, and everything is filled with solid color. Perhaps I am too far from understanding.

Before these, I tried the usual BlendState.NonPremultiplied, but it draws strange edges around objects. Where textures have smooth anti-aliasing.

I don’t need anything special. Just draw a lot of PNG images on a transparent buffer without artifacts around the images. This is just a stack of PNG images.

If their transparent pixels are as hard as GIFs, there is no problem. As soon as semi-transparency appears, I cannot find a way to draw them normally and then save this stack in one PNG on the desktop, for example.

This is a simple task on the CPU.

To easily understand the problem, try to write a program in monogame that glues 10 PNGs without artifacts and without losing the alpha channel

These PNGs are loaded through the content pipeline? Did you check that premultiply alpha is turned off, that can cause edge artefacts as you describe if used with BlendState.NonPremultiplied.

Yes, images are from content pipeline, and all are “PremultiplyAlpha: False”.

Everything works with PremultiplyAlpha and BlendState.AlphaBlend. I read that this is a common problem with blending and there is no normal solution without PremultiplyAlpha. Sadness :slight_smile:

I don’t think this is doable in a simple draw using the available blend settings.
The blending options are very limited. Whenever you need something more complex, you have to do it in a custom shader.

You’re right.
I needed this for later combining with the regular render target (null).
I ended up using normal color mode (not premultiplied) and got a combined image (pie), incorrect in transparent places (blackish).
To save it to disk, it would require some kind of recovery to normal colors in semi-transparent areas.
But I found that this wrong image can be combined with the regular render target (null) with BlendState.AlphaBlend and resulting colors are good. Despite the fact that this image is not premultiplied. Its contribution to resulting brightness turns out to be the same as when premultiplying.