02 混合模式是什么,如何选择

前置知识:第 01 章的源颜色、目标颜色与 Alpha。
本章示例:四种混合模式同屏对照 · 完整源码。
预期画面:普通混合形成彩色薄雾,加法形成明亮火光,乘法与减色使背景变暗。

学完本章能做什么

你将能读懂混合方程里的源、目标与因子,知道 Three.js r174 四种常用模式的实际 RGB 规则,并能为烟雾、发光火焰和局部变暗效果选择起点。你还会知道为什么同一个 opacity 滑块对不同模式反应不同。

先看现象

打开示例。四格使用两枚部分重叠的软圆,中心色和分布相同。普通混合的圆像半透明烟雾;加法重叠处变亮甚至发白;乘法与减色在背景上形成暗色。把 opacity 从 0.65 向 0 拖:上面两格明显变淡,下面两格的中心强度却没有按同样方式变化。

这不是滑块失灵,而是 r174 内置混合模式所用的 RGB 因子不同。示例为乘法和减色准备了中性的边缘颜色;否则,单凭一张“RGB 不变、只有 Alpha 渐变”的贴图,可能看到整块矩形。示例还使用不向浏览器暴露 Alpha 的 WebGL 画布,避免浏览器把画布 Alpha 再与网页背景合成,干扰对 RGB 混合公式的观察。

1. 混合方程的两个乘数

一般形式是:

C结果 = 运算( F源 × C源, F目标 × C目标 )

F源 和 F目标 是混合因子,可以取 0、1、源 Alpha、1 − 源 Alpha、源颜色等。通常的“运算”是相加。RGB 和 Alpha 通道可设置不同因子;下表只讨论最容易观察的 RGB。设源颜色为 S,目标颜色为 D,源 Alpha 为 a,均按当前混合步骤中的数值理解。

r174 模式,默认 premultipliedAlpha: false RGB 结果 视觉特征与常见用途
NoBlending S,直接替换 不做源与目标颜色混合;适合确实要覆盖结果的特殊通道
NormalBlending aS + (1−a)D 半透明薄雾、烟、雪的软边
AdditiveBlending aS + D 火光、星光、魔法光点;重叠越多越亮
MultiplyBlending D × S 染色、局部变暗;RGB 因子没有源 Alpha
SubtractiveBlending D × (1−S) 特殊暗化效果;在 r174 中不是简单的 D−S

最后两行尤其容易被名字误导。r174 的非预乘 MultiplyBlending 使用目标颜色乘源颜色;SubtractiveBlending 使用目标颜色乘 1−源颜色。例如某一通道 D=0.8、S=0.3,两者分别得到 0.24 和 0.56。S=0 对减色是中性值,S=1 对乘法是中性值。具体 GPU 因子可在本仓库的 WebGLState.js 中看到。

这些表达式说明默认非预乘材质的 RGB 行为。启用 premultipliedAlpha、使用自定义混合、改变渲染目标或后处理后,要重新核对实际因子;不能把这张表机械套到所有状态。

2. 烟、火、阴影为什么选得不同

烟雾的目标通常是让背景仍可辨认,同时叠加自身颜色,因此先试 NormalBlending。每层烟都会占据一部分视觉权重;交叠很多层时颜色会累积,顺序也更重要。

发光火焰、火花和星光通常先试 AdditiveBlending。它保留目标颜色再加源颜色,所以光斑交叠更亮。代价是亮背景上容易失去对比,重叠过多会饱和成一片白;纯黑的源 RGB 不会贡献亮度。加法混合缓和了许多透明排序问题,但并不保证在任何构图下都完全无关顺序。

乘法与减色适合需要压暗或改变已有颜色的效果。它们的“透明边缘”不能只指望 opacity:在 r174 的上述因子里,RGB 没有乘上源 Alpha。乘法贴图在不想改变背景的区域应输出白色 RGB;减色贴图应输出黑色 RGB。示例在圆外这样编码。把整体 opacity 拖到 0,下方两格的 RGB 效果仍然可见;需要淡出时必须改变参与混合的 RGB 或改用合适的自定义方案。

这四种模式只是起点。实际火焰常结合普通混合的暗烟与加法混合的火芯;阴影是否适合乘法,还要看光照模型、背景内容与颜色管理。

3. 在 Three.js 中设置

两枚软圆各是一张平面贴图。普通烟雾材质的关键设置是:

const smokeMaterial = new THREE.MeshBasicMaterial( {
  map: smokeTexture,
  transparent: true,
  opacity: 0.65,
  blending: THREE.NormalBlending,
  depthWrite: false
} );

把 blending 改成 THREE.AdditiveBlending 就得到发光叠加。这里关闭 depthWrite,是为了让示例中的两个半透明圆都能参与显示;这并非所有透明物体的固定配方。为什么透明粒子常这样设置,第 05 章会通过深度缓冲实验解释。

当需要精确控制因子时,使用 CustomBlending。下面的设置分别指定 RGB 与 Alpha 因子,重建非预乘普通混合的核心行为:

const material = new THREE.MeshBasicMaterial( {
  transparent: true,
  blending: THREE.CustomBlending,
  blendEquation: THREE.AddEquation,
  blendSrc: THREE.SrcAlphaFactor,
  blendDst: THREE.OneMinusSrcAlphaFactor,
  blendEquationAlpha: THREE.AddEquation,
  blendSrcAlpha: THREE.OneFactor,
  blendDstAlpha: THREE.OneMinusSrcAlphaFactor
} );

只有选择 CustomBlending 时,自定义的源/目标因子和方程才会按这些参数应用。RGB 公式与 Alpha 公式不能混为一谈。刚入门时先用内置模式,只有明确需要某个公式再自定义。

4. 对照实验怎么读

先保持中灰蓝背景,观察四格中心和两圆交叠处。然后切为深背景:加法火光更突出,乘法效果变得更难辨认。切为浅背景:减色和乘法的暗化更显眼,加法更容易洗白。

最后调 opacity,尤其观察下方两格。因为它们的 RGB 公式没有源 Alpha,不能把 opacity 理解成对所有混合模式通用的“可见程度”。若真的需要可调强度,可在生成的 RGB 里向中性值插值,或编写自定义片元/混合方案:乘法向白色插值,减色向黑色插值。对应练习会实现前一种做法。

常见问题

为什么我的乘法贴图显示出矩形边框? 透明像素若仍携带会改变目标的 RGB,内置乘法的 RGB 因子并不会因 Alpha 为 0 而自动变中性。检查透明区域的 RGB、纹理过滤及是否需要裁切,不能只看 Alpha 通道。

为什么“减色”不是直接相减? 混合模式名称是用途提示;在 r174 的 WebGL 实现中,它采用目标乘 1−源颜色 的因子。要实现另一个数学式,应明确选用 CustomBlending 并核对 GPU 支持的方程与因子。

为什么加法火花看上去太白? 减少每粒 Alpha、粒子尺寸或重叠层数,并观察背景亮度。单纯把贴图换成更暗的边缘,不一定解决中心的过度累积。

下一章会解释为何一张 Alpha 看似正确的贴图,边缘仍可能发黑。完成第 02 章练习。

参考:混合模式常量、Material.blending 与自定义因子。