在.NET中实现三种颜色之间的颜色插值

8
我想要平滑地插值从颜色A(我们称其为红色)到颜色C(我们称其为绿色),经过颜色B(我们称其为黄色),基于某个变量的值。
如果变量=100,我想要纯绿色。 如果变量=50,我想要纯黄色。 如果变量=0,我想要纯红色。
我了解您可以将每个RGB三元组视为三维空间中的坐标。我正在寻找一种快速而简单的线性插值技巧,它可以与.NET Color类型的特定布局清晰地配合使用(ARGB等分离值)。

1
不能通过三个随机点线性化。 - RBarryYoung
2个回答

17

首先,你要求进行线性插值,但你没有说明颜色B位于颜色A和颜色C之间的直线上;这是必须的。其次,你没有明确指定,但我做了一个简化的假设,即颜色B是颜色A和颜色C之间直线的中点;如果不是这样的话,下面的代码很容易修改。最后,我改变了你对参数的假设,将它从0到100之间的整数改为了0到1之间的双精度浮点型。在后一种情况下,代码更容易编写和理解,并且仍然可以与前一种情况一起使用(将输入除以100)。

class ColorInterpolator {
    delegate byte ComponentSelector(Color color);
    static ComponentSelector _redSelector = color => color.R;
    static ComponentSelector _greenSelector = color => color.G;
    static ComponentSelector _blueSelector = color => color.B;

    public static Color InterpolateBetween(
        Color endPoint1,
        Color endPoint2,
        double lambda) {
        if (lambda < 0 || lambda > 1) {
            throw new ArgumentOutOfRangeException("lambda");
        }
        Color color = Color.FromRgb(
            InterpolateComponent(endPoint1, endPoint2, lambda, _redSelector),
            InterpolateComponent(endPoint1, endPoint2, lambda, _greenSelector),
            InterpolateComponent(endPoint1, endPoint2, lambda, _blueSelector)
        );

        return color;
    }

    static byte InterpolateComponent(
        Color endPoint1,
        Color endPoint2,
        double lambda,
        ComponentSelector selector) {
        return (byte)(selector(endPoint1)
            + (selector(endPoint2) - selector(endPoint1)) * lambda);
    }
}
如果颜色B不是颜色A和颜色C之间的中点,你如何进行修改?最简单的方法如下。如果参数(我称之为“lambda”)小于0.5,则将lambda乘以2并返回颜色A和颜色B之间的插值颜色。如果参数大于0.5,则将lambda乘以2再减去1(这将[0.5, 1]映射到[0, 1]),然后返回颜色B和颜色C之间的插值颜色。
如果您不喜欢颜色B必须在颜色A和颜色C之间的限制,则可以使用刚刚描述的修改来对颜色进行分段线性插值。
最后,您没有指定是否要插值所谓的alpha值("ARGB"中的'A')。上述代码很容易进行修改以处理这种情况。添加一个额外的ComponentSelector定义为color => color.A,使用InterpolateComponent插值该值,并使用Color.FromArgb(int, int, int, int)重载的Color.FromArgb

为了获得更通用的解决方案,可能值得查看我在重复问题上的答案 - Steven Jeuris

5

另一种使用高斯分布来混合颜色的方法是(使用 0.0-1.0 范围内的任意数量的颜色,增加 sigma_2 值以增加混合)

public static Color InterpolateColor(Color[] colors, double x)
{
    double r = 0.0, g = 0.0, b = 0.0;
    double total = 0.0;
    double step = 1.0 / (double)(colors.Length - 1);
    double mu = 0.0;
    double sigma_2 = 0.035;

    foreach (Color color in colors)
    {                
        total += Math.Exp(-(x - mu) * (x - mu) / (2.0 * sigma_2)) / Math.Sqrt(2.0 * Math.PI * sigma_2);
        mu += step;
    }

    mu = 0.0;
    foreach(Color color in colors)
    {                
        double percent = Math.Exp(-(x - mu) * (x - mu) / (2.0 * sigma_2)) / Math.Sqrt(2.0 * Math.PI * sigma_2);
        mu += step;

        r += color.R * percent / total;
        g += color.G * percent / total;
        b += color.B * percent / total;
    }

    return Color.FromArgb(255, (int)r, (int)g, (int)b);
}

更多信息请参考http://en.wikipedia.org/wiki/Normal_distribution

混合3种颜色的示例:

enter image description here


2
我相信虽然其他人可能已经回答了原始问题,但这个回复的实用性是无穷的。谢谢! - Ayson Baxter

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