您将如何使一系列 RGB 颜色在光谱颜色范围内均匀分布?以至于看起来像真正的彩虹。
8 回答
改用 HSL:固定亮度和饱和度并将色调从 0 更改为 360,然后转换为 RGB。
HSL 描述人们感知的颜色。RGB 将它们描述为机器使用它们。所以你不能直接使用 RGB 做任何视觉上令人愉悦的事情。
您可以使用HSV 颜色空间并穿过 Hue 维度。
最简单的方法是在此序列中的每个连续对之间进行线性插值(以 RGB 为单位):
#ff0000
红色的#ffff00
黄色#00ff00
绿色#00ffff
青色#0000ff
蓝色的#ff00ff
品红#ff0000
回到红色
这应该得到与扫描 HSV 或 HSL 中的色调值几乎相同的结果,但可以让您直接在 RGB 中工作。请注意,每次插值仅更改一个组件,这简化了事情。这是一个 Python 实现:
def rainbow():
r, g, b = 255, 0, 0
for g in range(256):
yield r, g, b
for r in range(255, -1, -1):
yield r, g, b
for b in range(256):
yield r, g, b
for g in range(255, -1, -1):
yield r, g, b
for r in range(256):
yield r, g, b
for b in range(255, -1, -1):
yield r, g, b
其他解决方案需要相当大量的代码和条件分支,这使得它们不适合 GPU。我最近在 GLSL 中找到了以下神奇的简单公式。在 OpenCL 中基本相同:
vec3 HueToRGB(float hue) {
vec3 h = vec3(hue, hue + 1.0/3.0, hue + 2.0/3.0);
return clamp(6.0 * abs(h - floor(h) - 0.5) - 1.0, 0.0, 1.0);
}
这将为您提供与线性 RGB 中给定色调值相对应的彩虹色。要在图像中使用,请转换为 sRGB,然后乘以 255。
这是一个 C++17 版本:
std::array<float, 3> HueToRGB(float hue, float *rgb) {
std::array<float, 3> rgb;
for (unsigned i = 0; i < 3; ++i) {
const float h = hue + i / 3.f;
rgb[i] = std::clamp(6.f * std::fabs(h - std::floor(h) - 0.5f) - 1.f, 0.f, 1.f);
}
return rgb;
}
这里的关键是要认识到,R、G、B坐标中的每一个在色调值的函数中的图形是一个周期三角函数的一个钳位值,并且可以作为一个锯齿函数的绝对值来获得x - floor(x)
。
- 红色(网页颜色)(十六进制:#FF0000)(RGB:255、0、0)
- 橙色(色轮橙色)(十六进制:#FF7F00)(RGB:255、127、0)
- 黄色(网页颜色)(十六进制:#FFFF00)(RGB:255、255、0)
- 绿色 (X11) (电绿色) (HTML/CSS “石灰”) (色轮绿色) (Hex: #00FF00) (RGB: 0, 255, 0)
- 蓝色(网页颜色)(十六进制:#0000FF)(RGB:0、0、255)
- 靛蓝(电靛蓝)(十六进制:#6600FF)(RGB:102、0、255)
- 紫罗兰色(电紫罗兰色)(十六进制:#8B00FF)(RGB:139、0、255)
在每种颜色之间进行线性插值。
此类将使用 PHP 完成,将您想要的彩虹颜色数量传递给构造函数,并且 $sequence 属性将包含一个 rrggbb 十六进制代码数组。
class color
{
public $sequence = array();
/**
* constructor fills $sequence with a list of colours as long as the $count param
*/
public function __construct($count, $s = .5, $l = .5)
{
for($h = 0; $h <= .85; $h += .85/$count) //.85 is pretty much in the middle of the violet spectrum
{
$this->sequence[] = color::hexHSLtoRGB($h, $s, $l);
}
}
/**
* from https://stackoverflow.com/questions/3597417/php-hsv-to-rgb-formula-comprehension#3642787
*/
public static function HSLtoRGB($h, $s, $l)
{
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : (l + $s - l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;
$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;
switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
return array('r' => floor($r * 255.0),
'g' => floor($g * 255.0),
'b' => floor($b * 255.0)
);
}
//return a hex code from hsv values
public static function hexHSLtoRGB($h, $s, $l)
{
$rgb = self::HSLtoRGB($h, $s, $l);
$hex = base_convert($rgb['r'], 10, 16) . base_convert($rgb['g'], 10, 16) . base_convert($rgb['b'], 10, 16);
return $hex;
}
}
测试例如:
$c = new color(100);
foreach($c->sequence as $col)
print "<div style='background-color:#$col'>$col</div>\n";
我只声称将其打包成一个类,原始函数在这篇文章中找到: PHP HSV to RGB formula comprehension
我知道这是一个相当老的问题,但这是我简单易懂的解决方案,在大多数编程语言中应该很容易使用。将 steps 和 whichStep 替换为您自己的值。
int steps = 1280;
int stepChange = 1280 / steps;
int change = stepChange * whichStep;
int r=0, g=0, b=0;
if (change < 256)
{
r = 255;
g += change;
}
else if (change < 512)
{
r = 511 - change;
g = 255;
}
else if (change < 768)
{
g = 255;
b = change-512;
}
else if (change < 1024)
{
g = 1023 - change;
b = 255;
}
else
{
r = change - 1024;
b = 255;
}
我可以使用 JavaScript 和 HTML5 以编程方式绘制彩虹。
我从 rgb(255,0,0) -> rgb(255,255,0) -> rgb(0,255,0) -> rgb(0,255,255) -> rgb(0,0,255) -> rgb(255,0,255) 做一个渐变)。
我使用我的RainbowVis-JS库(它只是将渐变链接在一起)沿渐变计算十六进制颜色值。我用 HTML5 Canvas 绘制弧形,循环显示颜色。
<!DOCTYPE html>
<html>
<head>
<script src="rainbowvis.js"></script>
</head>
<body>
<script type="text/javascript">
window.onload = function(){
var RAINBOW_WIDTH = 60;
var RAINBOW_RADIUS = 130;
// Library class
var rainbow = new Rainbow();
rainbow.setNumberRange(1, RAINBOW_WIDTH);
rainbow.setSpectrum(
'FF0000', 'FFFF00', '00FF00',
'00FFFF', '0000FF', 'FF00FF'
);
// Canvas
var canvas = document.getElementById('MyCanvas');
var context = canvas.getContext('2d');
context.lineWidth = 1;
for (var i = 1; i <= RAINBOW_WIDTH; i++) {
context.beginPath();
context.arc(canvas.width/2, canvas.width/2, RAINBOW_RADIUS - i+1,
Math.PI, 0, false);
context.strokeStyle = '#' + rainbow.colourAt(i); // Library function
context.stroke();
}
};
</script>
<canvas id="MyCanvas" width="300" height="300">
<p>Rainbow arc example. Your browser doesn't support canvas.</p>
</canvas>
</body>
</html>