OpenGL GL_SELECT or manual collision detection?
Asked Answered
B

7

13

As seen in the image

https://static.mcmap.net/file/mcmap/ZG-Ab5ovKRfpMTWQcFlQd7BpWw2jaRA/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.

Brenn answered 28/10, 2010 at 7:14 Comment(3)
i appreciate all the answers,since I can only give bounty credit once and select one answer.I choose Kos answer as correct,since I have implemented and give bounty to Redrobes ,for good explanation on geometry. thanks all.Brenn
Look into how Irrlicht does it, example ./7.collision in 1.8.1 does it.Drislane
The name of this operation is "ray casting": unity3d.com/learn/tutorials/modules/beginner/physics/raycasting an Bullet physics can also do it: bulletphysics.org/mediawiki-1.5.8/index.php/Using_RayTestDrislane
A
20

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.

Assizes answered 30/10, 2010 at 16:52 Comment(8)
thanks for your reply.Will this method work fine for contours (lines) ? since I am not rendering solid polygons?Brenn
This means,I have to render the object two times ?b First draw for color picking ,then read pixel value. Then render in normal way ?Brenn
Yes, that's right. you draw every object twice. Since for the picking phase you only redraw one single pixel of the screen (because of glViewport), this shouldn't be a performance problem. About working for contours- basically if you draw the lines without blending etc, it should work perfectly, the only downside is that the user will need to click the line itself precisely. You might want to enlarge glLineWidth for the picking stage. Is this the behaviour you expect?Assizes
In case you want to be able to select a curve by clicking its interior, just draw the geometry as solid polygons (glPolygonMode) during the picking phase, and after that change it to draw lines again. That should do the trick. BTW- You're using GL_LINE_STRIP mode now? It might be feasible for you to use GL_POLYGON instead all the time and just switch glPolygonMode to GL_FILL for picking and GL_LINE for drawing.Assizes
Thank Kos for detailed explanation.My question is, if I first draw objects in GL_FILL ,I should clear the buffer before drawing my actual contours with GL_LINE right ? Doesn't it cause flickering ?Brenn
That's right, you should clear the buffers. No flickering will occur because you're not supposed to call SwapBuffers between the picking and the rendering stage - I assume you use double buffering, so the picking image won't ever be displayed. Please also note that if you use glViewport as I described, then only 1 pixel will be rendered to and then cleared.Assizes
+1 for viewport size reduction, didn't think about itAltissimo
@BartekBanachewicz I think I've made a mistake back then. The viewport must remain as is for the pixels to match, and the correct optimisation is to use scissor test. Edited, thanks!Assizes
C
6

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

Chiang answered 31/10, 2010 at 0:15 Comment(0)
P
3

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.

Profitsharing answered 1/11, 2010 at 15:4 Comment(0)
L
1

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.

Lambency answered 28/10, 2010 at 8:7 Comment(0)
A
1

Do you expect to select by clicking the contour (on the edge) or the interior of the polygon? Your second approach sounds like you want clicks in the interior to select the tightest containing polygon. I don't think that GL_SELECT after rendering GL_LINE_STRIP is going to make the interior responsive to clicks.

If this was a true contour plot (from the image I don't think it is, edges appear to intersect) then a much simpler algorithm would be available.

Aggress answered 30/10, 2010 at 16:50 Comment(0)
P
1

You cant use select if you stay with the lines because you would have to click on the line pixels rendered not the space inside the lines bounding them which I read as what you wish to do.

You can use Kos's answer but in order to render the space you need to solid fill it which would involve converting all of your contours to convex types which is painful. So I think that would work sometimes and give the wrong answer in some cases unless you did that.

What you need to do is use the CPU. You have the view extents from the viewport and the perspective matrix. With the mouse coord, generate the view to mouse pointer vector. You also have all the coords of the contours.

Take the first coord of the first contour and make a vector to the second coord. Make a vector out of them. Take 3rd coord and make a vector from 2 to 3 and repeat all the way around your contour and finally make the last one from coord n back to 0 again. For each pair in sequence find the cross product and sum up all the results. When you have that final summation vector keep hold of that and do a dot product with the mouse pointer direction vector. If its +ve then the mouse is inside the contour, if its -ve then its not and if 0 then I guess the plane of the contour and the mouse direction are parallel.

Do that for each contour and then you will know which of them are spiked by your mouse. Its up to you which one you want to pick from that set. Highest Z ?

It sounds like a lot of work but its not too bad and will give the right answer. You might like to additionally keep bounding boxes of all your contours then you can early out the ones off of the mouse vector by doing the same math as for the full vector but only on the 4 sides and if its not inside then the contour cannot be either.

Profitsharing answered 31/10, 2010 at 19:34 Comment(1)
thanks for the answer.My contours doesn't have a uniform direction.ie.some contours are clock-wise while others are counter-clock-wise. Do I have to change the cross-product operation depend on the direction?Brenn
P
0

The first is easy to implement and widely used.

Persistent answered 28/10, 2010 at 7:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.