ArtColors 应该提供一个简单的函数调用,用最少的代码以真实的方式减法混合两种颜色,只需要两个 RGB 输入和一个混合比,如下所示:
Return Color=SubtractiveMix(Color a, Color b, percentage)
ArtColors 使用的算法(我认为)使用其他方法的一小部分代码即可提供相当不错的结果,并且无需计算或存储反射率数据或计算复杂的公式。目标是仅用 20% 的代码实现 80% 的真实性。
基本方法的灵感来自于考虑颜料的实际混合方式。检查这个油漆混合的特写镜头:
如果你仔细观察,你会发现在某些区域,两种颜料完全混合,结果是相减的:黄色和蓝色正在形成更深的绿色。红色和蓝色正在形成非常深的紫色。然而,在混合不那么彻底的其他区域,黄色和蓝色的细线并排存在。这些涂料反射黄色和蓝色光。 在远处,当不同的漩涡太小而无法看到时,这些颜色会被眼睛叠加混合。
进一步考虑混合涂料是化学意义上的混合物:不会发生化学变化。红色和蓝色分子仍然存在于彻底混合中,完全按照它们分开时所做的事情:反射红色和蓝色光。当光线在介质中反弹时,会产生很多次表面物理效应。入射光被一个分子吸收和反射,然后被另一个分子吸收和反射,最终结果反射到眼睛。
这如何帮助解决我们的问题?
严格减法方法从白色开始,然后从白色中减去颜色 A 和颜色 B 的 RGB 值,并返回剩下的内容。 这种方法往往过于黑暗。 为什么?每种颜料中的一些仍然在很小的范围内反映其独特的颜色。如果我们采用一种部分加法,部分减法的方法,我们会得到更真实的结果!
此外,如果颜色 A = 颜色 B,我们的函数应该返回相同的颜色。同一种颜色混用同一种颜色应该等于同一种颜色!使用严格减法算法,结果是原始色调的较暗版本(因为输入颜色值从 White 中减去两次)。两种输入颜色越接近,在混合中看到的变化就越少。
减法混合的 ArtColor 代码是:
Color ColorMixSub(Color a, Color b, float blend) {
Color out;
Color c,d,f;
c=ColorInv(a);
d=ColorInv(b);
f.r=max(0,255-c.r-d.r);
f.g=max(0,255-c.g-d.g);
f.b=max(0,255-c.b-d.b);
float cd=ColorDistance(a,b);
cd=4.0*blend*(1.0-blend)*cd;
out=ColorMixLin(ColorMixLin(a,b,blend),f,cd);
out.a=255;
return out;
}
代码说明:
Color a
和Color b
是输入颜色。 blend
指定每种颜色混合多少,从 0 到 1.0,如线性插值 (LERP)。0.0 = 所有颜色 A,1.0 = 所有颜色 B。0.5 = A 和 B 的 50%-50% 混合。
首先,我们找到颜色 a 和 b 的 RGB 倒数,并将它们分配给新颜色 c 和 d。
c=ColorInv(a);
d=ColorInv(b);
然后我们从纯 RGB 白色中减去 c 和 d,将结果钳制为零,并将结果分配给颜色 f。
f.r=max(0,255-c.r-d.r);
f.g=max(0,255-c.g-d.g);
f.b=max(0,255-c.b-d.b);
到目前为止,f 是纯减法的结果,存在上述问题。
接下来,我们计算颜色 a 和颜色 b 之间的“颜色距离”,它只是它们在 RGB 空间中的向量距离,在 0.0(相同颜色)和 1.0(完全相反,如白色和黑色)之间进行归一化。
float cd=ColorDistance(a,b);
该值将有助于解决混合两种相似色调不应该对结果产生太大影响的问题。然后,颜色距离因子cd
由二次传递函数转换,该函数调节我们进行多少减法和加法混合:
cd=4.0*blend*(1.0-blend)*cd;
端点确保接近 0% 或 100% 的混合百分比看起来非常接近原始输入颜色。二次曲线为接下来的混合提供了良好的色域。曲线的峰值由颜色距离决定。这个函数的输出决定了我们结果中加法与减法混合的量。更远的颜色将与更减法的动态混合(在 y=1.0 时完全减法)。相似的颜色与更具附加性的动态(更平坦的曲线)混合,但仍然具有减色因子。二次传递函数的最大值是归一化颜色距离,因此颜色空间中截然相反的颜色将完全减法混合。
最后一行完成所有工作:
out=ColorMixLin(ColorMixLin(a,b,blend),f,cd);`
首先,我们以指定的比例将颜色 A 和颜色 B相加混合blend
,这是通过 完成的ColorMixLin(a,b,blend)
。这代表了上图中那些精细的颜色漩涡和地下相互作用的叠加混合效果。如果没有这个因素,严格的减法方法可能会产生奇怪的结果。然后,根据上面提到的传递函数,这个加法结果与我们的纯减法结果混合color f
,该传递函数基于 和 之间的颜色Color a
距离Color b
。
瞧!对于广泛的输入颜色,会产生非常好的结果。