Archive for 2023年9月

Neither α nor Pre-multiplied

2023/09/26

在图像处理和图形学里 α-blending 是频繁被提到的概念。下图是 s 色层和 t 色层的 α-blending 过程示意。习惯称 s 色层在 t 色层「之上」。

和其它数学方法一样,可以视其为纯粹的 artificial 定义,由应用环境赋予其意义。但是我们也希望它能自然而然地表示某种物理意义,从而为它被广泛引用找到合理性。

上面的定义中,αs 的取值范围是 [0, 1]。为了分析合理的物理意义,先看取值范围的上界 αs = 1,代表 s 色层完全不透明。这时 α-blending 退化成实际世界中简单的「遮盖」现象。现在把 αs 减小。那么 t 色层会对 blending 结果贡献 Ct(1-αs)。这时我们可以理解为 s 色层有了 (1-αs) 的「透光度」,而自身的颜色减小为原来的 αs 倍。这里既然提到了色层的「自身颜色」,就解释一下物体颜色的物理意义。

颜色的意义

首先假设读者有 radiometry 的概念(如果没有也关系不大,很多东西可以非严格地直观理解)。Radiometry 的基本概念中,颜色是光线的属性,是光线的频谱能量分布,而非物体属性。当我们说「物体的」颜色,有两种情况:一是自发光物体,二是指物体表面的反射系数。后者真正呈现的颜色是整个 physically-based rendering 的话题,这里只非常简单地设定色层只有漫反射系数 [1],在平行白光下 [2] 呈现的颜色在计算上类似自发光物体。

现在来看这种设定下 α 的意义。可以直观地认为光从色层的一面射入后有 (1-α) 的部分透过色层从另一面射出 [3]。我们设定这样一个场景:有一个完全不透明 (α = 1) 的色层,其漫反射系数为 C [4]。通过某种方式让它的 α 变为小于 1 的一个正数,其透明度变为为 (1-α)。那么问题在于是否有一种「自然合理」的基于单个 α 参数的物理模型,让它的漫反射系数同时变为 Cα。如果存在这种自然合理的模型,α-blending 的定义就更有意义。

统一 α 的困境

我们试图设计这样一个模型。首先假设半透明色层光学上「各向同性」,也就是说发生在其两个表面的光的作用完全相同。那么,s 色层能透过的 (1-αs) 的光就意味着它反射的光量减少到不透明色层的 αs 倍,等同于漫反射系数降低为 Csαs。这个分析合理吗?

我们先回顾 αs = 1 的完全不透明情况,这时反射系数 Cs 意味着色层「吸收了」(1-Cs) 部分的入射光线能量。那么,当某种方式让 αs 减小的时候,透过色层的光线不必非要从反射光线的衰减得到,也可以是部分的甚至全部的由避免「吸收」光线得到。所以,在直观的物理模型中,用 Csαs 表示半透明色层的漫反射系数并不能代表自然的物理意义,因为色层的漫反射系数和其透明度并没有单一的对应于 αs 的相互关系。完全可以说,半透明色层的漫反射系数应该是一个独立于 Cs 和 αs 的本源参数。

Pre-multiplied α-Blending

即便 α-blending 不能自然地符合某种简化的物理意义,能不能退一步说这不是个大问题?因为最简单的选择是把它完全当成一种「艺术效果」就好了。是吗?让我们看看下面的情况。

比如说,在软件中表示 s 和 t 两个图层 [5],它们分别有 C 和 α 属性(称为 channel)。这时在屏幕上要把它们缩小为原尺寸的 1/3 然后按照 α-blending 重叠。从 α-blending 的数学定义可以看到,要正确 render 这种效果,有两种方法。一种是先按照原大小进行 blending,然后再 resize 结果。这种方法不但效率低,更严重的问题是它没办法处理两个色层有不同的偏移,旋转,和缩放比例的情况。另一种情况是对两个色层分别按照 screen 坐标进行 resample 之后进行「余下对应的」α-blending。但是这时要注意的是,为了结果正确,对 s 色层进行 resample 时,不能简单的对 Cs 和 αs 分别进行。而是需要对 Csαs 整体 resample。这个需求导致了实现方面的困难。今天的计算机架构中图形硬件对色层的通常实现是 texture,texture resample 的硬件实现是对各个 channel 分别独立进行。为了适应这种硬件约束,图形软件发展了一种概念,texture 的 color channel 中存储 Cα 的乘积而不是原来的 C,对此我们称为 C’。而「余下对应的」α-blending 变成:

这种方式称为 pre-multiplied α-blending。对应的 texture 的存储方式称为 pre-multiplied α channels。

从上面两节可以看到,已经存在自然的简化物理模型和硬件实现约束这两种情况大大削弱了 Csαs 中两个因子各自的意义。取而代之的是一个整体的 C’s。这里 C’s 或者可以看成是一个本源的物理属性,或者依然可以看成 Cs 和 αs两个因子的乘积。我们要注意的是,如果采用后一种解释,C’s 必须小于 Csαs,它的最大上限不能超过 1。

下文的讨论中我们会发现,从物理现象和硬件约束来看,对 C’s 的解释一般选用本源属性更加贴切。

真实物理意义

图层的 blending 效果其实很少局限在「半透明薄片」或者「纯艺术效果」这类设定,而是常用来模拟更真实的物理场景。下面例子中的两个图层,下面的是真实场景,上面的是 3D rendering 的结果。它们的 blending 结果包含四种物理场景。

这四种场景都符合下面的一般公式,只需要取不同的参数。而这个公式「恰好」和上面的 pre-multiplied α-blending 形式相同。

