Picking with a color buffer

From Processing

Jump to: navigation, search
Versions: 1.0+
Contributors: antiplastik
Started: 2007-12-05


Picking is about selecting an object with the mouse in a 3D scene. There are many picking methods, and depending the result you expect, you may choose one or another.

To test if an object has been clicked using a color-buffer, the steps are:

  1. First draw the scene in a buffer, using one different color for each object and without any lights, strokes, textures or antialiasing. Be sure to use exactly the same camera settings as the scene displayed.
  2. Then, get the pixel color in the buffer under the mouse position : since each object has a unique color, you can retrieve it easily.


File:hacks-picking_colorbuffer_buffer.png File:hacks-picking_colorbuffer_display.png


Some tips:

  • In Processing, RGB colors are stored as integers, from -16 777 216 (black) to -1 (white). So using integers to identify the objects in the scene is a good idea.
  • You can use a hashmap to associate each object with an id.
  • Be careful when using transparent objects in your scene. Depending the result you expect, you may choose to draw them or not in the buffer.

Source Code

/**
picking code taken from http://wiki.processing.org/index.php?title=Picking_with_a_color_buffer
@author nicolas clavaud
*/
 
import processing.opengl.*;
 
class Cube {
 
  // variables
  int id;          // id
  int x, y, z, w;  // position (x, y, z) and width (w)
  color c;         // color (in scene, not buffer)
 
  // constructor
  public Cube(int id, int x, int y, int z, int w) {
    this.id = id;
    this.x = x; this.y = y; this.z = z; this.w = w;
    c = color(random(255), 250, 50);
  }
 
  // make the color change
  public void changeColor() {
    int r = (int)red(c);
    int g = (int)green(c);
    int b = (int)blue(c);
    c = color(r, 255 - g, b);
  }
 
  // display the cube on screen
  public void display(PGraphics ecran) {
    ecran.fill(c);
    drawCube(ecran);
  }
 
  // draw the cube in the buffer
  public void drawBuffer(PGraphics buffer) {
    color idColor = getColor(id);
    buffer.fill(idColor);
    drawCube(buffer);
  }
 
  private void drawCube(PGraphics g) {
    g.pushMatrix();
      g.translate(x, y, z);
      g.box(w);
    g.popMatrix();
  }
 
}
 
PGraphics buffer;   // buffer
Cube[] cubes;       // cubes
 
float a = 0;        // angle to make the scene rotate
 
void setup() {
 
  // we use OPENGL for display
  size(200, 200, OPENGL);
 
  // buffer is created using applet dimensions
  buffer = createGraphics(width, height, P3D);
 
  // put cubes randomly in the scene
  cubes = new Cube[10];
  for (int i = 0; i < cubes.length; i++) {
    cubes[i] = new Cube(
      i,                      // identifiant
      -15 + (int)random(30),  // position x
      -15 + (int)random(30),  // position y
      -15 + (int)random(30),  // position z
      5 + (int)random(15)     // taille
    );
  }
 
}
 
void draw() {
 
  background(255);
 
  camera(-20, -20, 50, 0, 0, 0, 0, 1, 0);
 
  rotateY(a);
 
  lights();
  for (int i = 0; i < cubes.length; i++) {
    cubes[i].display(this.g);
  }
 
  a += 0.002;
  if (a > TWO_PI) a -= TWO_PI;
 
}
 
void mouseClicked() {
 
  // draw the scene in the buffer
  buffer.beginDraw();
  buffer.background(getColor(-1)); // since background is not an object, its id is -1
  buffer.noStroke();
  buffer.camera(-20, -20, 50, 0, 0, 0, 0, 1, 0);
  buffer.rotateY(a);
  for (int i = 0; i < cubes.length; i++) {
    cubes[i].drawBuffer(buffer);
  }
  buffer.endDraw();
 
  // get the pixel color under the mouse
  color pick = buffer.get(mouseX, mouseY);
  // get object id
  int id = getId(pick);
  // if id > 0 (background id = -1)
  if (id >= 0) {
    // change the cube color
    cubes[id].changeColor();
  }
 
}
 
// id 0 gives color -2, etc.
color getColor(int id) {
  return -(id + 2);
}
 
// color -2 gives 0, etc.
int getId(color c) {
  return -(c + 2);
}

Downloads

Related Links

Personal tools