Subtractive Color Mixture Computation (Page 1)

Published March 10, 2015 by Scott Allen Burns; last updated May 28, 2021

Change Log
3/10/15 Original publication.
4/29/15 Separated portion on generating reflectance curves into a separate publication.
10/2/18 Changed matrix to be called to reflect established terminology.
10/2/18 Added link to applying the optimization methods to Rec. 2020 color space.
5/28/21 Divided long web page into a series of smaller ones for faster loading.
5/28/21 Added notice of new LHTSS method that performs better than ILLSS.

By Dennis Jarvis [CC BY-SA 2.0], via Wikimedia Commons (cropped)

Overview

I present an algorithm for computationally mixing screen colors (RGB colors) subtractively, written for a general audience. The question it addresses is, “Given two colors specified by their RGB triplets, what RGB triplet should be used to represent the color that would arise if the two colors were mixed like paint colors, i.e., mixed subtractively?” The only way I can think of doing this in a rigorous way is to employ the math behind how a stimulus (a continuous spectral power distribution) enters our eyes and is transformed into a three-dimensional color sensation by our brain. Once this process has been adequately modeled, subtractive color mixture follows directly. The approach I present here is to convert the RGB colors to spectral reflectance curves, mix the curves using the weighted geometric mean, and then convert the result back to RGB.

A Disclaimer

The algorithm described here provides a representative model for subtractive color mixture. The way actual paints mix, for example, is highly dependent upon the particular pigments being used, as well as many other factors. Be aware that you can mix a blue and a yellow paint to get green in one case, and then mix another blue (that appears IDENTICAL to the first blue and has the same RGB value) and another yellow (appearing IDENTICAL to the first yellow, with the same RGB value) and get brown or some other color as a result! There is no way to differentiate between these two different outcomes based on the RGB values of the source colors. Consequently, the colors calculated by my algorithm are plausible, generic, representative color mixture outcomes, and certainly are not predictive of any one specific paint chemistry. Some of the factors that contribute to the unpredictability of paint mixture, not represented by the RGB description of the source colors, include:

• The varying degree to which light is reflected directly off the top layer of grains of pigment and mix additively in our head.
• The “glazing” effect that comes from the ordering of layers of transparent paints, giving different results according to the order of application.
• The shift in hue that results from mixing a color with white paint due to the “undertones” of pigments.
• The difference in how transparent paints mix compared to opaque paints, due to the additional scattering possible within transparent paint layers.
• The fact that two visually identical pigments can have very different spectral reflective properties, strongly impacting how they mix with other colors.
• The difference in pigment grain sizes, which affect how they respond to tinting and shading actions.

With that said, I believe the algorithm I describe here will give very plausible results in computer graphics applications intended to mimic realistic paint mixture.

Introduction to RGB Color

A magnified view of a computer monitor. By ErnstA [CC BY-SA 3.0], via Wikimedia Commons

The colors we see on our TVs, computers, and phones, if we look closely enough, are produced by an array of tiny red, green, and blue dots. By varying the relative brightness of these three colors, we can produce a wide range of other colors through “additive” color mixture. We describe these colors using three numbers, the brightness of the R, G, and B dots, typically with whole numbers in the range 0-255, such as (R,G,B)=(217,15,145). This is how a program like Photoshop describes its RGB colors. In other settings, the RGB values are treated as floating point numbers the range 0.0 to 1.0, like (RGB)=(0.7263, 0.0112, 0.8956). Programs like Matlab use this convention.

Three-dimensional RGB color space (mouse over to animate).

The RGB “color space” is a three-dimensional space spanned by the R, G, and B axes. A point within this space represents a unique color. The origin, RGB=(0,0,0), represents black (or the darkest color the screen can produce, which is typically quite far from true black). The color RGB=(1.0,1.0,1.0) represents the color produced when R, G, and B are at their brightest, and is usually called white. There are many different RGB color spaces, differing in what specific red, green, and blue lights are used for the primaries, and what specific color it considers to be white, called the “reference white.” This document will focus on one specific space called the sRGB color space, which is commonly used with RGB display devices. It has a very specific definition that aims to make color images appear consistent across a wide variety of computer screen types.

