PHP中生成RGB渐变颜色的算法

7
我很感兴趣于生成两个给定颜色之间的'n'个渐变色的算法,以实现它们之间的平滑过渡。我尝试让两个通道保持静态,例如 R 和 G,并增量更改 B,但有时候两个颜色之间的差异比相邻的颜色之间的差异更难以处理。我想检查不同的算法并分析它们的优缺点。
我编写了这段代码,它看起来是逻辑的,但某些颜色之间的过渡比其他颜色之间的过渡更困难(例如,0和1之间的过渡比1和2之间的过渡更困难)。
<?php
$c1 = array(128,175,27); // Color 1
$c2 = array(255,255,140); // Color 2
$nc = 5; // Number of colors to display.
$dc = array(($c2[0]-$c1[0])/($nc-1),($c2[1]-$c1[1])/($nc-1),($c2[2]-$c1[2])/($nc-1)); // Step between colors

for ($i=0;$i<$nc;$i++){
    echo '<div style="width:200px;height:50px;background-color:rgb('.round($c1[0]+$dc[0]*$i).','.round($c1[1]+$dc[1]*$i).','.round($c1[2]+$dc[2]*$i).');">'.$i.'</div>'; // Output
}
?>

有没有更好的算法来实现这个呢?
我带来了一个例子:在上面的代码中,我使用了$c1=array(192,5,248);$c2 = array(142,175,240);$nc = 10;,获得了这张图片:

graduated colors example

0、1、8和9的RGB值是:
  • 0 = 192,5,248
  • 1 = 186,24,247
  • 8 = 148,156,241
  • 9 = 142,175,240
如果你看一下,邻近颜色之间有6、19、1的差异。但是0和1之间的过渡比8和9之间的过渡更平滑。对于HSV也是一样的。有些颜色的过渡会更加顺畅。

那么……你到目前为止尝试了什么? - Fosco
“我尝试让静态的两个通道,例如R和G,以及递增变化B”是什么意思?你是保持R和G不变,只改变B吗?这只有在源颜色和目标颜色中R和G相同时才有效。 - mcrumley
是的@mcrumley,我确实这样做了,但后来发现最好在通道之间分开差异。请查看添加的代码。 - Memochipan
2个回答

4

在HSV中,生成n个颜色,其色相从第一个颜色线性变化到第二个颜色。将每个HSV值转换为RGB。


你能建议一种用PHP实现的方法吗?看看我上面的示例代码?你提出的方法比那个更好吗?感谢你的回答。 - Memochipan
这与PHP无关,只是简单的算术可以在HSV和RGB之间进行切换。Google是你的好帮手。或者,参见http://stackoverflow.com/q/3597447/21727。 - mbeckish
我没有看到任何优势,给定两个RGB颜色,将其转换为HSV再重新转换为RGB。我测试了你说的,但是在每10步中更改V而不是H,结果与我上面的代码完全相同。 - Memochipan
@Memochipan - 使用HSV的原因是它提供了一种简单的方法来按照ROYGBIV顺序遍历颜色。同时改变R、G和B可能对某些颜色对有效,但在转换过程中其他颜色会在颜色谱上跳跃。 - mbeckish
@Memochipan - 在HSV中,您实际上希望同时更改H、S和V以从一种颜色过渡到另一种颜色。我最初建议仅更改色调是基于您只处理完全饱和颜色的假设。 - mbeckish
我写了一段代码,使用HSV和RGB查看过渡效果,请查看我的答案。第一次进行转换时,我使用了一种“奇怪”的颜色组合,它在转换为HSV后没有改变,因此我认为转换是不必要的。 - Memochipan

4
在下面的图片中,您可以看到我编写的一段代码的输出,使用RGB和HSV分割相等大小步骤来比较两种颜色之间的转换: enter image description here 我发现使用HSV进行转换会受到色调的影响,并且取决于颜色之间的距离。如果您选择具有相同色调的两种颜色,则有趣的是,HSV转换比RGB更清晰,因为您只需处理饱和度和值(黑色),而不是像RGB那样添加颜色。
<?php
// Configuration.
$nc = 6; // Number of colors.
$w = 300; // Width of divs.
$a = 50; // Height of divs.

// Colors
/* In RGB */
$c1 = array(rand(0,255),rand(0,255),rand(0,255));
$c2 = array(rand(0,255),rand(0,255),rand(0,255)); 
//$c1 = array(128,175,27); // Color 1: Whit these colors is not change.
//$c2 = array(255,255,140); // Color 2: Whit these colors is not change.
// $c1 = array(0,0,0); // Color 1: White.
// $c2 = array(255,255,255); // Color 2: Black.
/* In HSV */
$h3 = array(rand(0,360),rand(0,100),rand(0,100)); 
$h4 = array(rand(0,360),rand(0,100),rand(0,100)); 
//$h3 = array(145,50,50); // Color 3: To see the influence of Hue.
//$h4 = array(145,0,100); // Color 4: To see the influence of Hue.

