为电力计生成红色和绿色之间的颜色?

148

我正在编写一个Java游戏,我想要为射击某些东西的力度实现一个力量计。

我需要编写一个函数,它接受0-100之间的整数,并根据该数字的高低返回在绿色(功率刻度上的0)和红色(功率刻度上的100)之间的颜色。

类似于音量控件的工作方式:
volume control

我需要对颜色的红、绿、蓝三个部分进行哪些操作才能生成介于绿色和红色之间的颜色?

例如,我可以运行getColor(80),它将返回橙色(其R、G、B值),或者getColor(10),它将返回更多的绿/黄色RGB值。

我知道我需要增加R、G、B值的组成部分来生成新颜色,但我不知道随着颜色从绿色到红色的转变,具体哪些部分会上升或下降。


进展:

最终我使用了HSV / HSB颜色空间,因为我更喜欢它的渐变效果(中间没有深棕色)。

我使用的函数是:

public Color getColor(double power)
{
    double H = power * 0.4; // Hue (note 0.4 = Green, see huge chart below)
    double S = 0.9; // Saturation
    double B = 0.9; // Brightness

    return Color.getHSBColor((float)H, (float)S, (float)B);
}

“功率”是0.0到1.0之间的数字。 0.0将返回明亮的红色,1.0将返回明亮的绿色。

Java 色调图表:
Java Hue Chart


我之前在这里问过同样的问题(非常相似): https://dev59.com/dnVC5IYBdhLWcg3w1E7w - Tomas Pajonk
2
你不应该反转功率吗?假设红色是最高的命中率,并且你的工作范围在0.1到0.4之间,功率越高,H值就越低。 - Adriaan Davel
你正在使用OpenGL吗?因为有一些方法可以将三角形的点设置为不同的颜色,然后在它们之间进行混合/渐变。如果您让显卡来完成这项工作,您可能会获得更好的性能。此外,代码可能更简单/更易于适应(比如,如果您想让外星人的能量计从绿色变成蓝色)。 - DarcyThomas
19个回答

207

这应该可以 - 只需线性地缩放红色和绿色值。假设您的最大红/绿/蓝值为 255 ,且n在范围 0..100 内。

R = (255 * n) / 100
G = (255 * (100 - n)) / 100 
B = 0

(经过整数数学修正,向 Ferrucio 致敬)

另一种方法是使用 HSV 颜色模型,并在饱和度和值适合的情况下将色调从0 度(红色)循环到120 度(绿色)。这应该会给出一个更令人愉悦的渐变。

这里展示了每种技术的演示 - 顶部渐变使用 RGB,底部使用 HSV:

http://i38.tinypic.com/29o0q4k.jpg


22
最好在HSV空间中进行这个操作。 - DJClayworth
@DJClayworth,是的,我选择了HSV。 - mmcdole
+1;我本来想问一个关于如何改进我在HTML/PHP中用于着色条形图的颜色算法的新问题... Stack Overflow建议这个问题与您的答案类似,您的答案帮助我解决了问题,而无需提出问题!谢谢! - beggs
你会如何编写一个可以处理任何颜色值的函数?比如,计算 RGB 20,30,190 和 RGB 69,190,10 之间的颜色? - Kokodoko
1
同样的技巧 - 在N步中从值X移动到值Y,每一步你将X增加(Y-X)/N - 虽然如果你在HSV空间中移动会更好看,而不是RGB。 - Paul Dixon

32

就我所知,以下是在HSV空间中绿-红色调的转换,转换为RGB:

blue = 0.0
if 0<=power<0.5:        #first, green stays at 100%, red raises to 100%
    green = 1.0
    red = 2 * power
if 0.5<=power<=1:       #then red stays at 100%, green decays
    red = 1.0
    green = 1.0 - 2 * (power-0.5)

上面示例中的红色、绿色、蓝色值为百分比,你可能想将它们乘以 255,以获取最常用的 0-255 范围。


14

简短的复制粘贴答案...

关于Java标准库:

int getTrafficlightColor(double value){
    return java.awt.Color.HSBtoRGB((float)value/3f, 1f, 1f);
}

