预乘alpha合成
我正在尝试实现预乘alpha混合。 在此页面上: 什么是颜色混合? ,他们确实解释了标准的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进行平方
例如。 如果没有预先乘法,我们会得到源图像数据:
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值的像素获得的目标像素数比不预先乘法时更多,这是以指数级进行的。
我希望这能把它搞清楚。
您遇到的问题取决于您是否将源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);
一个疯狂的猜测:你改变混合量(srcA)? 如果是这样,您必须重新计算位图中的预乘Alpha值。 如果不这样做,您将获得类似于添加的效果,这可能就是您所描述的。
经过多次尝试,这就是我想出的:
我也预先乘以alpha通道,并保留我的第二个公式,我先发布; 这是我得到的最好的结果。
在我发现的最好的文档中,当预乘时,丑陋的边界消失了: http : //www.td-grafik.de/ext/xfrog/alpha/index.html和http://blogs.msdn.com /b/shawnhar/archive/2010/04/08/premultiplied-alpha-in-xna-game-studio-4-0.aspx
好吧,传统的alpha混合并不是真正的比较参考,我想我现在是因为它看起来比普通的alpha混合更好。
但说实话,我并不真正了解它的100%,(看起来)它有效; 另一个谜……
这就是让我觉得没关系的原因;
左:alpha混合; 右:预乘
感谢大家的帮助 !