颜色处理

颜色处理是 Shader 非常重要的一部分。我们所接触到的大部分图像/视频滤镜都是像素颜色混合的产物。简单来说,一切对于图像的处理都是对像素 RGB 的修改。当然,修改也不是乱改,我们通过对光学和物理领域的颜色现象的了解,更深刻地认识颜色。

原色

以不同比例将原色混合,可以产生出其他的新颜色。以数学的向量空间来解释色彩系统,则原色在空间内可作为一组基底向量,并且能组合出一个「色彩空间」。由于人类肉眼有三种不同颜色的感光体,因此所见的色彩空间通常可以由三种基本色所表达,这三种颜色被称为「三原色」。一般来说叠加型的三原色是红色、绿色、蓝色(又称三基色,用于电视机、投影仪等显示设备);而消减型的三原色是品红色、黄色、青色(用于书本、杂志等的印刷)。

—— 原色 - Wikipedia

简单翻译下维基百科中的定义,我们可以把原色分为叠加型三原色(光学三原色 RGB)和消减型三原色(色彩三原色 CMYK)

叠加型三原色的特点就是颜色本身是色光,将不同颜色的光加在一起形成新的颜色,而且越来越亮(比如电子显示屏)

消减型三原色的特点则恰好相反,颜色本身不发光,将不同的颜色混合成新的颜色,会越来越暗(如颜料,由于材料吸收特定波段的光,所以只有不被吸收的部分反射了回来,加上的颜色越多吸收的光也越多,所以越来越暗)

说了这么多,其实想说的就是在 Shader 中,我们可以通过 RGB 的加乘计算,来模拟这种自然的颜色混合现象。下面的 Demo 是叠加型三原色的实现(通过加法对颜色进行混合):

See the Pen RBG ADD by ShaderFans (@shaderfans) on CodePen.

下面的 Demo 是消减型三原色的实现(通过乘法对颜色进行混合):

See the Pen RBG Multiply by ShaderFans (@shaderfans) on CodePen.

颜色混合 mix

在大部分情况下,不会单独使用加乘法去做颜色混合,Shader 中内置了颜色混合的函数:mix(colorA, colorB, progress),它的实现公式是 x*(1-a)+y*a,通过分配不同的颜色比重,颜色混合就会有合理的渐变过渡。

See the Pen RBG Multiply by ShaderFans (@shaderfans) on CodePen.

当然,还可以对渐变曲线做一些额外的处理,让颜色变化更加丰富(三条曲线代表 RGB 三中颜色的变化趋势)

See the Pen RBG Mix by ShaderFans (@shaderfans) on CodePen.

色彩空间转换

HSL 和 HSV 都是一种将 RGB 色彩模型中的点在圆柱坐标系中的表示法。这两种表示法试图做到比基于笛卡尔坐标系的几何结构 RGB 更加直观。HSL 即色相、饱和度、亮度(英语:Hue, Saturation, Lightness)。HSV 即色相、饱和度、明度(英语:Hue, Saturation, Value),又称 HSB,其中 B 即英语:Brightness。

—— HSL和HSV色彩空间 - Wikipedia

知乎用户大蔚陈在某篇问答中回答道:RGB 是对机器很友好的色彩模式,但并不够人性化,因为我们对色彩的认识往往是「什么颜色?鲜艳不鲜艳?亮还是暗?」。HSL 模式和 HSV (HSB) 都是基于 RGB 的,是作为一个更方便友好的方法创建出来的。

HSL 和 HSB/HSV 又有一些区别:

这是它们之间的转换公式:

// RGB 转 HSB
vec3 rgb2hsb( in vec3 c ){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz),
                 vec4(c.gb, K.xy),
                 step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r),
                 vec4(c.r, p.yzx),
                 step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
                d / (q.x + e),
                q.x);
}

// HSB 转 RGB
//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ) {
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                6.0)-3.0)-1.0,
                0.0,
                1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix(vec3(1.0), rgb, c.y);
}

在笛卡尔坐标系和极坐标系下 HSB 颜色可以这么玩:

See the Pen Pattern by ShaderFans (@shaderfans) on CodePen.

混合模式 blend

混合模式(Blend Mode)是图像处理技术中的一个技术名词,广泛应用于 Photoshop、AfterEffect 等图像视频后期软件中,主要功效是可以用不同的方法将对象颜色与底层对象的颜色混合。当您将一种混合模式应用于某一对象时,在此对象的图层或组下方的任何对象上都可看到混合模式的效果。

—— 混合模式 - Baidu

根据 Photoshop 帮助文档中所展示的,不同的混合模式有不同的算法和特性,可以得到不一样的颜色混合效果:

幸运的是,Github 已经存在了现成的颜色混合 Shader 库:glsl-blend。点击对应的 glsl 文件即可查看实际代码,这里以滤色 screen.glsl 为例:

float blendScreen(float base, float blend) {
    return 1.0-((1.0-base)*(1.0-blend));
}
vec3 blendScreen(vec3 base, vec3 blend) {
    return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));
}

这里通过对两张照片进行滤色 (screen) 处理,查看每个通道的颜色信息,并将混合色的互补色与基色进行正片叠底。结果色总是较亮的颜色:

See the Pen Color Blend by ShaderFans (@shaderfans) on CodePen.