Web colors?
When programming web pages in html, we often hear about “web colors” This is simply another way to represent an RGB color. We take the RGB numbers and convert them to base 16 (hexadecimal). The 16 hexadecimal digits are 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f and they are usually prefixed with a #. A number in the range 0-255 can be represented by two hexadecimal digits #00 to #ff. We can group the RGB pairs of digits into one 6-digit string, like #2e71cb. Since #2e=46, #71=113, and #cb=203, the web color #2e71cb is the same as (RGB)=(46,113,203).
A programmer's joke
Octal (base eight) is another common system. Here, the digits 0,1,2,3,4,5,6,7 are used, and when counting in octal, the number following 7OCT is 10OCT (which is equal to 8DEC). Naturally, it follows that programmers always confuse Halloween and Christmas because 31OCT equals 25DEC!

Subtractive Color Mixture (By Jennifer Rensel [CC BY 2.0 ], via Wikimedia Commons)

Additive Color Mixture (By en:User:Bb3cxv [CC BY-SA 3.0] via Wikimedia Commons)

When we overlap the beams of two colored flashlights on a white wall, they mix additively. When we mix two paints of different colors, they mix subtractively. The rules for color mixing are very different in the two cases. Mixing blue and yellow paint, for example, usually gives some sort of green. But mixing blue and yellow light will typically give white or a neutral gray. Mixing red and green paint usually gives a muddy color, but with light, the combination is yellow.

There are computer graphics applications where a programmer needs to mimic subtractive color mixture. An obvious example is a program that teaches how to mix paints, such as this one. But other applications, like painting programs and photo-realistic scene generation can benefit from being able to model subtractive color mixture. Yet, finding an algorithm for realistically mixing colors this way is surprisingly difficult.

A high-profile mistake
I occasionally run across examples on the web of the confusion between additive and subtractive mixture. Take this web page for example: http://dba.med.sc.edu/price/irf/Adobe_tg/models/rgbcmy.html. The GIF they show depicts a classic way to additively mix colors by spinning a multicolor disc so fast that the colors on it mix additively in our brain. This example has blue and yellow, but when it spins, the GIF shows green instead of the neutral color that should appear! The designer of this GIF was apparently unaware of how additive mixture works.

Why is This a Problem?

Here are the RGB values for some basic colors:

• red=(1,0,0)
•  green=(0,1,0)
• blue=(0,0,1)
•  cyan=(0,1,1) .
• magenta=(1,0,1)
•  yellow=(1,1,0)
• black=(0,0,0)
•  white=(1,1,1)

Cyan, magenta, and yellow are often used as subtractive “primaries.” Note how well the math works when we multiply the RGB values together when mixing them:

•  cyan=(0,1,1)  times magenta=(1,0,1) is blue=(0,0,1)
• magenta=(1,0,1) times  yellow=(1,1,0)  is red=(1,0,0)
•  cyan=(0,1,1)  times  yellow=(1,1,0)  is  green=(0,1,0)

This is just what we would expect from a subtractive mix of these colors. Unfortunately, the multiplicative mixture model breaks down pretty quickly for other pairs of colors. Mixing red and yellow, for example, just gives red as the multiplicative result, not orange as we would expect. Even worse, it appears that mixing white with any other color has no effect at all! There is no way to produce a tint of a color in this mixing model.

Others have suggested converting the RGB to other color spaces, such as L*a*b* color space (a “perceptually uniform” color space), CMYK color space, or HSV color space, before doing the mixing to help with the subtractive mix computation. In my experience, I’m not aware of any successful attempts at doing this.

The only way I can think of doing this in a robust way is to first understand how the brain sees colors and then adapt this process to the subtractive mixture of RGB-based colors. The next section will discuss the mathematics of human color perception.