6
\$\begingroup\$

I'm programming a 3D game (using C++ and OpenGL), and I have a few 3D objects in the scene, we can say they are boxes for this example. I want to let the player click on those boxes to select them (ie. they might change color) with the typical restriction like if more than one box is located where the user clicked, only the one closest to the camera would get selected.

What would be the best way to do this? The fact that these objects go through several transforms before getting to window coordinates is what makes this a bit tricky.

One approach I thought about was that if the player clicks on the screen, I could normalize the x,y coordinates of mouse click and then transform the bounding box coordinates of the objects into clip-space so that I could compare then to the normalized mouse coordinates. I guess I could then do some sort of ray-box collision test to see if any objects lie as the path of the mouse click. I'm afraid I might be over complicating it. Any better methods out there?

\$\endgroup\$

3 Answers 3

7
\$\begingroup\$

First off you need to convert your mouse coordinate to a ray (a position and direction vector combo). This process is called 'un-projection' and the way I do it is to take the mouse x/y coordinate, convert it into screen space (-1 to +1 in both axes) by adding half the dimensions of the screen and then dividing by the screen dimensions and then construct two points (x, y, z, w): p(xi, yi, 0, 1) p1(xi, yi, 1, 1)

where xi and yi are screen space coordinate points (-1 to +1 rather than 0 to 'pixel_size_of_display') now multiply these by the inverse of the projection matrix and then divide the x,y,z components by the final 'w' component that you get after the matrix multiplication, i.e:

p.x = p.x / p.w; p.y = p.y / p.w; p.z = p.z / p.w;

You should now have two points: p and p1. Your ray will be formed from the point 'p' and the normalized vector between 'p' and 'p1'.

Finally, rotate the whole ray by the inverse of the view matrix and you have a ray that you can use to intersect with any primitive in the scene.

Test that it works by drawing the ray and constructing a new view of the scene from a different angle - this is pretty essential if you're testing visibility or intersection code anyway.

Next, Look up ray/sphere and ray/aabb intersection algorithms. I'd list ray/sphere here but this post is getting a bit long! Ray/Sphere intersections are quicker than ray/box intersections and generally I do a ray/sphere before final checking of a ray/box or ray/polygon mesh.

\$\endgroup\$
5
\$\begingroup\$

Usually, the mouse click co-ordinates are unprojected into world space, and then a ray intersection is performed. There are samples in the DirectX SDK but I wouldn't know where to find one for OGL.

\$\endgroup\$
0
\$\begingroup\$

Depends if you need this to be 100% accurate or not. If you want perfect accuracy then you have two answers already. However, if you want it fast and simple but less accurate you can project each object's bounding box on the screen and check if the mouse click is within it. Object's distance from the camera will be the third component of a projected vertex. You can perform the usual optimisations and this method is essentially free if you're already drawing some sort of selection box around objects your camera points at.

\$\endgroup\$

You must log in to answer this question.