在Android上:

int getTrafficlightColor(double value){
    return android.graphics.Color.HSVToColor(new float[]{(float)value*120f,1f,1f});
}

注意:value是一个介于0和1之间的数字,表示红到绿状态。


要直接转换为十六进制颜色代码,您可能想利用 String.format("#%06X", 0xFFFFFF&getTrafficlightColor(value)); - ngeek

10
如果你想要像被接受的答案建议的那样获得绿色-黄色-红色的表示,那么看一下这个。

http://jsfiddle.net/0awncw5u/2/

function percentToRGB(percent) {
    if (percent === 100) {
        percent = 99
    }
    var r, g, b;

    if (percent < 50) {
        // green to yellow
        r = Math.floor(255 * (percent / 50));
        g = 255;

    } else {
        // yellow to red
        r = 255;
        g = Math.floor(255 * ((50 - percent % 50) / 50));
    }
    b = 0;

    return "rgb(" + r + "," + g + "," + b + ")";
}


function render(i) {
    var item = "<li style='background-color:" + percentToRGB(i) + "'>" + i + "</li>";
    $("ul").append(item);
}

function repeat(fn, times) {
    for (var i = 0; i < times; i++) fn(i);
}


repeat(render, 100);
li {
    font-size:8px;
    height:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<ul></ul>


我使用这段代码得到了不同百分比的相等值...有什么想法吗?例如,以下百分比都返回RGB 1,255,0:0.277、0.222、0.111...更高的值(例如1.0)确实返回了正确的RGB值,绿色更亮,但它们在阈值后停止变化,我只能在我的比例尺上得到绿色的阴影。 - Patrick

8
在绿色和红色之间进行线性插值几乎可以奏效,但中间会出现泥巴般的棕色。最灵活、最美观的解决方案是在某个地方有一个恰好符合您要求的颜色渐变图像文件,然后查找其中的像素值。这样做具有灵活性,您可以微调渐变颜色以达到完美的效果。如果您仍然想从代码中实现它,那么最好在左侧的绿色和黄色之间,在右侧的黄色和红色之间进行插值。在RGB空间中,绿色为(R=0, G=255, B=0),黄色为(R=255, G=255, B=0),红色为(R=255, G=0, B=0) - 这是假设每个颜色分量都从0到255。

1
将光谱分为红/黄和黄/绿两部分的建议可以得到最令人愉悦的黄色结果。 - fig

5
我写了一个小函数,可以将百分比值转换为RGB整数值:
private int getGreenToRedGradientByValue(int currentValue, int maxValue)
{
    int r = ( (255 * currentValue) / maxValue );
    int g = ( 255 * (maxValue-currentValue) ) / maxValue;
    int b = 0;
    return ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
}

如果您的当前值是50,最大值是100,这个函数将返回您需要的颜色,因此,如果您使用百分比值循环该函数,则颜色值将从绿色变为红色。对于我糟糕的解释感到抱歉。


3

被接受的答案并没有真正回答问题...

C++

这是我引擎中简单的C++代码片段,它在三种任意颜色之间进行线性和高效的插值:

const MATH::FLOAT4 color1(0.0f, 1.0f, 0.0f, 1.0f);  // Green
const MATH::FLOAT4 color2(1.0f, 1.0f, 0.0f, 1.0f);  // Yellow
const MATH::FLOAT4 color3(1.0f, 0.0f, 0.0f, 1.0f);  // Red

MATH::FLOAT4 get_interpolated_color(float interpolation_factor)
{
    const float factor_color1 = std::max(interpolation_factor - 0.5f, 0.0f);
    const float factor_color2 = 0.5f - fabs(0.5f - interpolation_factor);
    const float factor_color3 = std::max(0.5f - interpolation_factor, 0.0f);

    MATH::FLOAT4 color;

    color.x = (color1.x * factor_color1 +
               color2.x * factor_color2 +
               color3.x * factor_color3) * 2.0f;

    color.y = (color1.y * factor_color1 +
               color2.y * factor_color2 +
               color3.y * factor_color3) * 2.0f;

    color.z = (color1.z * factor_color1 +
               color2.z * factor_color2 +
               color3.z * factor_color3) * 2.0f;

    color.w = 1.0f;

    return(color);
}

interpolation_factor假定在0.0 ... 1.0范围内。
颜色假定在0.0 ... 1.0范围内(例如OpenGL)。

C#

这是同一个函数的C#代码:

private readonly Color mColor1 = Color.FromArgb(255, 0, 255, 0);
private readonly Color mColor2 = Color.FromArgb(255, 255, 255, 0);
private readonly Color mColor3 = Color.FromArgb(255, 255, 0, 0);

private Color GetInterpolatedColor(double interpolationFactor)
{
    double interpolationFactor1 = Math.Max(interpolationFactor - 0.5, 0.0);
    double interpolationFactor2 = 0.5 - Math.Abs(0.5 - interpolationFactor);
    double interpolationFactor3 = Math.Max(0.5 - interpolationFactor, 0.0);

    return (Color.FromArgb(255,
                (byte)((mColor1.R * interpolationFactor1 +
                        mColor2.R * interpolationFactor2 +
                        mColor3.R * interpolationFactor3) * 2.0),

                (byte)((mColor1.G * interpolationFactor1 +
                        mColor2.G * interpolationFactor2 +
                        mColor3.G * interpolationFactor3) * 2.0),

                (byte)((mColor1.B * interpolationFactor1 +
                        mColor2.B * interpolationFactor2 +
                        mColor3.B * interpolationFactor3) * 2.0)));
}

interpolationFactor 假设在 0.0 ... 1.0 范围内。
颜色假设在 0 ... 255 范围内。


1
C#代码对我非常有效(已点赞),但其中有一个拼写错误,interpolationFactorColor2 应更正为 interpolationFactor2。 - DJIDave
此外,为了完整起见,这是我如何利用该函数将 RGB 返回到 MVC razr 视图:double n = actualValue / maxValue; // 这会给出 0-1 的比率var colour = GetInterpolatedColor(n); return string.Concat("#", colour.R.ToString("X"), colour.G.ToString("X"), colour.B.ToString("X")); - DJIDave
@DJIDave:哇,多么明显和愚蠢的错误啊。我已经修复了。谢谢! - Tara

3

我认为您想要在HSV空间中的线段和RGB空间中的线段之间找到一个平衡点(HSV空间中心的黄色稍微太亮,而RGB空间中央的颜色很丑陋)。我会使用这个方案,其中power = 0将提供绿色,power = 50将给出略微暗淡的黄色,power = 100将提供红色。

blue = 0;
green = 255 * sqrt( cos ( power * PI / 200 ));
red = 255 * sqrt( sin ( power * PI / 200 )); 

2

这里提供了Swift和HSV比例的复制粘贴解决方案:

UIColor初始化器接受[0, 1]之间的色相、饱和度和亮度,因此对于给定的在[0, 1]之间,我们有:

let hue:        CGFloat = value / 3
let saturation: CGFloat = 1 // Or choose any
let brightness: CGFloat = 1 // Or choose any
let alpha:      CGFloat = 1 // Or choose any

let color = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)

2
import java.awt.Color;

public class ColorUtils {

    public static Color interpolate(Color start, Color end, float p) {
        float[] startHSB = Color.RGBtoHSB(start.getRed(), start.getGreen(), start.getBlue(), null);
        float[] endHSB = Color.RGBtoHSB(end.getRed(), end.getGreen(), end.getBlue(), null);

        float brightness = (startHSB[2] + endHSB[2]) / 2;
        float saturation = (startHSB[1] + endHSB[1]) / 2;

        float hueMax = 0;
        float hueMin = 0;
        if (startHSB[0] > endHSB[0]) {
            hueMax = startHSB[0];
            hueMin = endHSB[0];
        } else {
            hueMin = startHSB[0];
            hueMax = endHSB[0];
        }

        float hue = ((hueMax - hueMin) * p) + hueMin;

        return Color.getHSBColor(hue, saturation, brightness);
    }
}

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