PDA

View Full Version : What is the best method for object picking?



gmseed
11-07-2011, 01:53 AM
Hi

If I turn to Chapter 13 of the Red Book [7th ed.] it states at the start of the chapter that from OpenGL v3.1 all of the selection and picking techniques described in the chapter have been dumped.

Thus, what is the best method of picking an object?

Does the statement at the start of Chapter 13 mean that if I a use a GL version less than 3.1 I use the methods described and 3.1+ I use another method, or should I be using a different method for all versions?

Also, I'm using JOGL and are there established classes [as there are for trackball] for object selection and picking or do I have to reinvent the wheel?

Thanks

Graham

V-man
11-07-2011, 02:01 AM
http://www.opengl.org/wiki/Common_Mistakes#Selection_and_Picking_and_Feedback _Mode

gmseed
11-07-2011, 03:28 AM
Hi

Thanks for the reply. Your opengl.org link seems to suggest using glReadPixels() or using a ray-tracing method.

I see you had a link to glhlib, which appears a C++ lib. However, I'm using JOGL.

Thanks

V-man
11-07-2011, 07:37 AM
Hi

Thanks for the reply. Your opengl.org link seems to suggest using glReadPixels() or using a ray-tracing method.

I see you had a link to glhlib, which appears a C++ lib. However, I'm using JOGL.

Thanks

That's correct, use glReadPixels() for color picking or use a ray-tracing method.

You don't need glhlib. That's just my signature.
Although, my lib has a function for ray to triangle testing.

gmseed
11-07-2011, 07:54 AM
Here's my attempt at the pick-ray method described at:

http://www.opengl.org/resources/faq/technical/selection.htm

However, there's a bug in it somewhere as is doesn't draw a pick-ray correctly.

Graham
===


public Ray3D mouseRayWorldCoordinates(GLAutoDrawable drawable)
{
// http://www.java-tips.org/other-api-tips/jogl/how-to-use-gluunproject-in-jogl.html
GL2 gl2 = drawable.getGL().getGL2();

int viewport[] = new int[4];
float mvmatrix[] = new float[16];
float projmatrix[] = new float[16];
float wcoordNear[] = new float[4];
float wcoordFar[] = new float[4];

int x = xCoord;
int y = yCoord;

gl2.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);
gl2.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, mvmatrix, 0);
gl2.glGetFloatv(GL2.GL_PROJECTION_MATRIX, projmatrix, 0);
int realy = viewport[3] - (int)y; /* GL y coord pos - note viewport[3] is height of window in pixels */

// world near
System.out.println("Coordinates at cursor are (" + x + ", " + realy);
boolean ok = glu.gluUnProject((float) x, (float)realy, 0.0f, //
mvmatrix, 0,
projmatrix, 0,
viewport, 0,
wcoordNear, 0);
System.out.println("World coords at z=0.0 are ( " //
+ wcoordNear[0] + ", " + wcoordNear[1] + ", " + wcoordNear[2]
+ "); ok: " + ok);
// world far
ok = glu.gluUnProject((float) x, (float) realy, 1.0f, //
mvmatrix, 0,
projmatrix, 0,
viewport, 0,
wcoordFar, 0);
System.out.println("World coords at z=1.0 are (" //
+ wcoordFar[0] + ", " + wcoordFar[1] + ", " + wcoordFar[2]
+ "); ok: " + ok);

// direction vector is far point - near point
Vector3D dirVector = new Vector3D(wcoordFar[0]-wcoordNear[0],wcoordFar[1]-wcoordNear[1],wcoordFar[2]-wcoordNear[2]);
dirVector.normalise();
Point3D viewerLocation = GLUtilities.cameraLocation(drawable.getGL());
Ray3D mouseRay = new Ray3D(viewerLocation,dirVector);

return mouseRay;
}

pgfeldman
03-09-2012, 10:20 AM
I've been putting together a data visualizer, and had to implement picking. I've had good success with using query objects. Here's a banged-together code snippet of all the pieces that I needed to get everything to work:



// Code to show how to use object queries to get picking behavior. For this code to work,
// You'll need to add your preferred rendering framework. This code was pulled from an
// FLTK framework I've been building for data visualization

// Class-wide global variables here for the sake of a compact file
// Classes that are not part of the OpenGl library are from the OpenGl SuperBible,
// which I highly reccomend: http://www.starstonesoftware.com/OpenGL/

GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
bool isPicking;

