http://www.fourmilab.ch/documents/specrend/ содержит довольно подробное описание того, как преобразовать длину волны в компоненты CIE (которые примерно соответствуют выходным сигналам трех типов конических датчиков в ваших глазах), а затем как преобразовать их в значения RGB (с предупреждением, что некоторые длины волн не имеют эквивалентов RGB в типичной гамме RGB).
Или: существуют различные «воспринимаемые однородные цветовые пространства», такие как CIE L * a * b * (см., Например, http://en.wikipedia.org/wiki/Lab_color_space);, вы можете выбрать один из них, сделать равные шаги вдоль прямой линии, соединяющей ваш старт и конец цвета в этом пространстве и конвертировать в RGB.
Любой из них, вероятно, будет излишним для вашего приложения, и нет никаких очевидных причин, почему они должны быть намного - или любой - лучше, чем что-то более простое и чисто эмпирическое. Так почему бы не сделать следующее:
- Выберите начальный и конечный цвета. Для простоты предположим, что они имеют S = 1 и V = 1 в пространстве HSV. Запишите их вниз.
- Посмотрите на оттенок "спектр", который вы разместили, и найдите цвет, который выглядит вам примерно посередине между вашей начальной и конечной точками. Запишите это.
- Теперь снова пополам: найдите цвета на полпути между началом и серединой и на полпути между серединой и концом.
- Повторите один или два раза больше, чтобы вы разбили шкалу оттенков на 8 или 16 "перцептивно равных" частей.
- Преобразовать в RGB, вставить их в таблицу поиска и выполнить линейную интерполяцию между ними.
- Изменяйте значения RGB, пока у вас не получится что-то, что выглядит хорошо.
Это совершенно нереально, и в нем нет ничего принципиального, но, вероятно, он будет работать довольно хорошо, и окончательный код будет в основном тривиальным:
void compute_rgb(int * rp, int * gp, int * bp, int t) {
// t in the range 0..255 (for convenience)
int segment = t>>5; // 0..7
int delta = t&31;
int a=rgb_table[segment].r, b=rgb_table[segment+1].r;
*rp = a + ((delta*(b-a))>>5);
a=rgb_table[segment].g; b=rgb_table[segment+1].g;
*gp = a + ((delta*(b-a))>>5);
a=rgb_table[segment].b; b=rgb_table[segment+1].b;
*bp = a + ((delta*(b-a))>>5);
}
(вы можете сделать код несколько понятнее, если не хотите сохранять каждый доступный цикл).
Что бы это ни стоило, мои глаза ставят точки деления на значения оттенков около (0), 40, 60, 90, 150, 180, 240, 270, (300). Ваш пробег может отличаться.