简单遮盖不用太多讨论。半透明遮盖很类似前面讨论的薄片色层。它对背景颜色的减去 αs 倍。但前景 s 图层的颜色 C’s 不再仅仅是漫反射参数,而是包括了 specular 反射的 rendering 结果。真实半透明物体最终反射的光线的颜色可以是任意正数 (超过屏幕动态范围的部分会被最终 clip)。阴影是对背景图层颜色的降低,降低程度为 αs。阴影部分前景图层的颜色 C’s = 0。反射是对背景图层颜色的增加。因为对背景颜色没有降低所以 αs = 0。前景图层 C’s 是反射的 rendering 结果。同一区域有可能同时拥有半透明、阴影和反射效果。

在这四种情况下,虽然图层 blending 的公式和 pre-multiplied α-blending 相同,但是意义相差很大。因为:

  • 在这个例子的 blending 中 C’s 的取值范围是任意正数。而 pre-multiplied α-blending 对C’s 的取值限制 [0, Csαs],上限为 1 [6]。
  • 在这个例子的 blending 中 C’s 没有对应任何 Csαs 乘积的意义。

避之不及的 α 术语

总结上面的讨论就是,实际常用的图层 blending,特别是模拟真实场景效果的 blending 符合公式:

尽管该式和 pre-multiplied α-blending 形式相同,但是它的意义完全不同于后者。

如此说来,就给这种 blending 起个名字,比如叫 r-blending。然后在应用它的场景不再提及 α-blending 就好。是的,这是我写这篇 blog 想说的第一个提议。但这个避免提及 α 的提议的第一个问题是毕竟公式里依然存在 α。也许依然有人想把它归为 α-blending 的变种(但我不属于这个人群)。对此我要说,至少不要让这种形式和 pre-multipilied 这个词有任何瓜葛。

但是,即便如此,还有一个绕不过去的生态问题。大多数用户进行图层 blending 的工具是 Photoshop(或者完全遵循 Photoshop 概念的类似工具)。Photoshop 只支持 α-blending。它并不支持我们说的 r-blending,它的内部数据表示也不支持 pre-multiplied α。Photoshop 唯一处理 pre-multiplied α 的地方是 file import,它会把所有标为 pre-multiplied α 的文件的 color channels 在 import 时转换为 unmultiplied 的内部表示。

所以一个工具要生成能在 Photoshop 里进行 r-blending 的图层就要做些特殊处理。比如上面的 rendering 结果,虽然和 pre-multiplied 没有任何关系,但是为哄骗 Photoshop 去做我们想要的工作,存储这个 rendering 结果到文件的时候要把它 tag 成 pre-multiplied-α 数据。这样 Photoshop 在打开文件的时候,会把每个 color channel 除以 α-channel [7]。然后 blending 的时候再乘回去。前后做了一些无用功但是结果也还大致正确 [8]。但并不总是。

因为上面提到,renderer 的结果会有以下两种情况:

  • α-channel 为零。比如反射现象。在这种情况下,Photoshop 本来的做法会导致除零错误。所以 Photoshop 在这种情况下会直接把 color channel 设置为零。
  • 颜色比 α 大,这违反了前面提到的 pre-multiplied-α 对数据上界的限制。这种情况下二者的商大于一,Photoshop 把内部图层的 color channel 截取为 1,在 blending 的时候会乘以 α 值,最后的结果低于正确值。

没有办法能生成一个图层让 Photoshop 完成 r-blending 的功能。所以变通方法是生成第三个图层,使用 Photoshop 的 Add blending 模式(Linear Dodge),把上面丢失的 color 部分补回来。

上面左图是 rendering 结果被 Photoshop 当作 pre-multiplied-α 数据调入并转换为 unmultiplied 之后的数据丢失结果。右图是把 unmultiplication 转换中丢失的信息变成一个 Add-blending 图层。作为参考,这里附上生成 Add-blending 图层的 shader:

结论

虽然 α-blending 最原本的定义非常简单,但是每当在实际案例中涉及它的讨论总会有各种各样的 confusion。这篇 blog 从最原本定义入手。其目标是探讨一下这些 confusion 的来历。

最开始的讨论可以看出,α-blending 的原本定义并不符合简单的物理模型。目前的图像硬件的常用算法也不能和 α-blending 很好的兼容,为此提出了 pre-multiplied-α 这样凑合的方案。这就提出了一个疑问,这样一个充满缺陷的定义如何能得到广泛应用?答案是,其实得到广泛应用的并不是 α-blending,α-blending 只是以 Photoshop 为代表的各种软件的误导名词。

很多具有物理意义的场景其实采用的是我们命名为 r-blending 的方法。因为 r-blending 的公式恰好和 pre-multiplied-α blending 形式相同,所以它的应用经常调用硬件和系统库中叫做 pre-multiplied-α 的方法。但是在这种场景中引入 α-blending 和 pre-multplied 这两个概念没有任何好处,只能增加 confusion。

而重量级的工具比如 Photoshop,依然顽固的只采用 α-blending 概念。习惯的力量如此强大,以至于 α-blending 和 pre-multiplication 依然在制造 confusion 和不必要的工作。

脚注:

  1. 称为理想的 lambertian 表面。
  2. 由于只有漫反射,光源的方向不重要。
  3. 在此设定下 α 并非微分属性,而是整体的体积属性。对于类似薄片的「色层」来说,这是可以接受的简化。
  4. C 是频谱的函数。
  5. 从这里开始我们用「图层」来代替「色层」,因为我们的讨论的例子从一开始的单色层转变成接近实际的每个 pixel 颜色不同的图像。
  6. 在声称支持 pre-multiplied α-blending 的系统中,如果图层的 C’s 违背了小于 αs 有时会导致系统无法正常工作。下文讨论会提到。
  7. 可以称为 unmultiplication。
  8. 考虑到 Photoshop 大多数处理还是基于 8-bit,这种反复的乘除的精度损失也不小。