解释预乘alpha的工作原理

5
有人能否解释一下,为什么使用预乘 alpha(和更正的混合函数)进行渲染时,看起来与“正常”alpha不同,而从数学上讲,它们是相同的?
我查阅了这篇文章以了解预乘 alpha:http://blogs.msdn.com/b/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx 作者还说最终计算结果相同:
“查看传统和预乘 alpha 的混合方程。如果将此颜色格式转换代入预乘混合函数中,则会得到传统混合函数,因此两种方式都会产生相同的最终结果。不同之处在于,预乘 alpha 将(源.rgb * 源.a)计算作为预处理,而不是在混合硬件内部执行。”
有什么遗漏吗?那么结果为什么不同呢?

你需要在使用预乘混合时使用预乘内容,在非预乘混合时使用非预乘内容。否则会得到错误的结果。如果你使用正确的内容和正确的方程式,你将得到正确的行为。 - fadden
为了获得帮助,您应该提供有关迄今为止所获得结果差异的更多信息。 - tonso
我实际上并不是想修复错误或其他什么。在使用它之前,我实际上想先理解它,但我发现网络上的信息有点混乱。这就是为什么我发布了这个问题。我还想知道是否可以在运行时实现相同的效果,而无需预乘资源以及在资源加载期间进行修改... 有什么有用的资源吗? - neshone
链接失效了。 - Andreas detests censorship
3个回答

7

区别在于过滤方式。

假设你有一张只有两个像素的纹理,并且你在这两个像素的中间精确采样。同时假设使用线性过滤。

Schematically:
R|G|B|A + R|G|B|A = R|G|B|A
non-premultiplied:
1|0|0|1 + 0|1|0|0 = 0.5|0.5|0|0.5
premultiplied:
1|0|0|1 + 0|0|0|0 = 0.5|0|0|0.5

注意绿色通道的差异。 过滤预乘alpha会产生正确的结果。

请注意,所有这些都与混合无关。


我不理解你的例子,为什么要将不同颜色0100和0000相加,结果显然会不同。 - frankelot
2
@frankelot 这是因为0|1|0|0和0|0|0|0看起来(对人类而言)是相同的颜色:完全透明。 我们将0乘以1(alpha乘以原始绿色)以获得预乘绿色,即0。 - Jack G

2
这只是一个猜测,因为目前没有足够的信息来解决问题。
应该是相同的。获取不同值的一种常见方法是在预乘和渲染步骤之间使用不同的Gamma校正方法。
我猜你的其中一个阶段,无论是混合还是预乘阶段,都使用了不同的Gamma值。如果您使用像DirectXTex texconv这样的工具生成带有预乘Alpha的纹理,并使用默认的srgb选项,则您的采样器需要是_SRGB格式,渲染目标也应该是_SRGB。如果您将它们视为线性,则可能无法渲染到_SRGB目标或使用Gamma校正对纹理进行采样,即使您在采样的着色器中进行了预乘(取决于3D API和渲染目标设置差异)。这样做会导致中间色调的Alpha值在两种方法之间显着不同。
请参见:线性的重要性
如果您是在Photoshop中生成Alpha通道,则应该知道几件事情。Photoshop不会以线性或sRGB格式保存Alpha通道。它会将其保存为介于线性和sRGB之间的Gamma值。如果您在Photoshop中进行预乘,它将正确计算预乘,但使用错误的斜坡保存结果。如果您生成普通的Alpha通道,然后在3D API中以sRGB或线性方式进行采样,则会接近但不匹配Photoshop在任一情况下显示的值。
对于更详细的回复,我们需要的信息是:
  • 您使用的3D API。
  • 您如何生成和采样纹理
  • 何时以及如何进行预乘Alpha。
  • 最好提供一个显示错误的代码或着色器示例。

1

我正在研究为什么要使用Pre而不是非Pre,并从Nvidia找到了这个有趣的信息。

https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre

使用 Pre-Alpha,似乎可以更精确地处理他们的特定情况,而比使用 Post-Alpha 更好。

我也读到过(我相信是在这里但找不到了),使用预先 Alpha(即将 Alpha 乘以每个 RGB 值),可以节省时间。我仍然需要找出这是否正确,但似乎有理由更喜欢使用预先 Alpha。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接