Как интерполировать цветовую последовательность? - PullRequest
2 голосов
/ 02 мая 2011

Мне нужно интерполировать или постепенно менять последовательность цветов, чтобы она переходила от 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();

Заранее спасибо

Ответы [ 6 ]

10 голосов
/ 02 мая 2011

Ваш код в основном правильный, но вы выполняете интерполяцию в обратном направлении: т.е. вы интерполируете 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, может обеспечить лучшие результаты.

8 голосов
/ 02 мая 2011

Существует два способа обработки интерполяционных цветов. Один - быстрый и простой (что вы делаете), другой - немного медленнее, но в некоторых случаях он может выглядеть лучше.

Первый - это очевидный, простой метод (x * s) + (y * (1-s)), который представляет собой чисто линейную интерполяцию и выполняет то, что предлагает название. Тем не менее, на некоторых цветовых парах (скажем, зеленый и оранжевый), вы получаете некоторые неприятные цвета в середине (грязно-коричневый). Это потому, что вы лепите каждый компонент (R, G и B), и есть моменты, когда комбинация неприятна. Если вам нужен самый простой lerp, то это тот метод, который вам нужен, и ваш код примерно верен.

Если вам нужен эффект, который выглядит лучше, но немного медленнее, вам нужно интерполировать в цветовом пространстве HSL. Так как оттенок, насыщенность и яркость интерполируются, вы получаете цвет, который ожидаете между ними, и можете избежать большинства уродливых. Так как цвета обычно рисуются в каком-то колесе, этот метод знает об этом (где в качестве основного рычага RGB действует так, как будто он работает с 3 дискретными линиями).

Чтобы использовать lepp HSL, вам нужно конвертировать значения RGB, переходить между результатами и конвертировать обратно. Эта страница имеет некоторые формулы, которые могут быть полезны для этого, а эта имеет PHP-код для ее обработки.

1 голос
/ 02 мая 2011

То, что у вас уже есть, выглядит очень хорошо, но я бы немного упростил математику:

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();
1 голос
/ 02 мая 2011

При интерполяции компонентов R, G и B будет получен рабочий код. Единственным недостатком является то, что выполняемые вами шаги не обязательно будут выглядеть одинаково, даже если они математически равны.

Если вас это беспокоит, вы можете преобразовать значения из RGB во что-то вроде L * a * b * (которое предназначено для более точного соответствия человеческому восприятию), выполнить интерполяцию этих значений, а затем преобразовать каждое интерполированное значение обратно в RGB для отображения.

0 голосов
/ 02 мая 2011

Мы делаем это над проектом, над которым я сейчас работаю. Мы просто обрабатываем значения R, G, B независимо друг от друга и переходим от color1 к color2 в зависимости от того, сколько «шагов» между ними. У нас есть дискретные значения, поэтому у нас есть подход с использованием таблицы поиска, но вы можете сделать то же самое с плавающей запятой и просто динамически вычислять значения RGB.

Если у вас остались вопросы, я мог бы опубликовать код Java.

0 голосов
/ 02 мая 2011

Разделите три компонента (RBG) и интерполируйте каждый отдельно, используя классический алгоритм интерполяции.

...