PDA

View Full Version : Picking in newest OpenGL (4.1)



Ingvar
09-16-2010, 05:31 AM
I have been on and off developing in OpenGL for several years.
Until recently, I have not had any need going beyond the functionality of version 1.4, but am currently on a bigger project.

This project contains perhaps thousands of objects and millions of polygons. Occlusion reduces this a ways, but real time flow (high fps) and continuous mouse cursor updates (thus picking) are required.

I have come across several pages talking about the "good old" OpenGL picking not being supported, or significantly slowed down with OpenGL 3.0 and newer. Developers are reporting a severe performance drop and sometimes seconds lag when picking.

( Here's a thread as an example
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=283142 )

OpenGL.org still refer to the "good old" picking method through their pages:
- On the menu: Coding Resources -> Sample Code & Tutorials
- Link: Beginning OpenGL
- Link: Picking tutorial
- Takes you to: http://www.opengl.org/code/detail/picking_tutorial/

And the most recent reference card still lists the same functions:
http://www.khronos.org/files/opengl41-quick-reference-card.pdf
On page 5, under "Special Functions":

Selection [5.2]
Determine which primitives are drawn into a
region of a window. The region is defined by the
current model-view and perspective matrices.
void InitNames(void);
void PopName(void);
void PushName(uint name);
void LoadName(uint name);
int RenderMode(enum mode);
mode: RENDER, SELECT, FEEDBACK
void SelectBuffer(sizei n, uint *buffer);

The functions seem to still be supported, but are they potentially terribly slow, are they supported by all, and are they obsolete (as some say) or discouraged?

If the "good old way is" discouraged, what other picking methods are recommended for a program of large scale with loads of models to draw and pick?
(Imagine a 3D reconstruction of our world.)

Thank you!

Jacek Nowak
09-16-2010, 07:01 AM
Have you used opengl legacy selection/picking? It isn't hardware accelerated at all, so it should be VERY slow.

You could try color picking - render every object with a unique color (very basic shader, should be quick), then read the pixel color under the cursor (this could be slower, but still should be much faster than opengl selection)

marcus256
09-16-2010, 07:09 AM
Just be careful with color ranges. Colors (unlike stencil, for instance) may be rounded/converted to different pixel formats etc (e.g. 16-bit vs 32-bit), meaning that an exact integer mapping may not be possible (at least it might be sensitive to which hardware it's running on).

One solution (if you have really, really many objects) might be to combine different buffers, e.g. stencil (256 values) * color (at least 32768 values).

Ingvar
09-17-2010, 05:00 AM
Thank you, guys.
I will take a closer look into color picking. I was thinking the limit of objects/colors would be fairly low, but it should be enough.

With color picking, you can only pick the front object, though?

david_f_knight
09-17-2010, 12:22 PM
With color picking, the limit of objects/colors is 2 raised to the total number of color bit planes in your framebuffer. If you use the typical 8 bit planes per red, green, blue, and alpha channel, then you have 32 color bit planes, or over four billion unique object IDs available.

With color picking, you cannot use blending/transparency, so you can only pick the front object. If you need to let the user pick from among translucent objects that may be overlapping each other, then you need to use an entirely different approach due to the ambiguity of identifying overlapping objects, such as with a feedback mechanism. For example, when the user rotates the mouse wheel, you could highlight a different object with each tick of the wheel. When the user has the desired object highlit, he could press the mouse button or the Enter key on the keyboard or something to indicate that he has selected the object that he wants processed. Very easy to do, and completely hardware accelerated.

You could also do a hybrid approach: draw each object in the framebuffer one at a time and then go through the color pick process. Clear the framebuffer between each iteration of drawing an object and testing if the cursor is over it. Build up a list of all the objects, if any, that are under the cursor. Now you could have the user, for example, scroll the mouse wheel, and you would highlight each object one at a time with each tick of the mouse wheel that you had previously identified as being under the cursor. When the user has the desired object highlit, they can click the mouse button or something. This approach filters out all the objects that aren't under the cursor making the selection process far faster for the user if there are many objects from which to choose (especially if some are off screen).

carsten neumann
09-17-2010, 03:30 PM
You could also do a hybrid approach: draw each object in the framebuffer one at a time and then go through the color pick process. Clear the framebuffer between each iteration of drawing ...


or use depth peeling to reduce number of framebuffer clears at the expense of more geometry passes - not sure if that is beneficial though.

if the rendering of the scene for color picking is a bottleneck one can use the knowledge of the cursor position to render just a small image of the area around it (adjusting the projection matrix accordingly of course). Together with frustum culling this will reduce the number of objects that actually need to be drawn considerably.

Ingvar
09-18-2010, 07:35 AM
Thanks again!

Seems with color coding; blending, lighning, antialiasing, shading can be turned off and colors drawn should stay relatively or entirely unaltered when they "reach the buffer". Or?

- Will one always be able to distinguish between glColor3ub(99,99,99) and glColor3ub(100,99,99), or are there any chance a color could end within a close range, and thus one should allow tolerances and thus reducing the number of available colors a fair bit?

- Can the alpha chanel be treated as a fourth color - i.e. glColor4ub(99,99,99,99) is entirely different from glColor4ub(99,99,99,100) though these would look same on the screen without blending?

- I suppose one should also add a lock on the back buffer to avoid double drawing. Wrong? Or use a separate buffer?

david_f_knight
09-18-2010, 12:02 PM
Yes, you are correct, with color coding it is essential that anything that modifies the color is prohibited. (Just to be explicit, all forms of antialiasing, including multisampling, must be turned off, and as you wrote, blending, lighting, and also fog must be turned off.) If so, then the colors will be entirely unaltered when they are written to the color buffers.

Provided nothing modifies colors as discussed in the previous paragraph, and provided your color buffers have sufficient bit depth that they can exactly represent the values specified, then one can always distinguish glColor3ub (99, 99, 99) from glColor3ub (100, 99, 99), for example. No need to allow tolerances.

The alpha channel is part of the color framebuffer provided your rendering context was created with an alpha channel, and if so then glColor4ub (99, 99, 99, 99) is entirely different from glColor4ub (99, 99, 99, 100), again assuming your R, G, B, and A channels all have sufficient bit depth to represent each of these values exactly. For the examples you've provided, seven bits per channel would be sufficient, but of course eight bits per channel is what is almost always used. The number of bits per channel has to do with how you create your framebuffer, which is determined by your operation system rather than by OpenGL.

I don't know what you mean by double drawing on the back buffer. Do you mean something like sharing a rendering context between multiple threads that may all be writing simultaneously to the same framebuffer? I have never looked into doing something like that. If you are doing something like that, then, as always, you would have to properly synchronize and coordinate all your threads. But generally, you only have one thread that owns any particular rendering context, and each rendering context has its own framebuffer, and if so one thread can't interfere with another. I don't see any reason to use a separate buffer, unless you have a static viewpoint and static geometry etc... then you could render your object IDs once in one full window buffer and render all your viewable stuff in the regular color buffers and not need to rerender the object IDs unless something in your frame's view changes.

One more thing: before you render your object IDs into the color buffers, you should clear the color buffers with a value that you will recognize as never being used for a valid object ID. That way you can distinguish picking the background versus picking an object.

Ingvar
09-19-2010, 10:51 AM
Lots of good stuff there. Thanks a lot!