Algorithm for Additive Color Mixing for RGB Values

2019-01-01 15:04发布

I'm looking for an algorithm to do additive color mixing for RGB values.

Is it as simple as adding the RGB values together to a max of 256?

(r1, g1, b1) + (r2, g2, b2) =
    (min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256))  

10条回答
余生无你
2楼-- · 2019-01-01 15:08

PYTHON COLOUR MIXING THROUGH ADDITION IN CMYK SPACE

One possible way to do this is to first convert the colours to CMYK format, add them there and then reconvert to RGB.

Here is an example code in 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)

The result to your question would then be (assuming a half-half mixture of your two colours:

r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)])

where the 0.5's are there to say that we mix 50% of the first colour with 50% of the second colour.

查看更多
何处买醉
3楼-- · 2019-01-01 15:10

Have written/used something like @Markus Jarderot's sRGB blending answer (which is not gamma corrected since that is the default legacy) using 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 / alpha + back.red   * back.alpha * (1.0 - front.alpha));
green = (front.green * front.alpha / alpha + back.green * back.alpha * (1.0 - front.alpha));
blue  = (front.blue  * front.alpha / 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   * front.alpha);
green = (back.green * (1.0 - front.alpha) + front.green * front.alpha);
blue  = (back.blue  * (1.0 - front.alpha) + front.blue  * front.alpha);

//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   * front.alpha) / 255;
green = (back.green * (255 - front.alpha) + front.green * front.alpha) / 255;
blue  = (back.blue  * (255 - front.alpha) + front.blue  * front.alpha) / 255;

more info: what-every-coder-should-know-about-gamma

查看更多
骚的不知所云
4楼-- · 2019-01-01 15:12

Fun fact: Computer RGB values are derived from the square root of photon flux. So as a general function, your math should take that into account. The general function for this for a given channel is:

blendColorValue(a, b, t)
    return sqrt((1 - t) * a^2 + t * b^2)

Where a and b are the colors to blend, and t is a number from 0-1 representing the point in the blend you want between a and b.

The alpha channel is different; it doesn't represent photon intensity, just the percent of background that should show through; so when blending alpha values, the linear average is enough:

blendAlphaValue(a, b, t)
    return (1-t)*a + t*b;

So, to handle blending two colors, using those two functions, the following pseudocode should do you good:

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

Incidentally, I long for a programming language and keyboard that both permits representing math that (or more) cleanly (the combining overline unicode character doesn't work for superscripts, symbols, and a vast array of other characters) and interpreting it correctly. sqrt((1-t)*pow(a, 2) + t * pow(b, 2)) just doesn't read as clean.

查看更多
步步皆殇っ
5楼-- · 2019-01-01 15:13

It depends on what you want, and it can help to see what the results are of different methods.

If you want

Red + Black        = Red
Red + Green        = Yellow
Red + Green + Blue = White
Red + White        = White 
Black + White      = White

then adding with a clamp works (e.g. min(r1 + r2, 255)) This is more like the light model you've referred to.

If you want

Red + Black        = Dark Red
Red + Green        = Dark Yellow
Red + Green + Blue = Dark Gray
Red + White        = Pink
Black + White      = Gray

then you'll need to average the values (e.g. (r1 + r2) / 2) This works better for lightening/darkening colors and creating gradients.

查看更多
浪荡孟婆
6楼-- · 2019-01-01 15:14

When I came here I didn't find the "additive color mixing" algorithm I was actually looking for, which is also available in Photoshop and is described as "Screen" on Wikipedia. (Aka "brighten" or "invert multiply".) It produces a result similar to two light sources being combined.

With Screen blend mode the values of the pixels in the two layers are inverted, multiplied, and then inverted again. This yields the opposite effect to multiply. The result is a brighter picture.

Here it is:

// (rgb values are 0-255)
function screen(color1, color2) {
    var r = Math.round((1 - (1 - color1.R / 255) * (1 - color2.R / 255)) * 255);
    var g = Math.round((1 - (1 - color1.G / 255) * (1 - color2.G / 255)) * 255);
    var b = Math.round((1 - (1 - color1.B / 255) * (1 - color2.B / 255)) * 255);
    return new Color(r, g, b);
}
查看更多
初与友歌
7楼-- · 2019-01-01 15:16

Yes, it is as simple as that. Another option is to find the average (for creating gradients).

It really just depends on the effect you want to achieve.

However, when Alpha gets added, it gets complicated. There are a number of different methods to blend using an alpha.

An example of simple alpha blending: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

查看更多
登录 后发表回答