预乘alpha合成

时间:2011-02-01 20:36:25

标签: c# graphics colors

我正在尝试实现预乘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混合;或者这是预期的行为?

谢谢。

4 个答案:

答案 0 :(得分:4)

预乘的原因是因为在将源图像添加到目标之前,它实际上最终会对目标的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 Pre-Multiplied扩展:

进行比较
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值的像素获得的目标像素数比未预先相乘时更多,这种情况发生在指数上规模。

我希望这可以解决它。

答案 1 :(得分:2)

您遇到的问题取决于您是否将源alpha值预先乘以预乘的一部分。如果你这样做,那么你在目标乘法中使用的srcA就是真实源Alpha的平方,所以你需要取平方根进行计算:

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

如果你没有预先乘以它(我认为更有可能),你需要自己乘以得到与工作结果相同的结果:

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

答案 2 :(得分:1)

一个疯狂的猜测:你是否改变了混合量(srcA)?如果是这样,您必须重新计算位图中的预乘Alpha值。如果不这样做,您将获得类似于添加的效果,这可能就是您所描述的。

答案 3 :(得分:0)

经过多次尝试,这就是我想出的:

我也预先乘以alpha通道,我保留了我的第二个公式,我先发布了;这是我得到的最好的结果。

在我发现的最好的文档中,当预乘时,丑陋的边界消失了: http://www.td-grafik.de/ext/xfrog/alpha/index.htmlhttp://blogs.msdn.com/b/shawnhar/archive/2010/04/08/premultiplied-alpha-in-xna-game-studio-4-0.aspx

嗯,传统的alpha混合并不是真正的比较参考,我想我现在就是因为它看起来比普通的alpha混合更好。

但说实话,我真的不明白100%,(看起来)它有效;另一个谜......

这就是让我觉得没关系的原因;

左:alpha混合; 右:预乘

谢谢大家的帮助!