// Constructor. Set up picking and the projection and model matrices
PickingExample::PickingExample(int x,int y,int w,int h,const char *l)
: Fl_Gl_Window(x,y,w,h,l)
{
isPicking = false;
pickQueryResult = READY;
glGenQueries(1, &pickQueryID);
if(pickQueryID == 0){
pickQueryResult = PICK_ERROR;
}

viewFrustum.SetPerspective(45.0f, (float)screenWidth/(float)screenHeight, 0.5f, 1000.0f);

projectionMatrix.LoadMatrix(viewFrustum.GetProject ionMatrix());
modelViewMatrix.LoadIdentity();
}

// A slightly modified version of the original gluPickMatrix, taken from:
// http://oss.sgi.com/cgi-bin/cvsweb.cgi/projects/ogl-sample/main/gfx/lib/glu/libutil/project.c?rev=1.4;content-type=text%2Fplain
void PickingExample::setPickMatrix(GLdouble x, GLdouble y, GLdouble deltax, GLdouble deltay, GLint viewport[4])
{

if (deltax <= 0 || deltay <= 0) {
return;
}

projectionMatrix.LoadIdentity();

/* Translate and scale the picked region to the entire window */
GLfloat dx = (float)((viewport[2] - 2 * (x - viewport[0])) / deltax);
GLfloat dy = (float)((viewport[3] - 2 * (y - viewport[1])) / deltay);
projectionMatrix.Translate(dx, dy, 0);
projectionMatrix.Scale((float)(viewport[2] / deltax), (float)(viewport[3] / deltay), 1.0);

projectionMatrix.MultMatrix(viewFrustum.GetProject ionMatrix());
}


// the main drawing method. In this case, only drawing for picking is done, though it would
// be easy enough to glClear() afther the picking code, and draw everything over again with
// a normal perspective
void PickingExample::draw() {
GLint result;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


modelViewMatrix.PushMatrix();

// Have some Euler angle rotations and translations just to show how
// setPickMatrix() doesn't care about eye or model position

// global eye transformations
modelViewMatrix.Rotate(eyeOrient[1], 0, 1, 0);
modelViewMatrix.Rotate(eyeOrient[0], 1, 0, 0);
modelViewMatrix.Translate(eyePos[0], eyePos[1], eyePos[2]);

// global world transformations
modelViewMatrix.Translate(worldPos[0], worldPos[1], worldPos[2]);
modelViewMatrix.Rotate(worldOrient[0], 1, 0, 0);
modelViewMatrix.Rotate(worldOrient[1], 0, 1, 0);

// set up picking
if(isPicking &amp;&amp; (pickQueryResult != PICK_ERROR))
projectionMatrix.PushMatrix();
// set the projection matrix so that it's just looking at a (in this case) 10x10 pixel area
// near the cursor. This method (shown above)
setPickMatrix(mouseX,viewport[3]-mouseY,10,10,viewport);

// for each item that you want to test, bracket it with a glBeginQuery()/glEndQuery()
// You'll need a unique pickQueryID for each model
glBeginQuery(GL_ANY_SAMPLES_PASSED, pickQueryID);

// render your model associated with pickQueryID here. It is assumed that
// the projection matrix and the model matrix will be handled by your shader(s)

// finish the query for this particular model
glEndQuery(GL_ANY_SAMPLES_PASSED);

projectionMatrix.PopMatrix();

// For each item that you queried above, see if at least one pixel was drawn
// Again, you'll need a unique pickQueryID for each model
glGetQueryObjectiv(pickQueryID, GL_QUERY_RESULT, &amp;result);
if(result)
printf("hit\n");
else
printf("miss\n");

isPicking = false;
}
modelViewMatrix.PopMatrix();
}

kevinkiller
04-19-2012, 02:29 PM
The previous algorithm doesn't seem to take in account the depth test. (GL_SAMPLES_PASSED counts fragments that pass the depth test).

What will happen if you draw an object and then draw another closer to the near plane that completely covers the first? What if you drew them in the opposite order?

The answers to these questions will be determined by the depth testing state in OpenGL. Different state settings can give different answers.

If you want a list of all the items that are under the pixel you can just disable depth testing and you'll get your list (but you won't know the depth order).

If you want depth order you could turn on depth testing but use the depth test GL_ALWAYS and then read back the depth buffer after rendering each primitive (but this can be bad for performance, depending on exactly how you read it).

You can get the top-most object(s) but you'll need two rendering passes. The first one you render normally with GL_LESS into the color and depth buffers. The second time you disable writes to the color and depth buffers use GL_LEQUAL and perform the SAMPLES_PASSED queries.