Object Selection Using the Back Buffer

http://www.opengl.org/discussion_boards/showthread.php/130628-performance-issues-do-they-ever-end?goto=nextoldest 
Hi,
I'm trying to implement a 3D editor program. I'm having trouble getting the selection mechanism to work.
I'm using the approach outlined in Chapter 14 of the Red Book ("Object Selection Using the Back Buffer"). According to this approach I need to redraw the scene into the back buffer with the objects encoded by colour. Then I read the colour of the pixel under the cursor.
I've implemented this, however I've run into two problems.
1. I don't understand how the viewing position is included in this approach. This obviously makes a difference (e.g. compare a scene containing a rectangle drawn in the x-z plane when viewed from above as opposed to when viewed from the front). Also, if I translate the object then things don't work properly.
<?> 
2. The second question follows on from the previous one. My application uses two subwindows which show different views of the object. Is there a way to detect which subwindow the user clicked in? If not, how do I know which view to use?
<我的程序使用两个子窗体来显示一个对象的不同视图。有什么方法可以知道用户在哪个子窗体中点击了?要不,我如何知道哪个视图被使用?> 

 Here's the selection code that I'm using:

void mouse(int button, int state, int x, int y)
{
    GLfloat pixel[4];                                            // An RGBA pixel.
    GLint value;

    if(state == GLUT_DOWN)
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glDrawBuffer(GL_BACK);
            
        glPushMatrix();
        glColor3f(1.01.01.0);
        glTranslatef(0.0, -1.00.0);
        glRotatef(gRotate, 0.0f1.0f0.0f);
        glutSolidSphere(1.03232);
        glPopMatrix();

        glColor3f(0.00.00.0);
        glBegin(GL_QUADS);
            glVertex3i(-10010);
            glVertex3i(10010);
            glVertex3i(100, -10);
            glVertex3i(-100, -10);
        glEnd();

        glReadBuffer(GL_BACK);
        glReadPixels(x, y, 11, GL_RGBA, GL_FLOAT, pixel);
        printf("R: %f\t G: %f\t B: %f\t\n", pixel[0], pixel[1], pixel[2]);
        value = (GLint)pixel[0];

        if(value == 1)
            printf("Hit!\n");
        else
            printf("Miss!\n");
    }
}

I've figured out how to detect the different subwindows--use a different mouse function for each subwindow (obvious really!).

I'm still not clear about how to integrate the viewing position into the code.

Any ideas?

Cheers,

Chris


RigidBody

you have to draw the scene into the back buffer using exactly the same projection and modelview matrices, otherwise it will not work.

 

using glColor3f (float) could result in rounding errors. if you draw something with glColor3f(0.3, 0.4, 0.5) and read the color values from the buffer, you might get

something close to (0.3, 0.4, 0.5), but not the exact values. better use glColor3ub (unsigned byte).

int object_id = 123456;
char r = object_id&amp;255, g = (object_id/256)&amp;255, b = (object_id/65536)&amp;255;
glColor3ub(r,g,b); 

// draw object

note that this does only work in the above way if you have 24-bit colours.

 

if i understand your question, the answer is: it doesn't make a difference if you're in front or back buffer. you can choose a buffer to render to, but that will not change the projection or modelview matrix.

actually, you don't even need to select glDrawBuffer(GL_BACK), because in a double-buffered configuration, the back buffer is default for rendering.

so you just have to draw your scene as usual, but without lighting, blending etc. and with the appropriate colours and without swapping buffers.



I'm working on color picking in my project. I have everything working correctly. The primitives each get their own color and a mouse event correctly identifies one. The problem is, I can only seem to read from the front buffer, when the primitives are rendered on screen. I need this to happen 'behind the scenes'. I thought I could use something like:

glDrawBuffer(GL_BACK)

//render

glReadBuffer(GL_BACK)

glReadPixels(...)

This doesn't appear to work. Should it? Alternatives?

In advance, thanks for your time!

 


Sounds like you might need to ensure that you've created a double-buffered context.


I've done this before, long time ago, so I may not remember correctly and hardware may behave differently nowadays. Off the top of my head...
Make sure you call glFinish so that everything is properly rendered and ready before you read it back. Swapbuffers supposedly does this (IIRC causes the well known pipeline stall), and glReadpixels seems to call glFinish under-the-hood with most drivers, but you never know (glFinish appears to be there for a reason).
By the way, I don't think v-sync has any implications in this case, since we're not calling Swapbuffers.

<确信你调用glFinish,这样在你从后缓冲区读像素时,所有的东西都被渲染了。Swapbuffers可是是如些做的。glReadpixels好像大多数驱动幕后调用glFinish,但是调不调用你不会知道> 

Ensure a double buffered context as mentioned by others.
Pixel ownership is a big issue. If the window is partially obscured by another overlapping window (or dropdown menus from the application), the pixels under that region may be black or typically garbage and not what you intended to render there. For picking/selection this is typically not an issue, but it might be for area selection, screengrabs, radiosity lightmap rendering, texture baking and other fancy stuff.

<像素是一个大问题。如果窗体部分被overlapping层覆盖(如下拉菜单),像素就是个大问题> 

I should note that on nVidia hardware with latest drivers, pixels are actually rasterized proper (the spec allows this), but don't rely on this behavior. In combination with FSAA and glViewport calls, you may still get garbage (or on nVidia, pixels that fail pixelownership test have the gray window background color).
Use PBO or FBO if you want to be sure you get pixels on offscreen surfaces. I've found PBO's easier for non-realtime critical image feedback stuff since they are easier to set up than FBO's, but if you want total control and maximum hardware acceleration, go for FBO. PBO support may also have diminished quality in modern drivers due to neglect, as FBO's are supposed to replace them.
Ensure that the drivers don't force full screen anti-aliasing and transparancy anti-aliasing (alpha-to-coverage) if you're using alpha-testing. This will obviously screw up your colors and thus indices!
Also, the OpenGL spec still doesn't (AFAIK) guarantee pixel accuracy, both color and raster position.
As for rasterization position accuracy, for single-click picking, read back 3x3 pixels to ensure you always catch 1x1 pixels (e.g. for vertex selection). While on the subject, use glPointSize/glLineWidth to make it easier to catch vertices/lines. Bonus points if you push back occluding geometry with glPolygonOffset (so that lines and vertices don't z-fight with polygons) to make vertex/edge selection easier for the user.
When you do color picking, allow some margin. E.g. RGB 25/25/25 to RGB 27/27/27 correspond to the same index, rather than exactly RGB 26/26/26). Color picking will likely fail if the user has desktop bit depth set to 16-bit. With some effort you can adapt the color margin to the bit depth, this will greatly reduce the range of indices you can use, though multi-pass rendering can be a solution (yikes!). If you only have a handful of objects/triangles/vertices, this should not be an issue though.
Watch out for rounding errors when doing the RGB<-->index conversion. Assume the colors will be slightly off (margin!), and always do a bounds check on the readback index result to catch index-out-of-bounds errors.
If you're smart you will do a conformance test when the application starts, and notify the user if the bit-depth or pixel accuracy fails acceptable standards. Ideally, display this along with the GL_VENDOR string so that the user can report to the manufacturer that they need to fix their crappy drivers (*cough* Intel).
Ultimately, picking/selection is best done with pure software rendering/plain math calculations, but if you take care of the things I mentioned it should be fairly reliable.
Hope that helps.
posted @ 2012-05-30 10:14  thinkpore  阅读(406)  评论(0编辑  收藏  举报