// HTML
$html .= '<div style="margin:auto;width:'.($w*2).'px;">';
// RGB to RGB split
$c = graduateRGB($c1,$c2,$nc);
$html .= customHTML($w,$a,$c,'RGB->RGBs');
// RGB to HSV split
$h1 = RGBtoHSV($c1);
$h2 = RGBtoHSV($c2);
$h = graduateHSV($h1,$h2,$nc);
$html .= customHTML($w,$a,$h,'RGB->HSVs');
// HSV to HSV split
$h = graduateHSV($h3,$h4,$nc);
$html .= customHTML($w,$a,$h,'HSV->HSVs');
// HSV to RGB split
$c3 = HSVtoRGB($h3);
$c4 = HSVtoRGB($h4);
$c = graduateRGB($c3,$c4,$nc);
$html .= customHTML($w,$a,$c,'HSV->RGBs');
// Output
$html .= '</div>';
echo $html;

/* FUNCIONES DE GRADUACIÓN */
// Dados dos colores RGB (0-255,0-255,0-255) y un número de colores deseados, regresa un array con todos los colores de la gradación.   
function graduateRGB($c1,$c2,$nc){
    $c = array();
    $dc = array(($c2[0]-$c1[0])/($nc-1),($c2[1]-$c1[1])/($nc-1),($c2[2]-$c1[2])/($nc-1));
    for ($i=0;$i<$nc;$i++){
        $c[$i][0]= round($c1[0]+$dc[0]*$i);
        $c[$i][1]= round($c1[1]+$dc[1]*$i);
        $c[$i][2]= round($c1[2]+$dc[2]*$i);
    }
    return $c;
}
// Dados dos colores HSV (0-360,0-100,0-100) y un número de colores deseados, regresa un array con todos los colores de la gradación en RGB.    (Hay un detalle con esta función y es que la transición se podría hacer por el lado contrario del círculo cromático)
function graduateHSV($h1,$h2,$nc){
    $h = array();
    $dh = array(($h2[0]-$h1[0])/($nc-1),($h2[1]-$h1[1])/($nc-1),($h2[2]-$h1[2])/($nc-1));
    for ($i=0;$i<$nc;$i++){
        $h[$i][0]= $h1[0]+$dh[0]*$i;
        $h[$i][1]= $h1[1]+$dh[1]*$i;
        $h[$i][2]= $h1[2]+$dh[2]*$i;
        $h[$i] = HSVtoRGB($h[$i]);
    }
    return $h;
}

/* FUNCIONES DE CONVERSIÓN. */
// Convierte a HSV (0-360,0-100,0-100) colores en RGB (0-255,0-255,0-255).
function RGBtoHSV(array $rgb) {

    $f = 0.00000001; // Factor de corrección para evitar la división por cero.

    list($R,$G,$B) = $rgb;

    $R = $R==0?$f:$R/255;
    $G = $G==0?$f:$G/255;
    $B = $B==0?$f:$B/255;

    $V = max($R,$G,$B);
    $X = min($R,$G,$B);
    $S = ($V-$X)/$V;

    $V_X = $V-$X==0?$f:$V-$X;

    $r = ($V-$R)/($V_X);
    $g = ($V-$G)/($V_X);
    $b = ($V-$B)/($V_X);    

    if ($R == $V)
        $H = $G==$X?(5+$b):(1-$g);
    elseif ($G == $V)
        $H = $B==$X?(1+$r):(3-$b);
    else
        $H = $R==$X?(3+$g):(5-$r);

    $H /= 6;

    $H = round($H*360);
    $S = round($S*100);
    $V = round($V*100);

    return array($H, $S, $V);
}

// Convierte a RGB (0-255,0-255,0-255) colores en HSV (0-360,0-100,0-100).
function HSVtoRGB(array $hsv) {
    list($H,$S,$V) = $hsv;

    $H = $H/360;
    $S = $S/100;
    $V = $V/100;

    //1
    $H *= 6;
    //2
    $I = floor($H);
    $F = $H - $I;
    //3
    $M = $V * (1 - $S);
    $N = $V * (1 - $S * $F);
    $K = $V * (1 - $S * (1 - $F));
    //4
    switch ($I) {
        case 0:
            list($R,$G,$B) = array($V,$K,$M);
            break;
        case 1:
            list($R,$G,$B) = array($N,$V,$M);
            break;
        case 2:
            list($R,$G,$B) = array($M,$V,$K);
            break;
        case 3:
            list($R,$G,$B) = array($M,$N,$V);
            break;
        case 4:
            list($R,$G,$B) = array($K,$M,$V);
            break;
        case 5:
        case 6: //for when $H=1 is given
            list($R,$G,$B) = array($V,$M,$N);
            break;
    }

    $R = round($R*255);
    $G = round($G*255);
    $B = round($B*255);

    return array($R, $G, $B);
}

// Función con un HTML de muestra para la visualización de colores, podría ser cualquier otro.
function customHTML($w,$a,$c,$header){
    $html = '<div style="float:left;text-align:center;"><h2>'.$header.'</h2>';
    foreach ($c as $color){
        $html .= '<div style="width:'.$w.'px;height:'.$a.'px;background-color:rgb('.$color[0].','.$color[1].','.$color[2].');">RGB '.$color[0].','.$color[1].','.$color[2].'</div>';
    }
    $html .= '</div>';
    return $html;
}
?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接