2
$\begingroup$

Forgive me, I dont really understand the basis of alpha blending. Like I know the formula but when it comes to the terms blending operation, source blend factor, and destination blend factor I am absolutely lost.

The goal is to do additive blending like in photoshop so black items would be invisible and items with any color would add their color resulting in an effect that pixels with many layers underneath are close to white.

The problem is I have no clue how to translate this to metal. For example this is how I am currently doing alpha blending (although this currently isnt working for whatever reason). What should I be doing for additive blending?

descriptor.colorAttachments[0].blendingEnabled = YES;
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
$\endgroup$
  • $\begingroup$ What is stored in the frame buffer or what is the clear color? $\endgroup$ – Matthias Aug 27 '18 at 17:04
  • $\begingroup$ The clear color is black. What do you mean what is stored in the frame buffer? $\endgroup$ – J.Doe Aug 27 '18 at 17:23
  • $\begingroup$ You could already start with some kind of image generated during a previous pass. $\endgroup$ – Matthias Aug 27 '18 at 17:30
  • $\begingroup$ So you basically want to use the following blend equation dst.rgba <- src.rgba + dst.rgba? The more non-black draws to a pixel, the larger the pixel color becomes (i.e. goes to white). $\endgroup$ – Matthias Aug 27 '18 at 17:32
  • $\begingroup$ @Matthias Correct! $\endgroup$ – J.Doe Aug 27 '18 at 17:34
4
$\begingroup$

The “blend factors” are the amount that the specified component is multiplied by while blending. “Destination” is the existing content that’s being blended “on top of”, while “source” is the new content that’s being blended “onto” it. When the blend operation is set to “add”, the final pixel value is calculated like this:

value = source * sourceBlendFactor + destination * destinationBlendFactor

…so a blend factor of “one” means the value is used unchanged (multiplied by 1), “[x]Alpha” means the value is multiplied by the alpha value of x (source or destination), and “oneMinus[x]Alpha” means the value is multiplied by (1 - the alpha value of x).

For additive blending, you most likely want the following:

Objective-C:

descriptor.colorAttachments[0].blendingEnabled = YES;
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;

Swift 5:

descriptor.colorAttachments.colorAttachments[0].isBlendingEnabled = true
descriptor.colorAttachments.colorAttachments[0].rgbBlendOperation = .add
descriptor.colorAttachments.colorAttachments[0].alphaBlendOperation = .add
descriptor.colorAttachments.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
descriptor.colorAttachments.colorAttachments[0].sourceAlphaBlendFactor = .one
descriptor.colorAttachments.colorAttachments[0].destinationRGBBlendFactor = .one
descriptor.colorAttachments.colorAttachments[0].destinationAlphaBlendFactor = .one

There’s a really helpful tool for visualizing the different options here—it’s written for OpenGL, but the Metal stuff works the same way.

| improve this answer | |
$\endgroup$
  • $\begingroup$ In Metal there is a blend factor of sourceColor as well as one called "one" whaw would source color look like? Would that just be the color squared or something? $\endgroup$ – J.Doe Aug 27 '18 at 17:47
  • $\begingroup$ Yeah, exactly. There’s some interesting things you could do with that, but I haven’t seen it used much. You can make a “multiply” blend mode by setting the source blend mode to “destinationColor” and the destination one’s to “zero” (or vice versa). $\endgroup$ – Noah Witherspoon Aug 27 '18 at 17:52
  • $\begingroup$ why do you set sourceRGBBlendFactor to BlendFactorSourceAlpha. Is this an error? $\endgroup$ – J.Doe Aug 28 '18 at 6:38
  • $\begingroup$ No—it allows you to modulate the amount of color being added with the pixel’s alpha value, just like changing a Screen layer’s opacity in Photoshop. $\endgroup$ – Noah Witherspoon Aug 28 '18 at 15:14
1
$\begingroup$

Terminology

  • The source color is the value written by the fragment/pixel shader.
  • The destination color is the color from the image currently in the frame buffer (that will be overridden).

Additive blending

// The blend formula is defined as:
// (source.rgb * sourceRGBBlendFactor  )   rgbBlendOperation (destination.rgb * destinationRGBBlendFactor  ) 
// (source.a   * sourceAlphaBlendFactor) alphaBlendOperation (destination.a   * destinationAlphaBlendFactor)
// <=>
// (source.rgba * 1) + (destination.rgba * 1)

descriptor.colorAttachments[0].blendingEnabled             = YES;
descriptor.colorAttachments[0].rgbBlendOperation           = MTLBlendOperationAdd;
descriptor.colorAttachments[0].alphaBlendOperation         = MTLBlendOperationAdd;
descriptor.colorAttachments[0].sourceRGBBlendFactor        = MTLBlendFactorOne;
descriptor.colorAttachments[0].sourceAlphaBlendFactor      = MTLBlendFactorOne;
descriptor.colorAttachments[0].destinationRGBBlendFactor   = MTLBlendFactorOne;
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;

If the clear color is equal to {0.0f,0.0f,0.0f,_} and we draw {0.1f,0.1f,0.1f,_} to the same pixel a couple of times, the pixel color eventually becomes {1.0f,1.0f,1.0f,_} (white). Drawing {0.0f,0.0f,0.0f,_} (black) has no effect. We do not care about the alphas.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Can you explain blend factors. Why is the factor one rather than source color? $\endgroup$ – J.Doe Aug 27 '18 at 17:41
  • $\begingroup$ Since you said in the comments above that you want the following blend equation: dst.rgba <- src.rgba + dst.rgba which basically adds the new color to the old one (so you multiply by 1)? $\endgroup$ – Matthias Aug 27 '18 at 17:43
  • $\begingroup$ So the factor is just a scalar on the RGBA values before they are added? $\endgroup$ – J.Doe Aug 27 '18 at 17:44
  • $\begingroup$ @J.Doe It could also be a vector: you can multiply dst.rgba with src.rgba for instance and you can also multiply with a constant color. But for additive blending, you just want to do an addition. $\endgroup$ – Matthias Aug 27 '18 at 17:46

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.