我们首先来看一下混合是如何实现的。当片元着色器产生一个颜色的时候,可以选择与颜色缓存中的颜色进行混合。这样一来,混合就和两个操作数有关:源颜色(source color)和目标颜色(destination color)。源颜色,我们用S表示,指的是由片元着色器产生的颜色值;目标颜色,我们用D表示,指的是从颜色缓冲中读取到的颜色值。对它们进行混合后得到的输出颜色,我们用O表示,它会重新写入颜色缓冲中。需要注意的是,当我们谈及混合中的源颜色、目标颜色和输出颜色时,它们都包含了RGBA四个通道的值,而并非仅仅是RGB通道。
想要使用混合,我们必须先开启它。在Unity中,当我们使用Blend(Blend Off命令除外)命令时,除了设置混合状态外也开启了混合。但是,在其他图形API中我们是需要手动开启的。例如在OpenGL中,我们需要使用glEnable(GL_BLEND)来开启混合。但在Unity中,它已经在背后为我们做了这些工作。
混合等式和参数
混合是一个逐片元的操作,而且它不是可编程的,但却是高度可配置的。也就是说,我们可以设置混合时使用的运算操作、混合因子等来影响混合。那么,这些配置又是如何实现的呢?
现在,我们已知两个操作数:源颜色S和目标颜色D,想要得到输出颜色O就必须使用一个等式来计算。我们把这个等式称为混合等式(blend equation)。当进行混合时,我们需要使用两个混合等式:一个用于混合RGB通道,一个用于混合A通道。当设置混合状态时,我们实际上设置的就是混合等式中的操作和因子。在默认情况下,混合等式使用的操作都是加操作(我们也可以使用其他操作),我们只需要再设置一下混合因子即可。由于需要两个等式(分别用于混合RGB通道和A通道),每个等式有两个因子(一个用于和源颜色相乘,一个用于和目标颜色相乘),因此一共需要4个因子。
命令 | 描述 |
Blend SrcFactor DstFactor | 开启混合,并设置混合因子。源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中 |
Blend SrcFactor DstFactor, SrcFactorA DstFactorA |
和上面几乎一样,只是使用不同的因子来混合透明通道 |
可以发现,第一个命令只提供了两个因子,这意味着将使用同样的混合因子来混合RGB通道和A通道,即此时SrcFactorA将等于SrcFactor,DstFactorA将等于DstFactor。下面就是使用这些因子进行加法混合时使用的混合公式:
Orgb=SrcFactor×Srgb+DstFactor×Drgb
Oa=SrcFactorA×Sa+DstFactorA×Da
那么,这些混合因子可以有哪些值呢?下表给出了ShaderLab支持的几种混合因子。
参数 | 描述 |
One | 因子为1 |
Zero | 因子为0 |
SrcColor | 因子为源颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于混合A的混合等式时,使用SrcColor的A分量作为混合因子 |
SrcAlpha | 因子为源颜色的透明度值(A通道) |
DstColor | 因子为目标颜色值。当用于混合RGB通道的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用DstColor的A分量作为混合因子。 |
DstAlpha | 因子为目标颜色的透明度值(A通道) |
OneMinusSrcColor | 因子为(1-源颜色)。当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子 |
OneMinusSrcAlpha | 因子为(1-源颜色的透明度值) |
OneMinusDstColor | 因子为(1-目标颜色)。当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子 |
OneMinusDstAlpha | 因子为(1-目标颜色的透明度值) |
使用上面的指令进行设置时,RGB通道的混合因子和A通道的混合因子都是一样的,有时我们希望可以使用不同的参数混合A通道,这时就可以利用Blend SrcFactor DstFactor, SrcFactorA DstFactorA指令。例如,如果我们想要混合后,输出颜色的透明度值就是源颜色的透明度,可以使用下面的命令:
Blend SrcAlpha OneMinusSrcAlpha, One Zero
混合操作
在上面涉及的混合等式中,当把源颜色和目标颜色与它们对应的混合因子相乘后,我们都是把它们的结果加起来作为输出颜色的。那么可不可以选择不使用加法,而使用减法呢?答案是肯定的,我们可以使用ShaderLab的BlendOp BlendOperation命令,即混合操作。下表给出了ShaderLab中支持的混合操作。
操作 | 描述 |
Add |
将混合后的源颜色和目标颜色相加。默认的混合操作。使用的混合等式是: Orgb=SrcFactor×Srgb+DstFactor×Drgb Oa=SrcFactorA×Sa+DstFactorA×Da |
Sub |
用混合后的源颜色减去混合后的目标颜色。使用的混合等式是: Orgb=SrcFactor×Srgb - DstFactor×Drgb Oa=SrcFactorA×Sa - DstFactorA×Da |
RevSub |
用混合后的目标颜色减去混合后的源颜色。使用的混合等式是: Orgb=DstFactor×Drgb - SrcFactor×Srgb Oa=DstFactorA×Da - SrcFactorA×Sa |
Min |
使用源颜色和目标颜色中较小的值,是逐分量比较。使用的混合等式是: Orgba=(min(Sr, Dr), min(Sg, Dg), min(Sb, Db), min(Sa, Da)) |
Max |
使用源颜色和目标颜色中较大的值,是逐分量比较的。使用的混合等式是: Orgba=(max(Sr, Dr), max(Sg, Dg), max(Sb, Db), max(Sa, Da)) |
其他逻辑操作 | 仅在DirectX11.1中支持 |
混合操作命令通常是与混合因子命令一起工作的。但需要注意的是,当使用Min或Max混合操作时,混合因子实际上是不起任何作用的,它们仅会判断原始的源颜色和目标颜色之间的比较结果。
常见的混合类型
通过混合操作和混合因子命令的组合,我们可以得到一些类似Photoshop混合模式中的混合效果:
// 正常(Normal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha
// 柔和相加(Soft Additive)
Blend OneMinusDstColor One
// 正片叠底(Multiply),即相乘
Blend DstColor Zero
// 两倍相乘(2x Multiply)
Blend DstColor SrcColor
// 变暗(Darken)
BlendOp Min
Blend One One
// 变亮(Lighten)
BlendOp Max
Blend One One
// 滤色(Screen)
Blend OneMinusDstColor One
// 等同于
Blend One OneMinusSrcColor
// 线性减淡(Linear Dodge)
Blend One One