起源
光,一种用来拯救世界的信仰 通常指的是人类眼睛可见的电磁波,视知觉 就是对于可见光的知觉。
可见光只是电磁波谱上介于 400nm - 700nm 之间的部分(也有一些资料定义不同,较窄的定义在 420nm - 680nm 之间,较宽的定义在 380nm - 800nm 之间),在教材上通常以这样一张 频谱图 来表示:

每种不同的波长对应的颜色各不相同,因此我们可以根据这个原理制作显示设备,只要找到一种在可控条件下发射不同波长的光的方法就可以了。但是这个想法不现实,首先是设备大小问题。想要制造出任意波长的光需要比较复杂的设备和条件,而这样制造出来的光线还只能显示单个像素。想象一下用这样的技术制造出一个标准 1080p 显示器,设备本身的大小会十分感人。
在进一步探索后,20 世纪 30 年代,国际照明委员会(英文:International Commission on Illumination,法文:Commission internationale de l’éclairage,采用法文缩写:CIE)设计了一套色彩系统,用于描述人眼所能感受到的色彩,并对其进行量化。
CIE 1931 色彩空间
经过医学研究,人眼感知色彩主要依靠三种色锥细胞,分别对应红、绿、蓝三种颜色,后来将其称为光的三原色。所以,使用三种原色的光线就可以混合出五彩斑斓的颜色。在此基础上,CIE 设计了一个实验,使用四个光源来量化颜色,并招募志愿者通过人眼感知对色彩进行匹配,称之为颜色匹配实验。从这个试验中,CIE 最终构建出了一套色彩模型,称为 CIE 1931 xyz 色彩空间,也可以直接称为 CIE 1931 色彩空间。
色彩实验
实验过程大致如下:
- 通过三个单色光源(红、绿、蓝)发射出三种光线,照射到一个白色的试验板上,汇聚成一个色彩光斑;
- 通过另一个单色光源调整出待测光线,照射在刚刚光斑的附近形成另一个光斑;
- 使用黑色的不透光材料作为两个光斑的分界线,并移动光源使两个光斑靠在一起;
- 实验者通过同时观察左右光斑,并根据观测结果不断调整三个单色光源的强度,使得两个光斑看起来显示出同一种颜色;
- 记录三个单色光源的强度,这样就得到了一个颜色的 RGB 值,被称为三刺激值。
更加直观的视频描述可以 参考这里,以下实验图像也来自于此。

在这个实验中,实验者们发现了一个问题:一些待测光线的色彩是无法通过三原色混合出来的,例如下面的例子,想要混合出这种绿色,似乎需要继续降低红光,但是红色光源已经完全关闭了:

于是实验者通过将红色光源移到右边进行混色,并通过调整使得两边色彩一致:

最终就可以确定出这个颜色的三刺激值了,只不过红色是负的。
在获取了大量的实验数据后,CIE 在亮度 - 波长坐标系中绘制出了这样 3 条曲线:

在这个曲线图中,λ 轴代表波长,y 轴代表亮度,而三条色彩曲线则代表了要混合出视觉上与此波长光线同等时,红、绿、蓝三种光线的强度。
CIE 1931 RGB 色彩空间
我们发现了一个问题,在这个坐标系中表示颜色很不直观。于是,CIE 又在以 RGB 亮度为轴的三维坐标系中绘制出了这些点:

这样,我们就得到了一个色彩空间定义,称为 CIE 1931 RGB 色彩空间。
格拉斯曼定律
格拉斯曼定律是一个关于光学理论的经验法则,它说明了人类对色彩的感知(大约)是 线性 的。
简单来说,取两个单色可见光,那么这两个可见光在 RGB 色彩空间里可以用两个坐标 , 来表示,那么这两个色彩混合一起得到的色彩就可以依照以下的简单加法公式计算出来:
这个公式就是格拉斯曼定律的简单表达。但是请注意格拉斯曼定律是由实验总结得出的,由于现代医学技术的限制,我们无法直接严格证明色彩空间就是一个线性空间。
格拉斯曼定律的更一般形式可以表达为:
其中, 为该光束对波长的强度分布;,, 则分别为人眼中三种锥状细胞对不同波长的反应强度。
CIE 1931 XYZ 色彩空间
在构建了人类视觉的 RGB 模型之后,CIE 特别委员会的成员希望开发出与 CIE RGB 色彩空间有关的另一个色彩空间,它假定格拉斯曼定律是成立的,这意味着新的色彩空间能够通过线性变换有关于 CIE RGB 色彩空间。
于是,CIE 1931 XYZ 色彩空间诞生了。在这个色彩空间中,X、Y、Z 三个坐标轴的定义是:
在这个新的色彩空间中,大部分定义与 RGB 色彩空间是相同的,但 XYZ 坐标轴则是歪过来的,这套坐标轴相对于 RGB 坐标轴的大致图例如下:

