Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL GL_SELECT or manual collision detection?

As seen in the image

http://oi56.tinypic.com/ifu33k.jpg

I draw set of contours (polygons) as GL_LINE_STRIP. Now I want to select curve(polygon) under the mouse to delete,move..etc in 3D .

I am wondering which method to use:

1.use OpenGL picking and selection. ( glRenderMode(GL_SELECT) )

2.use manual collision detection , by using a pick-ray and check whether the ray is inside each polygon.

like image 322
Ashika Umanga Umagiliya Avatar asked Oct 28 '10 07:10

Ashika Umanga Umagiliya


4 Answers

I strongly recommend against GL_SELECT. This method is very old and absent in new GL versions, and you're likely to get problems with modern graphics cards. Don't expect it to be supported by hardware - probably you'd encounter a software (driver) fallback for this mode on many GPUs, provided it would work at all. Use at your own risk :)

Let me provide you with an alternative.

For solid, big objects, there's an old, good approach of selection by:

  • enabling and setting the scissor test to a 1x1 window at the cursor position
  • drawing the screen with no lighting, texturing and multisampling, assigning an unique solid colour for every "important" entity - this colour will become the object ID for picking
  • calling glReadPixels and retrieving the colour, which would then serve to identify the picked object
  • clearing the buffers, resetting the scissor to the normal size and drawing the scene normally.

This gives you a very reliable "per-object" picking method. Also, drawing and clearing only 1 pixel with minimal per-pixel operation won't really hurt your performance, unless you are short on vertex processing power (unlikely, I think) or have really a lot of objects and are likely to get CPU-bound on the number of draw calls (but then again, I believe it's possible to optimize this away to a single draw call if you could pass the colour as per-pixel data).

The colour in RGB is 3 unsigned bytes, but it should be possible to additionally use the alpha channel of the framebuffer for the last byte, so you'd get 4 bytes in total - enough to store any 32-bit pointer to the object as the colour.

Alternatively, you can create a dedicated framebuffer object with a specific pixel format (like GL_R32UI, or even GL_RG32UI if you need 64 bits) for that.

The above is a nice and quick alternative (both in terms of reliability and in implementation time) for the strict geometric approach.

like image 190
Kos Avatar answered Nov 05 '22 22:11

Kos


I found that on new GPUs, the GL_SELECT mode is extremely slow. I played with a few different ways of fixing the problem.

The first was to do a CPU collision test, which worked, but wasn't as fast as I would have liked. It definitely slows down when you are casting rays into the screen (using gluUnproject) and then trying to find which object the mouse is colliding with. The only way I got satisfactory speeds was to use an octree to reduce the number of collision tests down and then do a bounding box collision test - however, this resulted in a method that was not pixel perfect.

The method I settled on was to first find all the objects under the mouse (using gluUnproject and bounding box collision tests) which is usually very fast. I then rendered each of the objects that have potentially collided with the mouse in the backbuffer as a different color. I then used glReadPixel to get the color under the mouse, and map that back to the object. glReadPixel is a slow call, since it has to read from the frame buffer. However, it is done once per frame, which ends up taking a negligible amount of time. You can speed it up by rendering to a PBO if you'd like.

Giawa

like image 6
Giawa Avatar answered Nov 06 '22 00:11

Giawa


umanga, Cant see how to reply inline... maybe I should sign up :)

First of all I must apologize for giving you the wrong algo - i did the back face culling one. But the one you need is very similar which is why I got confused... d'oh.

Get the camera position to mouse vector as said before.

For each contour, loop through all the coords in pairs (0-1, 1-2, 2-3, ... n-0) in it and make a vec out of them as before. I.e. walk the contour.

Now do the cross prod of those two (contour edge to mouse vec) instead of between pairs like I said before, do that for all the pairs and vector add them all up.

At the end find the magnitude of the resulting vector. If the result is zero (taking into account rounding errors) then your outside the shape - regardless of facing. If your interested in facing then instead of the mag you can do that dot prod with the mouse vector to find the facing and test the sign +/-.

It works because the algo finds the amount of distance from the vector line to each point in turn. As you sum them up and you are outside then they all cancel out because the contour is closed. If your inside then they all sum up. Its actually Gauss's Law of electromagnetic fields in physics...

See:http://en.wikipedia.org/wiki/Gauss%27s_law and note "the right-hand side of the equation is the total charge enclosed by S divided by the electric constant" noting the word "enclosed" - i.e. zero means not enclosed.

You can still do that optimization with the bounding boxes for speed.

like image 3
Redrobes Avatar answered Nov 06 '22 00:11

Redrobes


In the past I've used GL_SELECT to determine which object(s) contributed the pixel(s) of interest and then used computational geometry to get an accurate intersection with the object(s) if required.

like image 1
Daniel Paull Avatar answered Nov 05 '22 23:11

Daniel Paull