Generate list of k distant unique colors

Alec Jacobson

May 02, 2015

weblog/

As far as I know, the best way to do picking in OpenGL is still to render each pickable object (pickle?) using a unique color. Here's what I use to get k distant and unique RGB colors. The idea is very straightforward. Sample the RGB color cube at points on the k lattice. Here's what that looks like for k = 103

rgb cube

This is easy to generate in matlab with:

k = 10*10*10;
s = ceil(k^(1/3));
[R,G,B] = ind2sub([s s s],1:k);
rgb = uint8([R;G;B]'-1)/(s-1)*255);

Here's a plot of the colors in one image:

k unique colors in order

If you're object ids have spacial coherency then this will still place similar colors near each other (shouldn't really be a problem if you use a decent precision for your picking buffer). But you can always randomize the order with:

I = randperm(k); rgb = uint8([R(I);G(I);B(I)]'-1)/(s-1)*255);

to give you something like

k unique colors in order

Here's a C++ version using Eigen and libigl:

  size_t k = 1024;
  size_t s = ceil(cbrt(k));
  Matrix<unsigned char,Dynamic,3> rgb(k,3);
  ArrayXi I,re;
  igl::randperm(k,I);
  igl::mod(I,s*s,re);
  rgb.col(2) = (((I-re)/(s*s)*255)/(s-1)).cast<unsigned char>();
  I = re;
  igl::mod(I,s,re);
  rgb.col(1) = (((I-re)/(s)*255)/(s-1)).cast<unsigned char>();
  rgb.col(0) = ((re*255)/(s-1)).cast<unsigned char>();

Or for the faint of heart:

  igl::randperm(k,I);
  {
    size_t i = 0;
    for(size_t b = 0;b<s&&i<k;b++)
    {
      for(size_t g = 0;g<s&&i<k;g++)
      {
        for(size_t r = 0;r<s&&i<k;r++)
        {
          rgb(I(i),0) = r*255/(s-1);
          rgb(I(i),1) = g*255/(s-1);
          rgb(I(i),2) = b*255/(s-1);
          i++;
        }
      }
    }
  }

Update: Here's another way which requires less computation and doesn't require knowing how many unique colors you want ahead of time (though it sort of assumes a smallish number). This won't promise distant colors like above, but will still work for picking if you use unsigned byte precision:

[R,G,B] = ind2sub([256 256 256],mod((0:k-1)*(103811),256^3)+1);
rgb = uint8(permute(([R;G;B]'-1),[1 3 2]));

The idea is to sample the 2563 cube with a large prime stride. I choose 103811 because it produced a nice balance of colors for k==100.

k unique colors alternative;