Section 3.6. Selection


3.6. Selection

Interactive applications typically allow users to pick an object in the scene, which then becomes the focus of further user-directed operations. OpenGL provides this functionality with the selection feature.

To use selection, applications must provide OpenGL a selection array for storing hit records that correspond to selected objects. Next, the application puts OpenGL in selection mode. While in selection mode, OpenGL doesn't render geometry to the screen; instead, it stores in the selection array hit records that contain information about any visible primitives. Before applications send primitives in selection mode, they typically change the shape of the view volume so that it fits tightly around the area of interest, which typically is the location of a mouse click. When sending geometry in selection mode, applications specify identifiers for geometry or groups of geometry, called names. OpenGL stores the names of selected primitives in the hit record for that primitive. After all geometry has been sent, applications examine the hit records in the selection array to determine what primitives were selected by the user.

3.6.1. Performing a Selection Pass

Applications typically use selection in response to user input. Applications usually support a user interface in which a left-mouse-button click indicates that the user is attempting to indicate a model or piece of geometry of interest. An application must perform the operations described below to support the user pick request.

3.6.1.1. Specify the Selection Array

Specify the selection array by allocating enough memory to hold the hit records; then call glSelectBuffer().


void glSelectBuffer( GLsizei n, GLuint* buffer );


Specifies a block of memory to contain selection results. buffer points to the selection array, and n specifies its size as a count of GLuints.

buffer specifies a selection array that OpenGL potentially fills with hit records during a selection pass.

Applications must allocate enough memory for the selection array to hold all hit records that OpenGL might return. The number of hit records that OpenGL writes into the selection array depends on how many primitives are visible during the selection pass and how the application uses the name stack.

Call glSelectBuffer() before entering selection mode with a call to glRenderMode( GL_SELECT ).

3.6.1.2. Change the Render Mode

After your application specifies the selection array with glSelectBuffer(), it should change the render mode to enter selection mode. Change the render mode with glRenderMode().

By default, the render mode is GL_RENDER, and when you specify geometry, OpenGL renders it to the framebuffer. If you set the render mode to GL_SELECT, instead of rendering geometry, OpenGL returns hit records for each visible primitive in the selection array.


GLint glRenderMode( GLenum mode );


Selects the current rendering mode. mode must be either GL_RENDER or GL_SELECT.[3]

[3] A third render mode, GL_FEEDBACK, is not covered in this book. See Chapter 14, "Selection and Feedback," of OpenGL® Programming Guide.

If you are entering selection mode by changing from GL_RENDER to GL_SELECT, glRenderMode() returns 0. If you are changing from GL_SELECT back to GL_RENDER, glRenderMode() returns the number of hit records written into the selection array.

If the selection array is too small to hold all the hit records, glRenderMode() returns 1. Programmers should watch for this during application development and testing, and should allocate more memory for the selection array if necessary.

Set the selection array with glSelectBuffer() before calling glRenderMode( GL_SELECT ). If you place OpenGL in selection mode without first specifying a selection array, glRenderMode() generates the error GL_INVALID_OPERATION.

When OpenGL is in selection mode, it transforms geometry into clip coordinates and performs clipping. OpenGL generates a hit record for any primitives still visible at that point and writes the hit record into the selection array.

3.6.1.3. Narrow the View Volume

In selection mode, OpenGL returns a hit record for each visible primitive. So without changing the model-view or projection matrix, a selection pass would return hit records for every primitive visible in the window. This behavior is inadequate to support user picking by clicking the mouse. To support picking, applications narrow the view volume to surround the pick location. GLU provides a routine to help create a view volume suitable for picking: gluPickMatrix().


void gluPickMatrix( GLdouble x, GLdouble y, GLdouble width,
  GLdouble height, GLint* vp );


Creates a pick matrix and postmultiplies it onto the top of the active matrix stack. Typically, applications call gluPickMatrix() as part of the following sequence:

 glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPickMatrix( ... ); gluPerspective( ... ); 


In this case, the resulting projection transform stored in the top of the projection matrix stack has its center at window coordinates x and y, and has window-coordinate dimensions width and height. The vp parameter is a four-element array containing the current viewport, as obtained by calling glGetIntegerv( GL_VIEWPORT, vp ).

gluPickMatrix() is designed to support user picking with a mouse click. When the application receives a mouse-click event, it obtains the current mouse xy position from the operating system. Applications pass this xy location to gluPickMatrix() as the x and y parameters. The width and height parameters are application dependent; some applications allow the user to specify the width and height of a pick box, whereas other applications hard-code the pick-box dimensions.

