# 3D picking

This topic is 4551 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi, i have a simple question about 3D picking for my 3D game... i've used the GLPicking selection mode, however i need somethnig different that can be done only with maths. i need to click somewhere on the 2D screen and get coords for where the click ends on 2 axises out of 3 (for exemple, assuming Y is always equal to 0, where does this click end on the X and Z axises? (for exemple, there's a ground at Y = 0 and from any view i'd like to know where it's been clicked on in 3D coords) thanks!

##### Share on other sites
Probably the first thing you need is a picking ray; for this you need to be able to unproject a point in window coordinates. You can do this yourself if your math library supports matrix inversion and homogenous coordinates, or you can use gluUnProject(). I'm assuming that you are using the default OpenGL depth range of [0,1].

gluUnProject is of the form:
gluUnProject(    GLdouble windowx,    GLdouble windowy,    GLdouble windowz,    const GLdouble modelviewMatrix[16],    const GLdouble projectionMatrix[16],    const GLint viewport[4],    GLdouble* x,    GLdouble* y,    GLdouble* z);
You can query the matrices and viewport parameters from OpenGL using glGet*v(). x, y, and z are the output coordinates. windowx and windowy are in window coordinates where +y is up, so you'll probably need to invert your mouse y coordinate.

windowz is a depth value of your choosing. There are a couple of ways you can generate the ray. One is to call gluUnProject() once with a windowz value in the range [0,1]. The ray direction is then the resulting vector minus the camera position.

You can also call gluUnProject() once with windowz = 0 and once with windowz = 1, which gives you two points defining a segment that lies within the view frustum.

Once you have your ray, you can use it however you want, including intersecting with the ground plane as in your example.

I'll be happy to provide more details if you need them.

##### Share on other sites

i have done this functiuon with it:

void winToWorldCoords(GLdouble winX, GLdouble winY){    GLdouble* x; x=0;    GLdouble* y; y=0;    GLdouble* z; z=0;    GLdouble model[16]; glGetDoublev(GL_MODELVIEW,model);    GLdouble proj[16]; glGetDoublev(GL_PROJECTION,proj);    GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport);            gluUnProject(winX, winY, 0.0,                 model, proj, viewport,                 x,y,z);           cout << "x: " << x << " y: " << y << " z: " << z << endl;}

however, this causes a bus error and i really can't figure out why

and by the way, when i have the ray i'm not too sure how to calculate where it arrives at some point - i'm afraid my maths lack, i only know how to get the coords of a 2D ray

[Edited by - Marianne on September 3, 2005 2:43:05 PM]

##### Share on other sites
    GLdouble x; x=0;    GLdouble y; y=0;    GLdouble z; z=0;gluUnProject(winX, winY, 0.0,                 model, proj, viewport,                 &x,&y,&z);

I would guess that's right.

##### Share on other sites
Quote:
 and by the way, when i have the ray i'm not too sure how to calculate where it arrives at some point - i'm afraid my maths lack, i only know how to get the coords of a 2D ray
Looks like Ilici spotted the error in your code. As for your other question, I'm not sure what you mean by the ray ariving at some point. Do you mean how to intersect it with the ground plane or some other object? If it's the former, googling for 'ray plane intersection' should turn up the solution. Just watch out for the failure case where the ray is parallel to the plane.

##### Share on other sites
thanks it works great for turning window coords into world coords! i'll see for the maths i've found a few websitesw, i'll try to get them to work - and if i can't i can fake it with my 2D maths i think (i'd just need twice more calculations i think)

thanks anyway i should be able to continue on my own without annoying you ;)

##### Share on other sites
well guess i spoke too fastly... :/ your code works greatly however coords are always mapped to the center of teh screen no amtter what winodw coordinates i enter (i try printing everything, and no matter what window coords i give, i end up with the same space location) - what's wrong? thanks again

##### Share on other sites
Hi,

There are too many variables to say for sure what's wrong, so if you haven't figured out the problem already perhaps you could post the following bits of code:

1. Where you set up the modelview matrix, projection matrix, and viewport
2. Where you query OpenGL for the necessary info and call gluUnProject()
3. Where you construct the ray (if you're getting that far)

##### Share on other sites
well most of the interesting code is already up this thread but i'll post it again ;)

/*Where i set up the modelview matrix, projection matrix, and viewport*/void GL_reshape(int width, int height){    glViewport(0, 0, width, height);    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    gluPerspective(45, (float)width/(float)height, 1.0, 200.0);    glMatrixMode(GL_MODELVIEW);    glLoadIdentity();}/*method that turns window coords into world coords - i have tried printing the window coords and they do vary, the problem is not there*/point3d toWorldCoords(GLdouble winX, GLdouble winY){    GLdouble x=0;    GLdouble y=0;    GLdouble z=0;    GLdouble model[16]; glGetDoublev(GL_MODELVIEW,model);    GLdouble proj[16]; glGetDoublev(GL_PROJECTION,proj);    GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport);gluUnProject(winX, winY, (GLdouble)1.0,                     model, proj, viewport,                     &x,&y,&z));        cout << "x: " << x << " y: " << y << " z: " << z << endl;   return new point3d(x,y,z); // point3d is a simple class holding x y and z variables to represent a 3d vector}

##### Share on other sites
Hm, I'm not seeing where the error is...but maybe I'm missing it. If you're still trying to fix this, here are a few more ideas:

1. Go into the debugger and check the values of the queried modelview matrix and viewport parameters. Based on what you posted, the modelview matrix should be identity. Also see what the values of x, y and z are after the call to gluUnProject(). I can't imagine why cout would be outputting them incorrectly, but you might check them in the debugger just in case.

2. If you can, post your results. That is, show some sample window coordinates and the world coordinates you're getting back. I'd be happy to run the input through gluUnProject() and my own unproject() function and see if I get the same thing. You might also give your default window width and height.

I've successfully done this with both gluUnProject() and my own code, so I know it works. There's probably just some little detail throwing things off for you...

##### Share on other sites
i've changed the method code to this:

    GLdouble x=0;    GLdouble y=0;    GLdouble z=0;    GLdouble model[16]; glGetDoublev(GL_MODELVIEW,model);    GLdouble proj[16]; glGetDoublev(GL_PROJECTION,proj);    GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport);            for(int i=0; i<4; i++) cout << "viewport [" << i << "] = " << viewport << endl;    for(int i=0; i<16; i++) cout << "model [" << i << "] = " << model << endl;    for(int i=0; i<16; i++) cout << "proj [" << i << "] = " << proj << endl;        cout << "# Unprojecting win x: " << winX << " win y: " << winY<< " win z: " << 1 << endl;        if(!gluUnProject(winX, winY, (GLdouble)1.0,                     model, proj, viewport,                     &x,&y,&z)){        cout << "Unprojection failed. " << endl;        return new ThetaTriplet(0,0,0);    };        cout << "x: " << x << " y: " << y << " z: " << z << endl;

here's the ouput: (i've edited theoutput tu make matrices easier to read but didn't change any value)

viewport
0,0, 640, 480

model
-0.229924, 0,-0.973209 0,
0, 1 0, 0,
0.973209, 0, -0.229924, 0
4.86604, 0, -1.14962, 1

proj
-2.38424e+11, 1.12335e-314, -2.38424e+11, -2.38424e+11
2.18976e-308, 2.85328e-306, 2.30959e-317, -3.01703e-273
2.29895e-307,-1.99692,-1.99692,-3.23797e-232
2.30959e-317, -3.01703e-273, -1.99692, 6.88044e-308

# Unprojecting win x: 290 win y: 157 win z: 1

results:
x: 0.233788 y: 5.02861e+272 z: -4.94477

if i click many times it continues this way, with different matrices depending on where the camera's looking at and different window coords depending on where i clicked, however when at a static location clicking on different window coords prints different win coords but same results.
ouput examples: (all at the same location and with the same matrices)

# Unprojecting win x: 524 win y: 361 win z: 1
x: 1.00004 y: 8.37584e-12 z: -5

# Unprojecting win x: 90 win y: 115 win z: 1
x: 1.00004 y: 8.37584e-12 z: -5

# Unprojecting win x: 501 win y: 145 win z: 1
x: 1.00004 y: 8.37584e-12 z: -5

