我正在尝试实现预乘alpha混合。在此页面上What Is Color Blending?,它们确实解释了标准Alpha混合,但没有解释预乘值。

Alpha混合:(源×Blend.SourceAlpha)+(目标×Blend.InvSourceAlpha)

根据公式,它转换为:

  a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8);
  r = ((srcR * srcA) >> 8) + ((tgtR * (255 - srcA)) >> 8);
  g = ((srcG * srcA) >> 8) + ((tgtG * (255 - srcA)) >> 8);
  b = ((srcB * srcA) >> 8) + ((tgtB * (255 - srcA)) >> 8);


它有效,显然...

现在如何将其转换为处理预乘值?

  a = ((srcA)) + ((tgtA * (255 - srcA)) >> 8);
  r = ((srcR)) + ((tgtR * (255 - srcA)) >> 8);
  g = ((srcG)) + ((tgtG * (255 - srcA)) >> 8);
  b = ((srcB)) + ((tgtB * (255 - srcA)) >> 8);


由于它已经被预乘,因此我在第一项中放弃了乘以...对!
但是结果是在alpha混合和加法混合之间,趋向于加法。最后,它看起来并没有太复杂。这可能是错误的,因为它看起来应该完全像经典的alpha混合。还是这种预期的行为?

谢谢。

最佳答案

预乘有效的原因是,在将源图像添加到目标之前,它实际上最终对目标的alpha进行平方。

例如。无需预乘,就可以得到源图像数据:

srcA = origA
srcR = origR
srcG = origG
srcB = origB


当应用于目标时,我们将得到的结果图像是这样的:

a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8)
r = ((srcR * srcA) >> 8) + ((tgtR * (255 - srcA)) >> 8)
g = ((srcG * srcA) >> 8) + ((tgtG * (255 - srcA)) >> 8)
b = ((srcB * srcA) >> 8) + ((tgtB * (255 - srcA)) >> 8)


对此进行扩展,我们得到:

a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8)
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8)
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8)
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8)


没有惊喜...

现在,对于预乘的源图像数据,我们得到:

srcA = (origA * origA) >> 8
srcR = (origR * origA) >> 8
srcG = (origG * origA) >> 8
srcB = (origB * origA) >> 8


当应用于目标时,它是:

a = (srcA >> 8) + ((tgtA * (255 - srcA)) >> 8);
r = (srcR >> 8) + ((tgtR * (255 - srcA)) >> 8);
g = (srcG >> 8) + ((tgtG * (255 - srcA)) >> 8);
b = (srcB >> 8) + ((tgtB * (255 - srcA)) >> 8);


好的,所以我们知道这一点,但是如果我们对此进行扩展,您将看到不同之处:

a = (origA * origA) >> 8 + ((tgtA * (255 – ((origA * origA) >> 8))) >> 8);
r = (origR * origA) >> 8 + ((tgtR * (255 - ((origA * origA) >> 8))) >> 8);
g = (origG * origA) >> 8 + ((tgtG * (255 – ((origA * origA) >> 8))) >> 8);
b = (origB * origA) >> 8 + ((tgtB * (255 – ((origA * origA) >> 8))) >> 8);


将其与以下项的NON预乘展开式比较:

a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8)
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8)
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8)
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8)


马上就可以看到,将origA值应用于目标时,我们将对它进行平方处理,这意味着将有更多的目标到达最终的颜色值。

通过平方,您说的是,我希望更多的目标得以实现。

这就是为什么在预乘时它会消除透明块周围的条带数量的原因,因为与Alpha值较低的那些像素相比,如果不进行预乘,这些像素将获得更多的目标像素,并且发生在指数级。

我希望这可以解决。

08-27 02:25