在这个色彩空间中,所有的点都在第一卦限中,因此避免了负值。不过如果仅仅只是为了将所有的点都放在第一卦限中,那其实可以有很多种选择。而 XYZ 色彩空间如此选择的一个重要原因是 Y 轴精确满足了“CIE 标准适应光观察者”(CIE 1926)的 适应光发光效率函数(光度函数) 。
光度函数描述了人眼对于不同波长光的平均视觉灵敏度,可用于将辐射能量转化为可见光的计算。
从 XYZ 色彩空间中,我们可以通过以下的变换得到 RGB 色彩空间:
插话:绝对色彩空间
绝对色彩空间 就是不依赖任何外部因素就可以准确表示颜色的色彩空间。
RGB 色彩模型(这里指的不是 CIE 1931 RGB 色彩空间)就是一个非绝对色彩空间,因为它的三个坐标轴并没有量化意义,一种颜色是通过 RGB 三种颜色通过调整亮度混合出来的,而这些颜色并不是标准、精确的定义。在两个不同计算机显示器上,同一个 RGB 颜色可能看起来大不相同。为了表示 RGB 色彩在绝对色彩中的位置,其中一种方法是定义一个 International Color Consortium(ICC)色彩配置文件,目前这种方法是业界标准,广泛采用的 RGB 配置文件有 sRGB、Adobe RGB 等。
插话:白点
白点(white point),在技术文档中常被称作参考白色(reference white)或目标白色(target white),用来在处理颜色时定义 白色。白点通常是在色彩空间中的一个点,它定义了色彩空间中的 中性白色,并且也定义了色彩空间中的 亮度。
白点不是一个绝对的概念,比如说在午后日光的照射下,色彩整体会偏向暖色;而在阴天或者室内冷色 LED 灯的环境下,色彩整体会偏向冷色。但是白点在视觉中的感知是不变的,因此在不同的光照环境下,白点的色彩定义也会有所不同。
在 sRGB 色彩空间中,白点的定义模拟了平均北方天空的日光条件,是以地球上不同地点对日光进行光谱辐射的大量数据为基础,总结出的一组相对光谱功率分布数据,色温为 6500K,近似平均昼光。我们把这个白点称为 D65 白点(Daylight Neutral)。
HSL 和 HSV 色彩空间
HSL(Hue 色相;Saturation 饱和度;Lightness 亮度)和 HSV(Hue 色相;Saturation 饱和度;Value 明度)都是将 RGB 色彩模型中的点表示在圆柱坐标系中的方法。这两种表示法试图做到比基于笛卡尔坐标系几何结构的 RGB 模型更加直观。
其中,Lightness 和 Value 都是表示颜色的亮度,但是它们的含义有所不同,参考下图:

HSV 以人类更熟悉的方式封装了关于颜色的信息:“这是什么颜色?深浅如何?明暗如何?”,因此在早期被广泛应用于设计行业与计算机设计中。但是 HSV 仍旧是 RGB 色彩空间的线性变换,RGB 模型的缺点 HSV 也并没有解决,对于亮度/明度相同的颜色来说,人眼感受到的视觉亮度不一定相同。
CIE L*a*b* 色彩空间
显然,RGB 色彩空间很适合机器实现,只需要使用三个单色灯泡即可。但是对于需要量化色彩的设计行业而言,RGB 色彩空间却显得不那么直观。在 RGB 色彩空间里,人眼对于红色的感知程度相对较低,而对绿色的感知程度则相对较高,这导致了使用 RGB 色彩空间进行色彩搭配的时候,很难保证所选择的色彩能够带给用户“匹配”的感觉,例如很亮的绿色 #00FF00 和很亮的红色 #FF0000 放在一起时,人类倾向于感知到绿色的亮度更高。

如上图,显然红色背景下的白色字体可读性更好,而绿色背景下可读性差多了。这是因为红色的视觉亮度更低,与白色的对比度更高。
如果想要得到一套对于人眼而言亮度更加均匀的颜色,那么使用 RGB 色彩空间是没有办法直观进行计算的,只能通过设计师的直觉慢慢进行调整,最终得到一个近似的结果。这么做导致调色的过程费事费力,而且最终的效果和设计师本身的经验和水平有很大关系。
CIE 1931 色彩空间的另一个问题是,它并没有给出估量颜色差别的直接方式。
测量两个颜色之间的差别的想法是 D.L. MacAdam 开发的并总结于 MacAdam 椭圆 的概念中,而在 XYZ 色彩空间中,两个颜色之间的欧氏距离并不能很好地表示人类视觉系统对于两个颜色之间的 差异 的感知。基于 MacAdam 的工作,在 1960 年开发了 CIE L*u*v* 色彩空间,并经过改良后得到了 CIE L*a*b* 色彩空间。
CIELAB 色彩空间(英语:CIELAB color space) 又写为 L*a*b*,是 CIE 在 1976 年定义的色彩空间,是惯常用来描述人眼可见的所有颜色的最完备的色彩模型。
在这个模型中,L* 代表颜色的 亮度(Lightness)( L* = 0 指示黑色而 L* = 100 指示白色),a* 和 b* 代表颜色的 色度偏移(a* 指示颜色在红色/品红到绿色之间的位置,负值指示绿色,正值指示品红;b* 指示颜色在黄色和蓝色之间的位置,负值指示蓝色,正值指示黄色)。这个色彩模型用于充当做参照的设备无关的模型,用于描述颜色的感知。
L*a*b* 色彩空间也可以表达为 L*c*h*,其中 L* 的含义不变,而 c* 代表颜色的 色度(Chroma),色度的含义与灰度类似,取值越高颜色越鲜艳;而 h* 代表颜色的 色相(Hue),色相的含义与角度类似,更改色相就可以遍历色彩。下图展示了 L*a*b* 色彩空间在 L* 取不同值的色彩分布,只展示可充入 sRGB 色域的颜色(因此可以显示在典型的计算机显示器上)。每个正方形的每个轴取值于 -128 到 127。