Most applications restrict the same view volume that was just used for the previous render pass. In this case, typical code for using gluPickMatrix() might look like the following:

 glMatrixMode( GL_PROJECTION ); // Obtain the current projection matrix GLdouble proj[16]; glGetDoublev( GL_PROJECTION_MATRIX, proj ); // Set the projection matrix for picking glLoadIdentity(); gluPickMatrix( x, y, width, height, vp ); glMultMatrixd( proj ); 


In this code, the application obtains the current projection matrix with a call to glGetDoublev( GL_PROJECTION_MATRIX, proj ) and subsequently postmultiplies it onto the matrix created by gluPickMatrix() before performing the selection pass.

3.6.1.4. Render Geometry with Name Identifiers

If your application renders a visible primitive during selection mode, OpenGL generates a hit record for it and writes the record into the selection array. The hit record identifies visible primitives using names, which are unsigned integers (GLuints) stored in a name stack. The hit record contains the entire contents of the name stack at the time the primitive was submitted.

Unlike the attribute and matrix stacks, the OpenGL name stack can contain at least 64 entries and must be initialized with a call to glInitNames(). After initialization, the name stack is empty. You can simultaneously push the name stack and store a name onto the new top of stack with a call to glPushName(), or you can replace the current top of stack with the glLoadName() command. The glPopName() command pops the stack one level.


void glInitNames( void );
void glPushName( GLuint name );
void glLoadName( GLuint name );
void glPopName( void );


Use these commands to manipulate the name stack. glPushName() pushes the name stack one level and stores name on the new top of stack. glLoadName() replaces the current top of stack with name. If the name stack is empty, calling glLoadName() generates the error GL_INVALID_OPERATION.

For hit records in the selection array to have any meaning, your application must initialize the name stack with glInitNames(), push it at least once, and store a name onto the top of stack before rendering pickable primitives.

In the simplest use of the name stack, an application initializes it, pushes it once, and then repetitively loads a new name and renders a primitive. In this case, OpenGL creates hit records with a single name, because the name stack is only one entry deep for each visible primitive. Many applications use the name stack to encode an object hierarchy, however.

3.6.1.5. Process Hit Records in the Selection Array

After the application submits all pickable geometry, it returns to render mode with the command glRenderMode( GL_RENDER ). If the selection array is too small to hold all hit records, this command returns 1. If no hit records were generated because all geometry was outside the selection pass view volume, it returns 0. Otherwise, glRenderMode( GL_RENDER ) returns the number of hit records in the selection array.

Hit records are a sequence of GLuint values. OpenGL places the following information in each hit record:

  • The first GLuint in the hit record is the name stack depth at the time the hit record was created.

  • The second and third GLuint values are the minimum and maximum depth values of the selected primitive in the range 0 to 2321. (If the application enables depth offset during a selection pass, OpenGL doesn't apply it to the returned depth values.)

  • If the name stack depth is greater than 0, each name in the name stack, from the bottom to the top, appears last in the hit record.

3.6.2. Selection in the Example Code

The Picking example program (see Figure 3-5), available from this book's Web site, demonstrates the OpenGL selection feature. This program displays a sphere, a cylinder, and a torus. When the user clicks the left mouse button, the code performs a selection pass. If the user drags an empty part of the scene, OpenGL records no hit records, and the code changes the view. If the user clicks on one of the three objects, however, OpenGL records a hit record. The code marks the object as selected and repositions it in the scene as the user drags the mouse.

Figure 3-5. The Picking example program. In this image, the user has picked the sphere and is dragging it around the scene.


The code assigns a unique name to each of the three objects in the scene. The name stack never grows more than a single level; the code doesn't employ a name hierarchy. As a result, each hit record contains only a single name.

When multiple primitives overlap, the selection pass could return multiple hit records. If this occurs, the code determines the picked object by finding the hit record with the smallest minimum depth value. This corresponds to the object closest to the viewer.

Sadly, selection is unoptimized in many commodity OpenGL implementations, which perform the selection by using software rather than high-speed graphics hardware. Because selection requires an implementation to transform each vertex into clip coordinates, selection is commonly vertex-limited in unoptimized implementations, creating an unacceptable delay during a selection pass. The example program shows one way to minimize this delay. It displays objects at a relatively high resolution to create acceptable visual quality but uses a low-resolution copy of each object for the selection pass. Reducing the number of vertices during selection results in acceptable performance in implementations with unoptimized selection.




OpenGL Distilled
OpenGL Distilled
ISBN: 0321336798
EAN: 2147483647
Year: 2007
Pages: 123
Authors: Paul Martz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net