3.1 颜色模型基础
为什么需要 4 种颜色空间?它们各自擅长什么?色盲眼中看到的世界是怎样的?
Why do we need 4 color spaces? What is each one good at? What does the world look like through the eyes of someone with color blindness?
为什么要学颜色模型?
Why learn color models?
设计 colormap 时,不能直接用 RGB 插值——因为 RGB 不是感知均匀的(255,0,0 与 0,255,0 "看上去"亮度差很多)。你需要感知均匀的颜色空间(Lab / HCL)来插值,才能得到平滑的渐变。
When designing a colormap, do not interpolate directly in RGB—because RGB is not perceptually uniform (255,0,0 and 0,255,0 "look" very different in lightness). You need a perceptually uniform color space (Lab / HCL) to interpolate and get smooth gradients.
4 大颜色空间
The 4 major color spaces
- RGB:加色模型,设备相关。屏幕显示的默认。
- RGB: additive model, device-dependent. The default for screen display.
- HSL:艺术家友好(色相/饱和度/亮度独立),但仍不感知均匀。
- HSL: artist-friendly (independent hue / saturation / lightness), but still not perceptually uniform.
- CIE Lab:感知均匀,设计 colormap 的黄金标准。
- CIE Lab: perceptually uniform, the gold standard for designing colormaps.
- CMYK:减色(印刷),web 可视化用不到。
- CMYK: subtractive (print). Not used in web visualizations.
CVD(色觉缺陷)
CVD (Color Vision Deficiency)
约 8% 男性、0.5% 女性是色盲。常见三种:
About 8% of men and 0.5% of women are color-blind. Three common types:
- 红色盲(protanopia):约 1% 男性
- Protanopia (red-blind): ~1% of men
- 绿色盲(deuteranopia):约 6% 男性(最常见)
- Deuteranopia (green-blind): ~6% of men (most common)
- 蓝色盲(tritanopia):极罕见
- Tritanopia (blue-blind): extremely rare
⭐ 关键代码
⭐ Key code
⭐ 关键点 1:RGB → HSL 手写
⭐ Key point 1: Hand-written RGB ↔ HSL
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) { h = s = 0; }
else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h * 360, s * 100, l * 100];
}
⭐ 自己写 RGB↔HSL 是为了理解每一步的物理含义——"最大通道决定色相、相减决定饱和度"——比用 d3 库更能理解色相为什么在 0-360° 循环。
⭐ Writing RGB↔HSL yourself is to understand the physical meaning of each step—"the largest channel determines hue, the difference determines saturation"—which is more illuminating than using a d3 library for understanding why hue cycles between 0–360°.
⭐ 关键点 2:CVD 模拟矩阵
⭐ Key point 2: CVD simulation matrix
const CVD_MATRICES = {
deuteranopia: [
[0.625, 0.375, 0.000],
[0.700, 0.300, 0.000],
[0.000, 0.300, 0.700]
],
protanopia: [[0.567, 0.433, 0.000], [0.558, 0.442, 0.000], [0.000, 0.242, 0.758]],
tritanopia: [[0.950, 0.050, 0.000], [0.000, 0.433, 0.567], [0.000, 0.475, 0.525]]
};
function simulateCVD([r, g, b], type) {
const m = CVD_MATRICES[type];
return [
r * m[0][0] + g * m[0][1] + b * m[0][2],
r * m[1][0] + g * m[1][1] + b * m[1][2],
r * m[2][0] + g * m[2][1] + b * m[2][2]
].map(v => Math.round(Math.max(0, Math.min(255, v))));
}
⭐ 关键点 3:d3 内置多空间
⭐ Key point 3: d3's built-in multi-space support
const c = d3.color('rgb(100, 50, 200)');
c.rgb(); // {r, g, b}
c.hsl(); // {h, s, l}
c.lab(); // {l, a, b} ← 感知均匀
c.hcl(); // {h, c, l} ← Lab 极坐标版