Алгоритм аддитивного смешивания цветов для значений RGB
Я ищу алгоритм для аддитивного смешивания цветов для значений RGB.
это так же просто, как добавление значений RGB вместе до максимума 256?
(r1, g1, b1) + (r2, g2, b2) =
(min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256))
9 ответов:
это зависит от того, что вы хотите, и это может помочь увидеть, что результаты различных методов.
если вы хотите
Red + Black = Red Red + Green = Yellow Red + Green + Blue = White Red + White = White Black + White = Whiteзатем добавление с зажимом работает (например
min(r1 + r2, 255)
) Это больше похоже на модель Света, которую вы упомянули.если вы хотите
Red + Black = Dark Red Red + Green = Dark Yellow Red + Green + Blue = Dark Gray Red + White = Pink Black + White = Grayзатем вам нужно будет усреднить значения (например,
(r1 + r2) / 2
) это работает лучше для осветления / затемнения цветов и создания градиентов.
для смешивания с использованием Альфа-каналов можно использовать следующие формулы:
r = new Color(); r.A = 1 - (1 - fg.A) * (1 - bg.A); if (r.A < 1.0e-6) return r; // Fully transparent -- R,G,B not important r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A; r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A; r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A;
fg
- это цвет краски.bg
- это фон.r
результирующий цвет.1.0e-6
это просто очень малое количество, чтобы компенсировать ошибки округления.Примечание: все переменные, используемые здесь находятся в диапазоне [0.0, 1.0]. Вы должны разделить или умножить на 255, если вы хотите использовать значения в диапазоне [0, 255].
например, 50% красный поверх 50% зеленый:
// background, 50% green var bg = new Color { R = 0.00, G = 1.00, B = 0.00, A = 0.50 }; // paint, 50% red var fg = new Color { R = 1.00, G = 0.00, B = 0.00, A = 0.50 }; // The result var r = new Color(); r.A = 1 - (1 - fg.A) * (1 - bg.A); // 0.75 r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A; // 0.67 r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A; // 0.33 r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A; // 0.00
результирующий цвет-это:
(0.67, 0.33, 0.00, 0.75)
, или 75% коричневый (или темно-оранжевый).
вы также можете изменить эти формулы:
var bg = new Color(); if (1 - fg.A <= 1.0e-6) return null; // No result -- 'fg' is fully opaque if (r.A - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent if (r.A - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important bg.A = 1 - (1 - r.A) / (1 - fg.A); bg.R = (r.R * r.A - fg.R * fg.A) / (bg.A * (1 - fg.A)); bg.G = (r.G * r.A - fg.G * fg.A) / (bg.A * (1 - fg.A)); bg.B = (r.B * r.A - fg.B * fg.A) / (bg.A * (1 - fg.A));
или
var fg = new Color(); if (1 - bg.A <= 1.0e-6) return null; // No result -- 'bg' is fully opaque if (r.A - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent if (r.A - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important fg.A = 1 - (1 - r.A) / (1 - bg.A); fg.R = (r.R * r.A - bg.R * bg.A * (1 - fg.A)) / fg.A; fg.G = (r.G * r.A - bg.G * bg.A * (1 - fg.A)) / fg.A; fg.B = (r.B * r.A - bg.B * bg.A * (1 - fg.A)) / fg.A;
формулы рассчитают, что цвет фона или краски должен быть для получения заданного результирующего цвета.
если ваш фон непрозрачен, результат также будет непрозрачным. Затем цвет переднего плана может принимать диапазон значений с различными альфа-значение. Для каждого канала (красный, зеленый и синий) вы должны проверить, какой диапазон букв приводит к допустимым значениям (0 - 1).
забавный факт: значения RGB компьютера получены из квадратного корня потока фотонов. Поэтому в качестве общей функции ваша математика должна учитывать это. Общая функция для этого для данного канала:
blendColorValue(a, b, t) return sqrt((1 - t) * a^2 + t * b^2)
где A и b-цвета для смешивания, а t-число от 0-1, представляющее точку в смеси, которую вы хотите между a и b.
альфа-канал отличается; он не представляет интенсивность фотонов, а только процент фона, который должен показать до конца; поэтому при смешивании Альфа-значений достаточно линейного среднего:
blendAlphaValue(a, b, t) return (1-t)*a + t*b;
Итак, чтобы обрабатывать смешивание двух цветов, используя эти две функции, следующий псевдокод должен вам помочь:
blendColors(c1, c2, t) ret [r, g, b].each n -> ret[n] = blendColorValue(c1[n], c2[n], t) ret.alpha = blendAlphaValue(c1.alpha, c2.alpha, t) return ret
кстати, я стремлюсь к языку программирования и клавиатуре, которые позволяют представлять математику, которая (или более) чисто (объединяющий символ unicode overline не работает для надстроек, символов и огромного массива других символов) и интерпретирует ее правильно. sqrt ((1-t)*pow(a, 2) + t * pow (b, 2)) просто не читается как чистый.
несколько пунктов:
- Я думаю, что вы хотите использовать min вместо max
- Я думаю, что вы хотите использовать 255 вместо 256
Это даст:
(r1, g1, b1) + (r2, g2, b2) = (min(r1+r2, 255), min(g1+g2, 255), min(b1+b2, 255))
однако, "естественный" способ смешивания цветов заключается в использовании среднего, а затем вам не нужно min:
(r1, g1, b1) + (r2, g2, b2) = ((r1+r2)/2, (g1+g2)/2, (b1+b2)/2)
функция Javascript для смешивания цветов rgba
c1, c2 и результат-JSON, как c1={r:0.5,g: 1, b: 0,a:0.33}
var rgbaSum = function(c1, c2){ var a = c1.a + c2.a*(1-c1.a); return { r: (c1.r * c1.a + c2.r * c2.a * (1 - c1.a)) / a, g: (c1.g * c1.a + c2.g * c2.a * (1 - c1.a)) / a, b: (c1.b * c1.a + c2.b * c2.a * (1 - c1.a)) / a, a: a } }
PYTHONцветадля смешиваниячерездополнениена CMYKпробел
один из возможных способов сделать это, чтобы сначала преобразовать цвета CMYK формат, добавьте их там, а затем повторно преобразуйте в RGB.
вот пример кода на Python:
rgb_scale = 255 cmyk_scale = 100 def rgb_to_cmyk(self,r,g,b): if (r == 0) and (g == 0) and (b == 0): # black return 0, 0, 0, cmyk_scale # rgb [0,255] -> cmy [0,1] c = 1 - r / float(rgb_scale) m = 1 - g / float(rgb_scale) y = 1 - b / float(rgb_scale) # extract out k [0,1] min_cmy = min(c, m, y) c = (c - min_cmy) m = (m - min_cmy) y = (y - min_cmy) k = min_cmy # rescale to the range [0,cmyk_scale] return c*cmyk_scale, m*cmyk_scale, y*cmyk_scale, k*cmyk_scale def cmyk_to_rgb(self,c,m,y,k): """ """ r = rgb_scale*(1.0-(c+k)/float(cmyk_scale)) g = rgb_scale*(1.0-(m+k)/float(cmyk_scale)) b = rgb_scale*(1.0-(y+k)/float(cmyk_scale)) return r,g,b def ink_add_for_rgb(self,list_of_colours): """input: list of rgb, opacity (r,g,b,o) colours to be added, o acts as weights. output (r,g,b) """ C = 0 M = 0 Y = 0 K = 0 for (r,g,b,o) in list_of_colours: c,m,y,k = rgb_to_cmyk(r, g, b) C+= o*c M+=o*m Y+=o*y K+=o*k return cmyk_to_rgb(C, M, Y, K)
результат на ваш вопрос будет тогда (предполагая половину-половину смеси из ваших двух цветов:
r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)])
где 0.5 там сказать, что мы смешиваем 50% первого цвета с 50% второго цвета.
Да, это так просто. Другой вариант-найти среднее значение (для создания градиентов).
Это действительно просто зависит от эффекта, который вы хотите достичь.
однако, когда Альфа добавляется, это становится сложным. Существует несколько различных методов смешивания с использованием Альфа.
пример простого альфа-смешивания: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
вот высокооптимизированный, автономный класс c++, public domain, с плавающей точкой и двумя по-разному оптимизированными 8-битными механизмами смешивания как в функциональных, так и в макроформатах, а также техническое обсуждение как проблемы, так и того, как и важность оптимизации этой проблемы:
написал/использовал что-то вроде @Markus Jarderot ' s
sRGB
ответ на смешивание (который не исправляется гамма, так как это наследие по умолчанию) с использованием C++//same as Markus Jarderot's answer float red, green, blue; alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha)); red = (front.red * front.alpha / result.alpha + back.red * back.alpha * (1.0 - front.alpha)); green = (front.green * front.alpha / result.alpha + back.green * back.alpha * (1.0 - front.alpha)); blue = (front.blue * front.alpha / result.alpha + back.blue * back.alpha * (1.0 - front.alpha)); //faster but equal output alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha)); red = (back.red * (1.0 - front.alpha) + front.red * frontAlpha); green = (back.green * (1.0 - front.alpha) + front.green * frontAlpha); blue = (back.blue * (1.0 - front.alpha) + front.blue * frontAlpha); //even faster but only works when all values are in range 0 to 255 int red, green, blue; alpha = (255 - (255 - back.alpha)*(255 - front.alpha)); red = (back.red * (255 - front.alpha) + front.red * frontAlpha) / 255; green = (back.green * (255 - front.alpha) + front.green * frontAlpha) / 255; blue = (back.blue * (255 - front.alpha) + front.blue * frontAlpha) / 255;
подробнее: что-каждый-кодер-должен-знать-о-гамма