Selection in 3D Graphics Environments
Christine Albert and Lindsey Press

Because we want the cubes to all have a solid distinct color and an index value, we modified the code so that the 600 cubes on the screen are all randomized colors. Color values are between 0 and 1 for the red green blue componenets (RGB), and are no longer based on the position of the vertex as was the case in the code previously. The vertex shader is used to color one cube at a time. Because every cube has a unique color based on index, we also have a method that returns the index based on the color and a different method that returns the color based on the index. The changes to the code are highlighted and explained below.

Step 1: A constant was added at the start of the code.

const GLfloat FOV = radians(90.0);
This line added a constant, called FOV (short for field of view) and instantiated it to 90.0 radians. This allows us to use a FOV of 90.0 radians throughout the rest of the code.

Step 2: Changes were made to the Cube class, and changes from previous step are in bold.

class Cube
      glm::vec3 center;
      GLfloat scale;
      glm::vec2 angle;
      glm::mat4 model;
      glm::vec3 color;
      GLuint index;

The two added lines provide a color and index attribute for each cube. Within the cube class, the preexisting attributes remain and the addition of color and index allows us to designate a unique index and color value to all of the 600 cubes in the scene.

Step 3: changes were made within the GenerateRandomCubes method within the for loop. Changes from the previous version of code are bolded.

for(int i = 0; i < NUMCUBES; i++)
      cubes[i].center.z = 0.01+rand() % (GLint)FAR;
      GLint maxy = 4*(1+glm::tan(FOV/2)*cubes[i].center.z);
      GLint maxx = maxy * SCREEN_SIZE.x / SCREEN_SIZE.y;
      cubes[i].center.x = -maxx/2 + (rand() % maxx) + rand();
      cubes[i].center.y = -maxy/2 + (rand() % maxy) + rand();
      cubes[i].scale = rand() / RANDMAX * 0.4;
      cubes[i].color.r = (20 + rand() % 230)/255.0;
      cubes[i].color.g = (20 + rand() % 230)/255.0;
      cubes[i].color.b = (20 + rand() % 230)/255.0;
      cubes[i].angle[0] = 0;
      cubes[i].angle[1] = 0;
      cubes[i].index = i;

The change of adding tan(FOV/2) ensures that the maxy value is not zero. Adding the three lines that designate a color for red, green, and blue do so by adding 20 to a random value, doing modulo 230, and dividing by 255. The end result of each of these caluclations is that random number between 0 and 1 is generated for the red, green, and blue components of each cube. These three componenets taken together form the final, randomized solid color of each cube. Furthermore, the line that designates the index value does so by setting the index value to the number of the iteration through the for loop to ensure that all index values are unique. Thus, each cube now has one solid color that is assigned by generating random values between 0 and 1 for the rgb componenets, and each cube also has a unique index that is connected with the other traits of each cube, including its color.

Step 4: Made minor changes to the LoadShaders method.

projection = glm::perspective(FOV, AR, NEAR, FAR);
GLfloat d = 1/glm::tan(FOV/2);

The two above changes use the new field of view (FOV) constant of 90 radians for setting the projection in the vertex shader.

Step 5: Added methods to get a cube's index and color.

int GetIndexByColor(int r, int g, int b)
      return (r)|(g<<8)|(b<<16);

glm::vec3 GetColorByIndex(int index)
      int r = index&0xFF;
      int g = (index>>8)&0xFF;
      int b = (index>>16)&0xFF;

      return glm::vec3(float(r)/255.0f, float(g)/255.0f, float(b))/255.0f);

The first method, GetIndexByColor, takes parameters for the integer values of the red, green, and blue componenets of a cube and returns the index value associated with the red, blue, and green components. A bitwise inclusive or operator is used to compare each bit of the red component to the corresponding bit in the left shifted green component, and again to compare each bit of the shifted green component to the shifted blue component. The end result is a method that returns the index given the RGB components.
The second method, GetColorByIndex, takes an integer parameter that is the index of the cube. The method accomplishes this by getting the integer values for the red, green, and blue components of the cube. The method accesses specific parts of the index address using the & operator and right shifts to adjust for the different locations of the different color components to access the information in memory. After setting these integer values of the color components, they are each divided by 255 to get the values between 0 and 1 and returned as a vector.

Solid Colored Cubes