Eu estou procurando um algoritmo para fazer mistura de colors aditivas para valores RGB.
É tão simples quanto adicionar os valores RGB juntos a um máximo de 256?
(r1, g1, b1) + (r2, g2, b2) = (min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256))
Depende do que você quer e pode ajudar a ver quais são os resultados de diferentes methods.
Se você quiser
Vermelho + preto = vermelho Vermelho + Verde = Amarelo Vermelho + Verde + Azul = Branco Vermelho + Branco = Branco Preto + branco = branco
em seguida, adicionando com um grampo funciona (por exemplo, min(r1 + r2, 255)
) Este é mais parecido com o modelo de luz que você se referiu.
Se você quiser
Vermelho + preto = vermelho escuro Vermelho + Verde = Amarelo Escuro Vermelho + Verde + Azul = Cinza Escuro Vermelho + branco = rosa Preto + branco = cinza
então você precisará calcular a média dos valores (por exemplo, (r1 + r2) / 2
). Isso funciona melhor para clarear / escurecer colors e criar gradientes.
Para misturar usando canais alfa, você pode usar essas fórmulas:
r = new Color(); rA = 1 - (1 - fg.A) * (1 - bg.A); if (rA < 1.0e-6) return r; // Fully transparent -- R,G,B not important rR = fg.R * fg.A / rA + bg.R * bg.A * (1 - fg.A) / rA; rG = fg.G * fg.A / rA + bg.G * bg.A * (1 - fg.A) / rA; rB = fg.B * fg.A / rA + bg.B * bg.A * (1 - fg.A) / rA;
fg
é a cor da tinta. bg
é o fundo. r
é a cor resultante. 1.0e-6
é apenas um número muito pequeno, para compensar erros de arredondamento.
NOTA: Todas as variables usadas aqui estão no intervalo [0.0, 1.0]. Você tem que dividir ou multiplicar por 255 se quiser usar valores no intervalo [0, 255].
Por exemplo, 50% vermelho em cima de 50% verde:
// 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(); rA = 1 - (1 - fg.A) * (1 - bg.A); // 0.75 rR = fg.R * fg.A / rA + bg.R * bg.A * (1 - fg.A) / rA; // 0.67 rG = fg.G * fg.A / rA + bg.G * bg.A * (1 - fg.A) / rA; // 0.33 rB = fg.B * fg.A / rA + bg.B * bg.A * (1 - fg.A) / rA; // 0.00
A cor resultante é: (0.67, 0.33, 0.00, 0.75)
ou 75% marrom (ou laranja escuro).
Você também pode inverter essas fórmulas:
var bg = new Color(); if (1 - fg.A <= 1.0e-6) return null; // No result -- 'fg' is fully opaque if (rA - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent if (rA - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important bg.A = 1 - (1 - rA) / (1 - fg.A); bg.R = (rR * rA - fg.R * fg.A) / (bg.A * (1 - fg.A)); bg.G = (rG * rA - fg.G * fg.A) / (bg.A * (1 - fg.A)); bg.B = (rB * rA - fg.B * fg.A) / (bg.A * (1 - fg.A));
ou
var fg = new Color(); if (1 - bg.A <= 1.0e-6) return null; // No result -- 'bg' is fully opaque if (rA - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent if (rA - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important fg.A = 1 - (1 - rA) / (1 - bg.A); fg.R = (rR * rA - bg.R * bg.A * (1 - fg.A)) / fg.A; fg.G = (rG * rA - bg.G * bg.A * (1 - fg.A)) / fg.A; fg.B = (rB * rA - bg.B * bg.A * (1 - fg.A)) / fg.A;
As fórmulas calcularão que o fundo ou a cor da pintura teriam que ser para produzir a cor resultante resultante.
Se o seu fundo for opaco, o resultado também será opaco. A cor do primeiro plano pode então ter um intervalo de valores com diferentes valores alfa. Para cada canal (vermelho, verde e azul), você precisa verificar qual intervalo de alfas resulta em valores válidos (0 - 1).
Curiosidade: Os valores RGB do computador são derivados da raiz quadrada do stream de fótons. Então, como uma function geral, sua matemática deve levar isso em conta. A function geral para isso para um determinado canal é:
blendColorValue(a, b, t) return sqrt((1 - t) * a^2 + t * b^2)
Onde a e b são as colors a serem mescladas, e t é um número de 0 a 1 representando o ponto na mescla que você deseja entre a e b.
O canal alfa é diferente; não representa a intensidade do fóton, apenas a porcentagem de fundo que deve aparecer; então, ao misturar valores alfa, a média linear é suficiente:
blendAlphaValue(a, b, t) return (1-t)*a + t*b;
Então, para lidar com a mistura de duas colors, usando essas duas funções, o seguinte pseudocódigo deve ser bom para você:
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
A propósito, eu anseio por uma linguagem de programação e teclado que permita representar a matemática (ou mais) de forma limpa (o caractere unicode da linha de combinação não funciona para sobrescritos, símbolos e uma vasta gama de outros caracteres) e interpretá-lo corretamente. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) simplesmente não lê como limpa.
Alguns pontos:
Isso vai dar:
(r1, g1, b1) + (r2, g2, b2) = (min (r1 + r2, 255), min (g1 + g2, 255), min (b1 + b2, 255))
No entanto, a maneira “natural” de misturar colors é usar a média, e então você não precisa do min:
(r1, g1, b1) + (r2, g2, b2) = ((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2)
c1, c2 e resultado – JSon é como 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 COLOR MISTURA ATRAVÉS DE ADIÇÃO EM ESPAÇO CMYK
Uma maneira possível de fazer isso é primeiro converter as colors para o formato CMYK , adicioná-las e depois reconverter para RGB.
Aqui está um exemplo de código em 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)
O resultado para a sua pergunta seria então (assumindo uma meia metade das suas duas colors:
r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)])
onde os 0.5’s estão lá para dizer que nós misturamos 50% da primeira cor com 50% da segunda cor.
Sim, é tão simples quanto isso. Outra opção é encontrar a média (para criar gradientes).
Isso realmente depende apenas do efeito que você deseja alcançar.
No entanto, quando Alpha é adicionado, fica complicado. Existem vários methods diferentes para mesclar usando um alfa.
Um exemplo de mistura alfa simples: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
Aqui está uma class c ++ autônoma altamente otimizada, domínio público, com ponto flutuante e dois mecanismos de mistura de 8 bits otimizados de maneira diferente nos formatos de function e macro, assim como uma discussão técnica do problema em questão e como e a importância de otimização deste problema:
Escreveu / usou algo como a resposta de mistura de sRGB
@Markus Jarderot (que não é corrigida por gama, já que é o legado padrão) usando 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;
mais informações: what-every-coder-should-know-about-gama