颜色距离
在 L*a*b* 色彩空间中,两个颜色之间的欧氏距离可以很好地表示人类视觉系统对于两个颜色之间的 差异 的感知。在 L*a*b* 中的均匀改变对应在感知颜色中的均匀改变。我们定义两个颜色的欧氏距离为 ,那么两个颜色 和 之间的距离就是:
RGB / CMYK 与 L*a*b* 的转换
从 L*a*b* 色彩空间到 RGB / CMYK 等设备相关色彩的转换是一个复杂的过程,我们需要借助 XYZ 色彩空间作为中间桥梁。
XYZ 与 L*a*b* 的转换
正向变换:
其中 是参照白点的三刺激值(下标 n 的含义是 Normalized);其中 函数的定义为:
这里 函数被分开定义是为了防止在 时的无限斜率,在某个 之下, 被假定是线性的,并且被假定为匹配函数的 部分在 处的值和斜率。
我们指定一个 ,并令 ,上述两个方程对 和 有解:
逆向变换:
OKLAB 色彩空间
CIELAB 色彩空间已经基本完备,但是它存在一个小问题:它的 色彩预测 是不准确的。这里的色彩预测指的是给定色相和饱和度,预测颜色的亮度。但是在 CIELAB 中我们发现,亮度的预测并不是很准确,尤其是对蓝色来说,随着亮度的提高,整体颜色会往紫色偏移。
而 OKLAB 通过重新设计色彩空间,解决了这个问题。如下对比图可以看出两者区别:

另外我们还可以对比 HSL/HSV(RGB)色彩空间通过固定亮度和色度颜色渐变:
HSV:

OKLAB:

由于没有一种比较好的办法来比较视觉亮度,这里以灰度做一个简单代替,需要注意的是灰度并不能等同于视觉亮度,只能大致描述。
可以很明显的感知出两者的区别,OKLAB 产生的渐变色彩在亮度上更加统一,而且色相的变化更加平滑。
OKLAB 色彩空间的定义如下:
- L:视觉亮度;
- a:颜色有多绿/红;
- b:颜色有多蓝/黄;
更常用的是极坐标形式的 色彩空间,其中
而在实用意义上, 代表 Chroma,即颜色的饱和度,而 代表 Hue,即颜色的色相。
在 LCH Color Picker & Converter 中我们可以直观的调整 OKLAB 色彩的参数,以便更好地理解这个色彩空间:

推导
为了定义 OKLAB,作者使用了三个数据集:
- 使用 CAM16 和正常观看条件生成的具有相同亮度但随机色调和色度的颜色对的生成数据集。颜色仅限于指针色域(即一组可能的表面颜色)内;
- 使用 CAM16 和正常观看条件生成的具有相同色度但随机色调和亮度的颜色对的生成数据集。颜色仅限于指针色域(即一组可能的表面颜色)内;
- 用于导出 IPT 的 统一感知色相数据。在这些数据中,颜色被组合成具有相同感知色调的颜色对。
这些数据集可用于分别测试亮度、色度和色调的 预测。如果颜色空间准确地模拟 、 和 ,则亮度数据集中的颜色对应该具有相同的 ,色度数据集中的颜色对应该具有相同的 ,而色调数据集中的颜色对应该具有相同的 。
RGB / CMYK 与 OKLAB 的转换
RGB / CMYK 与 OKLAB 的转换同样需要借助 XYZ 色彩空间作为中间桥梁。
XYZ 与 OKLAB 的转换
给定 XYZ 坐标中的颜色,D65 白点 和 白色值 Y = 1,我们可以通过以下过程计算出 OKLAB 色彩空间中的颜色:
首先,我们将 XYZ 坐标转换为近似的圆锥响应:
再应用一轮非线性变换:
最后将其转换为 坐标:
和 是两个矩阵,定义如下:
OKLAB 色彩空间目前已经在现代浏览器中普及,可以通过 CSS 的 oklch() 函数直接使用。
利用 OKLAB 色彩空间,我们终于能够从人类感官的层面来定义一个颜色,并通过色彩空间模型对整个设计色彩体系进行预测与计算。
