alphaBlendOperation = Max/Min Fails

The rgbBlendOperation values for Min/Max work, however when changing alphaBlendOperation to MTLBlendOperationMax or MTLBlendOperationMin, they have no effect.


It appears like the alpha channel just gets ignored.


However, I can successfully set MTLBlendFactorSourceAlpha to sourceRGBBlendFactor, and MTLBlendFactorOneMinusSourceAlpha to destinationRGBBlendFactor.


I basically have a stamped paint stroke made of transparent airbrush looking circles. My thought was that I should be able to set alphaBlendOperation to Max, and it would pick the least transparent pixel from the source or destination.


I was able to achieve this effect successfully using the Max value for rgbBlendOperation, so I know that my premise is correct, however when trying the same technique, alphaBlendOperation appears to not be functional at all.


I have taken two screenshots. The first is how it should look (I achieved this without the alpha channel using rgbBlendOperation = Max) The second screenshot is what it looks like when I try to use alphaBlendOperation = Max. The alpha is ignored and instead I just get a bunch of squares stamped along the drawing stroke.


https://imgur.com/a/Wgqhw


https://imgur.com/a/gtaSD


I've double checked all the settings I could find, and everything appears to be set correctly, is this a bug with alphaBlendOperation?

Accepted Reply

The answer is to set the CAMetalLayer's opaque property to false. Without this, the alphaBendOperation will do nothing.

Replies

I'm new to Metal and not 100% sure about this, but the docs for `.max` say: A = max(Source.a, Dest.a). So it's going to set alpha=1 in your result pixels, because I'm assuming your background (Dest) is opaque so it has a=1.


I'm working on paint strokes too and haven't quite got it figured out yet. But you might want the `rgbBlendOperation` to be `.add`, and then you set the blend factors to `.sourceAlpha` and `.sourceOneMinusAlpha`, or `.one` and `.sourceOneMinusAlpha` if the source (ie, the blue paint) is pre-multiplied. That will mix the color, based on the alphas. The pre-multiplied alpha from CGImages is what's messing with me at the moment. It can create dark edges on the paint strokes.


I don't know about `.max` for `rgbBlendOperation`. If you try a white background I'm guessing it will pick that white instead because it's a higher value than the paint color.


Rob


ps. this HTML editor is some weird ****. It inserts odd font size changes all over the place...

Setting the rgbBlendOperation is what I described in my original post, and that operation works like I mentioned previously, but it is not the effect that I am after. Using max, if it worked properly, would give different results.


When the texture is loaded, I assume that the alpha value would be zero. Even if it's not, I just tried using the replaceRegion function on the MTLTexture, however the alpha bytes have no effect, while the rgb bytes correctly influence the MTLTexture.


Perhaps an apple employee can shed some light on this, as I do not see this documented anywhere.

You wrote: "I should be able to set alphaBlendOperation to Max, and it would pick the least transparent pixel from the source or destination."


By "pixel", do you mean "alpha value of the pixel"? The way I read that sentence it sounds like you mean "the whole pixel, including rgb values".

In the default pixel format, the Metal Texture is a big array of 8 bit integers. The values range from 0-255. So in bgra, the first byte is blue, second byte is green, third byte is red, and fourth byte is alpha. The fifth byte is the next pixel's red value, and so on.


All my values begin at 0, which means a black color that is fully invisible. When a shader sets the alpha byte with the max alpha blend mode, it's supposed to look at the alpha value currently in the Metal Texture array and compare it to the new value being set. If the new value is larger than the value already in the Metal Texture, it should change the value in the Metal Texture to be this new value. If the new value is less, then it should not change the value in the Metal Texture.


This is how it is supposed to work, and the min blend operation is the reverse, meaning that if the new value is smaller, then it will change the value in the Metal Texture (You would have to start with all the values in the Metal Texture being 255 or atleast higher than 0 for it to do anything)


The trouble is, that this does not happen.

The answer is to set the CAMetalLayer's opaque property to false. Without this, the alphaBendOperation will do nothing.