【问题标题】:How to interpolate a color sequence?如何插入颜色序列?
【发布时间】:2011-08-17 03:15:18
【问题描述】:

我需要逐步插入或更改一系列颜色,因此它从 colorA 到 colorB 到 colorC 再到 colorD 并返回到 colorA,这需要基于以毫秒为单位的时间,任何帮助将不胜感激(算法,伪代码会很棒)。

请注意,我正在使用 RGB,它可能是 0-255 或 0.0-1.0 范围。

这就是我到目前为止所拥有的,我需要在每个“timePeriod”上更改颜色,我计算经过的时间百分比并更改颜色,这段代码的问题是它去的时候有一个跳转从A到B到B到C等等

int millisNow = ofGetElapsedTimeMillis();
int millisSinceLastCheck = millisNow - lastTimeCheck;
if ( millisSinceLastCheck > timePeriod ) {
    lastTimeCheck = millisNow;
    millisSinceLastCheck = 0;
    colorsIndex++;
    if ( colorsIndex == colors.size()-1 ) colorsIndex = 0;
    cout << "color indes: " << colorsIndex << endl;
    cout << "color indes: " << colorsIndex + 1 << endl;
}
timeFraction = (float)(millisSinceLastCheck) / (float)(timePeriod);
float p = timeFraction;
colorT.r = colors[colorsIndex].r * p + ( colors[colorsIndex+1].r * ( 1.0 - p ) );
colorT.g = colors[colorsIndex].g * p + ( colors[colorsIndex+1].g * ( 1.0 - p ) );
colorT.b = colors[colorsIndex].b * p + ( colors[colorsIndex+1].b * ( 1.0 - p ) );
colorT.normalize();

提前致谢

【问题讨论】:

标签: c++ colors sequence interpolation openframeworks


【解决方案1】:

您的代码大部分是正确的,但是您正在反向进行插值:即您正在插值 B->A,然后是 C->B,然后是 D->C,等等。这会导致切换颜色时的不连续性。

你应该替换这个:

colorT.r = colors[colorsIndex].r * p + ( colors[colorsIndex+1].r * ( 1.0 - p ) );

与:

colorT.r = colors[colorsIndex].r * (1.0 - p) + ( colors[colorsIndex+1].r * p );

其他行也一样。

另外,正如其他人所说,使用与 RGB 不同的色彩空间可以提供更好的效果。

【讨论】:

  • 呸,你打败了我。 +1,这是(到目前为止)唯一真正回答问题而不是标题的答案。
  • 很酷的非常简单的解决方案,我唯一要做的就是处理从 C-D 到 D-A 的跳跃,但我想我可以自己弄清楚,非常感谢!我也不确定标题:) 英语不是我的第一语言
  • 如果你想让颜色变浅,只需将整个值乘以你的选择
【解决方案2】:

有两种方法可以处理插值颜色。一种快速简单(您正在做的事情),另一种稍慢但在某些情况下看起来更好。

第一个是(x * s) + (y * (1-s))的明显、简单的方法,它是纯线性插值,顾名思义。但是,在某些颜色对(例如绿色和橙色)上,中间会出现一些令人讨厌的颜色(脏棕色)。那是因为您正在使用每个组件(R、G 和 B),并且有些地方的组合令人不快。如果你只需要最基本的lerp,那么这就是你想要的方法,而且你的代码也差不多。

如果您想要更好看但速度稍慢的效果,您需要在 HSL 颜色空间中进行插值。由于色调、饱和度和亮度都是插值的,因此您可以在它们之间得到您期望的颜色,并且可以避免大多数丑陋的颜色。由于颜色通常是在某种轮子中绘制的,因此该方法意识到这一点(基本 RGB lerp 的行为就像使用 3 条离散线一样)。

要使用 HSL lerp,您需要转换 RGB 值,在结果之间进行 lerp,然后再转换回来。 This page 有一些可能有用的公式,this one 有 PHP 代码来处理它。

【讨论】:

  • 但是请注意,HSL 不是真的是一个特别好的选择。 HSL 和 HSB 易于可视化,并且往往比直接在 RGB 中工作提供更好的结果——但仅此而已。 Lab* 不能完美地对应颜色感知,但它比 HSL 或 HSB 更接近 很多
  • @Jerry:我自己对 LAB 并不是很熟悉(不过会检查它),HSL 可能不是最好的选择,但它比 RGB 更上一层楼,而且不是太复杂了。我想这取决于这里需要什么。
  • 是的,这绝对比直接使用 RGB 更上一层楼。这也是一种相当简单的转换,因此不会导致几乎一样多的额外计算,这对于实时转换很重要。
【解决方案3】:

对 R、G 和 B 分量进行插值将产生工作代码。一个缺点是您生成的步骤不一定看起来相同,即使它们在数学上是相等的。

如果这让您感到困扰,您可以将值从 RGB 转换为 L*a*b*(旨在更接近人类感知),对这些值进行插值,然后将每个插值转换回转RGB显示。

【讨论】:

  • 能否提供 Lab* 的参考资料? (从字面上看,RGB 与人类感知相距甚远。将其正确转换为 HSL 可以改善情况,但转换必须尊重人类感知的异常,但这种情况并不常见。)
  • @James Kanze:Steven Schevell 的 The Science of Color 相当不错,虽然它是关于一般颜色的,而不是特定于 Lab *。可在线免费获得的一个是brucelindbloom.com(它还涵盖了 UP 色彩空间,至少从理论上讲,这应该是该任务的更好选择)。我应该补充一点,对于我们正在讨论的事情,Bruce 有很多可能方式过分(不过,公平地说,CIELAB 也可能......)
【解决方案4】:

你已经得到的东西看起来很不错,但我会稍微简化一下数学:

int millisNow = ofGetElapsedTimeMillis();
int millisSinceLastCheck = millisNow % timerPeriod;
int colorsIndex = (millisNow / timerPerod) % (colors.size() - 1);


float p = (float)(millisSinceLastCheck) / (float)(timePeriod);
colorT.r = colors[colorsIndex+1].r * p + ( colors[colorsIndex].r * ( 1.0 - p ) );
colorT.g = colors[colorsIndex+1].g * p + ( colors[colorsIndex].g * ( 1.0 - p ) );
colorT.b = colors[colorsIndex+1].b * p + ( colors[colorsIndex].b * ( 1.0 - p ) );
colorT.normalize();

【讨论】:

    【解决方案5】:

    我们正在为我目前正在进行的一个项目执行此操作。我们只是独立处理 R、G、B 值,并根据中间有多少“步骤”从 color1 过渡到 color2。我们有离散值,因此我们有一个查找表方法,但您可以对浮点执行相同的操作,然后动态计算 RGB 值。

    如果您仍有疑问,我可以发布一些 Java 代码。

    【讨论】:

      【解决方案6】:

      分离三个分量(RBG)并对每个分量进行插值 分别使用经典的插值算法。

      【讨论】:

        猜你喜欢
        • 2015-10-30
        • 2018-04-01
        • 2013-05-13
        • 2013-01-01
        • 2020-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-31
        相关资源
        最近更新 更多