6

I'm making an iOS game using cocos2d libraries.

Lets say you have two objects that have two separate colours - defined in RGB as

Blue:    0,0,255
Yellow:  255,255,0

I want to add blue and yellow to make green.

To over complicate things, let's say that the Blue object is bigger than the Yellow object (for the sake of argument let's say that the ratio is 2:1), I'm adding twice as much blue as yellow - how to I calculate this new (light green) colour correctly.

I understand LAB * Color Space is useful for this sort of 'natural colour' kind of thing, but I'm not sure how to use it - especially in the context of a cocos2d object which (AFAIK) is limited to using RGB in its colour schemes.

I'd really appreciate practical help on how to implement this. Thanks heaps!

21/4 Update: So in LAB* blue+yellow ≠ green (which makes sense when you see they're at opposite ends of the same channel). It's actually quite a tricky problem with a little bit of discussion on SO. It seems that the ultimate answer is to use the Kubelka-Munk method that a piece of open source software called Krita uses. I can't find that anywhere (either the formula or the code itself).

This question has a link which uses HSL to work in a similar method to paint. I'm going to try to see if it works, and I'll feed back the result here.

In the meantime if anyone knows how to implement Kubelka-Munk or where I can find code to do this, or another solution, I would be very, very stoked!

Community
  • 1
  • 1
glenstorey
  • 5,134
  • 5
  • 39
  • 71
  • 1
    `Blue + Yellow` is never `Green`, though you learn that in preschool. What this actually is referring to is a simple substractive colour model such as CMYK, where `1 - Cyan - Yellow ≈ Green` (with `Cyan ≈ Blue`). – leftaroundabout Apr 13 '12 at 11:23

6 Answers6

6

There is no color model where mixing blue and yellow makes green. Try it yourself with gouache, the only way it works is cyan and yellow. This is why you should try switching from RGB to CMYK, and back if you need. Here is how it's done

void toCMYK(float red, float green, float blue, float* cmyk)
{
  float k = MIN(255-red,MIN(255-green,255-blue));
  float c = 255*(255-red-k)/(255-k); 
  float m = 255*(255-green-k)/(255-k); 
  float y = 255*(255-blue-k)/(255-k); 

  cmyk[0] = c;
  cmyk[1] = m;
  cmyk[2] = y;
  cmyk[3] = k;
}

void toRGB(float c, float m, float y, float k, float *rgb)
{
  rgb[0] = -((c * (255-k)) / 255 + k - 255);
  rgb[1] = -((m * (255-k)) / 255 + k - 255);
  rgb[2] = -((y * (255-k)) / 255 + k - 255);
}

And then in your code, mix the cyan and yellow

float cmyk1[4];
toCMYK(255, 255, 0, cmyk1);  // yellow

float cmyk2[4];
toCMYK(0, 255, 255, cmyk2);  // cyan

// Mixing colors is as simple as adding
float cmykMix[] = { cmyk1[0] + cmyk2[0], cmyk1[1] + cmyk2[1], cmyk1[2] + cmyk2[2], cmyk1[3] + cmyk2[3] };

float rgb[3];
toRGB(cmykMix[0], cmykMix[1], cmykMix[2], cmykMix[3], rgb);  

NSLog(@"RGB mix = (%f, %f, %f)", rgb[0], rgb[1], rgb[2]);

Running the code will yield: RGB mix = (0.000000, 255.000000, 0.000000)

mprivat
  • 21,582
  • 4
  • 54
  • 64
  • Thanks heaps for your answer, especially for the code. I think that this will work for yellow plus blue - but I don't know if it'll work for other colour arithmetic. I've been doing some research on this, and answers like this one: http://stackoverflow.com/a/398268/459116 make it look like LAB is the best way to go. – glenstorey Apr 14 '12 at 21:26
  • I've done it with yellow and blue model paints, where the blue was a deep blue that was not "greenish"--definitely not "cyan", and the result was definitely green. Such a thing wouldn't happen with printing dyes, but can easily happen with paints and can sometimes happen with dyes as well. (see my answer for more info) – supercat Jun 11 '12 at 22:30
2

Check the formulas on this site: http://www.easyrgb.com/index.php?X=MATH I've been doing similar thing, and it can be achieved by converting RGB->XYZ->Lab. However the computation is quite expensive(if you doing it for a lot of pixels).

And forget about RGB math when trying to mix colors if you want to obtain results similar to human eye

Michał Zygar
  • 4,052
  • 1
  • 23
  • 36
  • So objectA, objectB are coloured with RGB - convert the RGB to XYZ then to LAB, add the two colours together, then convert them back to XYZ and RGB again? – glenstorey Apr 13 '12 at 23:37
  • Yes exactly, as I wrote it requires a lot of computation(I analyzed whole image pixel by pixel) but gives decent results – Michał Zygar Apr 16 '12 at 07:16
  • When you add the two colors together in the LAB space. How is that done correctly? Just L1*0.5 + L2*0.5, A1*0.5 + A2*0.5 and B1*0.5 + B2*0.5? – Christoffer Oct 13 '13 at 22:42
2

I think, it is worth to try HSL color space. When adding colors we interpolate their Hue values (even taking in account objects weights). If the colors are 100% saturated, Luminance and Saturation values will be equal.

brigadir
  • 6,874
  • 6
  • 46
  • 81
1

Dyes don't work in the real world quite like subtractive-color models suggests. The dyes used for CYMK printing are pretty close, since they're formulated for that purpose, but many dyes made from naturally-occurring substances can behave somewhat oddly. The difficulty is that while white light is perceived as a combination of red, green, and blue, it actually consists of many different wavelengths--literally "all the colors of the rainbow"--each of which will stimulate the red, green, and blue receptors in the eye by different amounts. It is possible for two colors which appear identical to in fact contain different combinations of wavelengths; likewise, two dyes may appear identical when viewed in white light, but absorb different combinations of wavelengths. Such dyes may look identical to each other when used alone, but may yield very different-seeming results when combined with something else.

Although dyes can sometimes be tricky, however, paints are even worse. Paints contain reflective particles, and some of the light which hits a painted surface will be reflected back off the surface by the first particle it hits; in that regard, they mix like additive colors. For example, if the paint contains 20% green particles, then a significant amount of green light will be reflected, regardless of what other colors it might contain. On the other hand, some of the light which hits a painted surface will bounce around and hit multiple particles. If any of those particles absorbs a photon of some color, that photon won't be reflected. In that regard, paints behave more like subtractive colors. In practice, paints behave somewhat like additive colors, somewhat like subtractive colors, and sometimes like something weird and wacky and totally unlike either.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

If you look at the HSL colour space wheel, you can see that the "mixed" colours are between the two primaries. You could use this to your advantage by having your "Add" method really be a sort of "mix" method with something like this pseudocode example:

function int GetMixedHue(int firstHue, int secondHue, int firstWeight, int secondWeight) {
    int hueDiff = absolute(firstHue - secondHue);
    int proportionHue = hueDiff / (firstWeight + secondWeight);
    proportionHue = proportionHue * firstWeight;
    if (firstHue < secondHue)
        return firstHue + proportionHue;
    else
        return secondHue + proportionHue;
}

It might need some tweaking, and I've obviously not tested it. But the basics are there. I'm a little unsure about the weighting method but I hope this gives you a good starting point.

Bear in mind that this is just one way of doing it, there are other methods this is just the one I would choose. HSL Color Wheel

Edit:

I ended up writing this out myself in C# and ended up with the following method. I decided to not use absolute values as this actually makes it easier to work the proportion calculations.

public HSLColor MixColors(HSLColor colourA, HSLColor colourB, int proportionA, int proportionB)
{
//This is important so that colours mix correctly.So that Red + Blue = Magenta, not Green.
    int AHue = colourA.Hue;
    int BHue = colourB.Hue;
    if (colourA.Hue == 0 && colourB.Hue > 180)
    {
        AHue = 360;
    }
    else if (colourB.Hue == 0 && colourA.Hue > 180)
    {
        BHue = 360;
    }
//Get the total difference between all three aspects of the colours
    int hueDiff = BHue - AHue;
    double satDiff = colourB.Saturation - colourA.Saturation;
    double lumDiff = colourB.Luminance - colourA.Luminance;
    HSLColor newColour;
//Scale the amount of each difference proportional to the ratio.
    hueDiff = hueDiff / (proportionA + proportionB) * proportionA;
    satDiff = satDiff/ (proportionA + proportionB) * proportionA;
    lumDiff = lumDiff / (proportionA + proportionB) * proportionA;
    newColour = colourA.AddHue(hueDiff).AddSaturation(satDiff).AddLuminance(lumDiff);
    return newColour;
}
0

Actually it seems that converting RGB->XYZ->LAB does exatly the same thing as RGB->LAB

Christoffer
  • 7,470
  • 9
  • 39
  • 55