RGB与HSL

cube_40

20 Jun 2019

Author:wuguanxi


CSS中的色彩表示

CSS中常见的颜色表示方法有#HEXRGBHSL这几种。

其中#HEXRGB的关系比较简单,就是16进制与10进制之间的转换。都是表示R(红)G(绿)B(蓝)这三原色之间的混合比例,数值是取 [0 ~ 255]。

HSL则是一种用色相(hue)饱和度(Saturation)亮度(Lightness)表示颜色的方法,这种方法更复合人类对颜色表达的习惯。

RGB与HSL的关系

RGB与HSL的关系,维基百科这篇文章说得很清楚了https://en.wikipedia.org/wiki/HSL_and_HSV

RGBHSL可以看成是两种对颜色表达的不同模型,有其对应的几何意义。

RGB的几何模型是一个立方体,HSL 则是一个圆柱体,它们之间存在着非线性转换的关系。

The RGB gamut can be arranged in a cube

HSL cylinder

transform

用three.js模拟RGB cube

我们可以用three.js搭建一个3D的RGB魔方

分别用[RGB]代表[x,y,z]三个方向,创建一个16*16*16的魔方,每一块对应其RGB颜色

const Cube = function(zb){
  const {px,py,pz,sc,color,name,visible} = zb;
  // 创建一个长宽高均为 sc 个单位长度的立方体(几何体)
  const cubeGeometry = new THREE.BoxGeometry(sc, sc, sc);
  // 创建材质
  const cubeMaterial = new THREE.MeshLambertMaterial({
    color
  });
  // 创建一个立方体网格(mesh):将材质包裹在几何体上
  const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
  // 设置网格位置
  cube.position.x = px;
  cube.position.y = py;
  cube.position.z = pz;
  // 将立方体网格加入到场景中
  return cube;
}

const cubesData = [];
const size = 16;
const sc = 2;
for(let r = 0; r < size; r ++){
  for(let g = 0; g < size; g ++){
    for(let b = 0; b < size; b ++){
      cubesData.push({
        px:r * sc,
        py:g * sc,
        pz:b * sc,
        sc,
        color:createColor(r,g,b,size),
      })
    }
  }
}
const cubeMeshs = cubesData.map((item) => {
  return Cube(item);
});

cubeMeshs.forEach((item) => {
  scene.add(item);
});

function createColor(r,g,b,size){
  const length = size - 1;
  return parseInt(r / length * 0xff) * 0x10000 + parseInt(g / length * 0xff) * 0x100 + parseInt(b / length * 0xff);
}

cube_1

移动摄影机的位置观察魔方的四周(可以在网页里按PALY按钮)

cube_2

cube_3

让摄影机移动到(200,200,200)的位置并lookAt(0,0,0)(按Hue按钮),就可以模拟魔方在x+y+z=0这个面上的投影,看到类似HSL的色环,这里可以看到360度的色相。 按STOP按钮返回。

cube_4

只显示魔方的中轴线,隐藏其他(按Lig按钮),可以看到一条从黑到灰到白的线,这个就相当与是HSL里的亮度的梯度。

cube_5

饱和度用魔方比较难显示,但可以用点阵模拟。3D的RGB点阵

points_1

points_2

这是一个亮度50%的色彩面,可以看到他是由6个直角三角形拼接而成。越靠近中心饱和度越低,外围饱和度最高。

调整L进度度条,可以改变亮度,可以看不同亮度下的色彩面。

l1

10%

l2

30%

l3

50%

l4

70%

l5

90%

RGB 和 HSL 互换公式

// RGB 转换成 HSL 
// R[0~255] G[0~255] B[0~255] H[0~360] S[0~1] L[0~1]
// https://en.wikipedia.org/wiki/HSL_and_HSV
export function rgb2hsl(R,G,B){
  const r = R / 0xff;
  const g = G / 0xff;
  const b = B / 0xff;
  const max = Math.max(r,g,b);
  const min = Math.min(r,g,b);
  const c = max - min;
  let _hue;
  if (c === 0){
    _hue = null;
  } else if (max === r) {
    _hue = ((g - b) / c) % 6;
  } else if (max === g) {
    _hue = (b - r) / c + 2;
  } else {
    _hue = (r - g) / c + 4;
  }
  const hue = _hue === null ? 0 : (_hue * 60) % 360;
  const lightness = (max + min) / 2;
  const saturation = (lightness === 1 || lightness === 0) ? 0 : c / (1 - Math.abs(2 * lightness - 1));
  return {
    H:(hue >= 0 ? hue : 360 + hue) ,S:saturation,L:lightness
  }
}
// HSL 转换成 RGB 
// R[0~255] G[0~255] B[0~255] H[0~360] S[0~1] L[0~1]
// https://en.wikipedia.org/wiki/HSL_and_HSV
export function hsl2rgb(H,S,L){
  const saturation = S;
  const lightness = L;
  const c = (1 - Math.abs(2 * lightness - 1)) * saturation;
  const _hue = (H % 360) / 60;
  const x = c * (1 - Math.abs(_hue % 2 - 1));
  let r,g,b;
  if (_hue >= 0 && _hue < 1) {
    [r,g,b] = [c,x,0];
  } else if (_hue >= 1 && _hue <= 2) {
    [r,g,b] = [x,c,0];
  } else if (_hue >= 2 && _hue <= 3) {
    [r,g,b] = [0,c,x];
  } else if (_hue > 3 && _hue <= 4) {
    [r,g,b] = [0,x,c];
  } else if (_hue > 4 && _hue <= 5) {
    [r,g,b] = [x,0,c];
  } else if (_hue > 5 && _hue <= 6) {
    [r,g,b] = [c,0,x];
  }
  const m = lightness - c / 2;
  const [R,G,B] = [Math.abs(r + m),Math.abs(g + m),Math.abs(b + m)];
  return {
    R:R * 0xff,G:G * 0xff,B:B * 0xff
  }
}