##### Share on other sites
I may not get to it until tomorrow, but I'll look over your results and see if I can find the problem (unless someone else finds it first).

##### Share on other sites
Hi Marianne,

There may be other problems, but the first place I'd look is the projection matrix. I used your values to create a projection matrix using both gluPerspective() and my own code, and got the following:
1.81066  0.00000  0.00000  0.000000.00000  2.41421  0.00000  0.000000.00000  0.00000 -1.01005 -1.000000.00000  0.00000 -2.01005  0.00000
At first glance at least, the projection matrix values you posted seem to be more or less random, so maybe something is going wrong when setting or querying the OpenGL projection matrix.

One other minor detail. I printed out the matrix entries in sequential order like you did, but printing them out in the order 0-4-8-12-1-5-9-13-2-6-10-14-3-7-11-15 would better reflect the actual form of the matrix.

##### Share on other sites
well i really don't understand what's going on... if i just copy and paste the codee that prints the projection matrix to a method that's called on a keypress and it gives different values if i click and press the keyboard button whitout changing anything else!

it gave me:

on mouse click:
proj [0] = -2.38424e+11
proj [1] = 1.12335e-314
proj [2] = -2.38424e+11
proj [3] = -2.38424e+11
proj [4] = 2.18978e-308
proj [5] = 2.85328e-306
proj [6] = 2.30944e-317
proj [7] = -3.01703e-273
proj [8] = 2.25498e-307
proj [9] = -1.99692
proj [10] = -1.99692
proj [11] = -3.23797e-232
proj [12] = 2.30944e-317
proj [13] = -3.01703e-273
proj [14] = -1.99692
proj [15] = 6.88041e-308

on key press:
proj [0] = -1.99684
proj [1] = 0
proj [2] = 0
proj [3] = 4.24399e-314
proj [4] = 2.36533e-317
proj [5] = 0
proj [6] = 2.30944e-317
proj [7] = -3.01703e-273
proj [8] = 2.25498e-307
proj [9] = 2.11625e-301
proj [10] = -1.99692
proj [11] = 8.34209e-303
proj [12] = 2.36533e-317
proj [13] = 2.70643e-307
proj [14] = -1.99692
proj [15] = -2.1859e-215

i just don't understand how this can happen they both use the same code :

GLdouble projection_matrix_array[16]; glGetDoublev(GL_PROJECTION,projection_matrix_array);
for(int i=0; i<16; i++) cout << "proj [" << i << "] = " << projection_matrix_array << endl;

and i call them one right after the other (open, click then press key and collect 2 diffrent outputs!) ... really not getting it :S

##### Share on other sites
I think this will make you happy :) I can't guarantee this will make everything work perfectly, as there may be other problems in your code. But it will be a step in the right direction.

OpenGL uses different constants for glGet*v() than it does for glMatrixMode(). For matrix mode it's:

GL_MODELVIEW
GL_PROJECTION

For glGet*v() it's:

GL_MODELVIEW_MATRIX
GL_PROJECTION_MATRIX

So I think that's why you're getting garbage. Try this instead:

GLdouble modelview_matrix_array[16]; glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix_array);
GLdouble projection_matrix_array[16]; glGetDoublev(GL_PROJECTION_MATRIX,projection_matrix_array);

'Hope that fixes everything (and sorry I didn't notice it before).

##### Share on other sites
thanks, a lot for your support! few would have helped me so much ;)

it works all perfectly! the only thing i don't get is why changing the window z factor by 1 unit changes the result coords by 200 units (??) anyway i'll be able to manage ;)

thanks again

##### Share on other sites
Quote:
 it works all perfectly! the only thing i don't get is why changing the window z factor by 1 unit changes the result coords by 200 units (??)
Great, I'm glad you got it working :)

The answer to your question is that window coordinates are mapped differently than camera coordinates. The OpenGL default is that z values in the range [near, far] in camera space are mapped to the range [0,1] in window coordinates. When you unproject you're going the other way, so [0,1] maps to [near,far]. It's a little hard to explain without getting into the whole pipeline, but that's the general idea.

##### Share on other sites
ok thanks now that's my last post in this thread i swear ;)