2

I read many sample code about opengl picking. Nearly all of them use gluPerspective funcion for projection.I'm using glOrtho instead of gluPerspective function.

And my Selection function is as below(DrawBuffer is my paint code):

void Selection( int x, int y )
{
    GLuint buffer[512];
    GLint hits;

    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(512, buffer);

    (void)glRenderMode(GL_SELECT);

    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    GLdouble w = (double)m_ClientRect.Width();
    GLdouble h = (double)m_ClientRect.Height();
    gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3] - y), 500, 500, viewport);
    glOrtho(-w / 2, w / 2, -h / 2, h / 2, -1000000.0, 100000.0);
    glMatrixMode(GL_MODELVIEW);
    DrawBuffer();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    hits = glRenderMode(GL_RENDER);

    if (hits > 0)
    {
        TRACE(_T("%d %d %d %d %d\n"), hits, buffer[0], buffer[1], buffer[2], buffer[3]);
    }
}

But it doesn't work, I can't figure out the reason? Another problem is: When I using glDrawArrays function to draw many lines, how can I call glLoadName to flag each of them?

My raytracer algorithm is as follow:

void CGraphicView::KDSearch( PICKING_VERTEX *root, CRay *pRay, PICKING_VERTEX **found, double *dCurSplit )
{
    if (NULL == root)
    {
        return;
    }
    SearchNode(root, m_pRay, m_globaltMin, m_globaltMax, found, dCurSplit);
}

void CGraphicView::SearchNode( PICKING_VERTEX *node, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
    if (NULL == node)
    {
        return;
    }
    if (node->bLeaf)
    {
        SearchLeaf(node, pRay, tmin, tmax, found, dCurSplit);
    }
    else
    {
        SearchSplit(node, pRay, tmin, tmax, found, dCurSplit);
    }
}

void CGraphicView::SearchSplit( PICKING_VERTEX *split, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
    if (NULL == split)
    {
        return;
    }
    int axis = split->axis;
    double thit = pRay->GetSplit(axis, split->coor[axis]);

    Point3D pSrc(split->coor[0], split->coor[1], split->coor[2]);
    double scale = m_pCam->GetScale();
    double disP2L = DistanceP2L(pSrc, m_RayStart, m_RayEnd);
    if (disP2L * scale < MAX_DISTANCE && thit < *dCurSplit)
    {
        *found = split;
        *dCurSplit = thit;
    }

    PICKING_VERTEX *first = NULL, *second = NULL;
    if (IS_EQUAL_FLOAT(pRay->m_direction[axis], 0.0))
    {
        first = (pRay->m_origin[axis] < split->coor[axis]) ? split->left : split->right;
    }
    else
    {
        first = (pRay->m_direction[axis] > 0.0) ? split->left: split->right;
        second = (pRay->m_direction[axis] < 0.0) ? split->left : split->right;
    }

    if ((thit >= tmax || thit < 0))
    {
        SearchNode(first, pRay, tmin, tmax, found, dCurSplit);
    }
    else if (thit <= tmin)
    {
        SearchNode(second, pRay, tmin, tmax, found, dCurSplit);
    }
    else
    {
        SearchNode(first, pRay, tmin, thit, found, dCurSplit);
    }
}

void CGraphicView::SearchLeaf( PICKING_VERTEX *leaf, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
    if (NULL == leaf)
    {
        return;
    }

    int axis = leaf->axis;
    double thit = pRay->GetSplit(axis, leaf->coor[axis]);

    Point3D pSrc(leaf->coor[0], leaf->coor[1], leaf->coor[2]);
    double scale = m_pCam->GetScale();
    double disP2L = DistanceP2L(pSrc, m_RayStart, m_RayEnd);
    if (disP2L * scale < MAX_DISTANCE && thit < *dCurSplit)
    {
        *found = leaf;
        *dCurSplit = thit;
    }

    ContinueSearch(leaf, pRay, tmin, tmax, found, dCurSplit);
}

void CGraphicView::ContinueSearch( PICKING_VERTEX *leaf, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
    if (IS_EQUAL_FLOAT(tmax, m_globaltMax))
    {
        return;
    }
    else
    {
        tmin = tmax;
        tmax = m_globaltMax;
        SearchNode(m_root, pRay, tmin, tmax, found, dCurSplit);
    }
}
Merlin
  • 173
  • 7

2 Answers2

1

When I using glDrawArrays function to draw many lines, how can I call glLoadName to flag each of them?

You can't. And frankly: You should not use OpenGL selection mode in the first place! It's slow, no current driver supports it (you'll always drop back into software emulation mode with it), it doesn't work (well) with shaders and is cumbersome to use.

A far better alternative is to either backproject selection rays into the scene or (if modern OpenGL is used) to use a transform feedback buffer applied in bounding boxes (or other kind of bounding volume) to sort geometry into a screen space Kd-tree from where you can quickly select what's been clicked onto.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Because I draw the lines with function glDrawArrays, so I cut the graphic into small lines and use the endpoints of lines to built KD tree, because in 3D, I must change the mouse coordinate to the world coordinate, the result is very big. – Merlin May 24 '13 at 05:59
  • @Merlin: If performance is your primary concern, then *stay away* from OpenGL selection mode. It's about the slowest picking method there is. Also the size of the resulting data structure scales proportionally with the amount of lines. When you think about "very big", can you quantify it? kilobytes? Megabytes? What's the actual number? Don't guess, calculate, or make a sound estimation. – datenwolf May 24 '13 at 07:36
  • :Because I am using glOrtho function, near and far parameters are -1000000.0 and 1000000.0, if setting too small some graphics may be missing, the 3D coordinate is too big in z-axis. – Merlin May 24 '13 at 07:59
  • @Merlin: Ah, by big you mean "large numerical value". But that's no problem, because you're using floating point, where each number has the same size. Also for that Kd-tree the depth coordinate matters, only XY is of interest for picking. If doing a screen space KD-tree approach you can simply drop the Z coordinate. – datenwolf May 24 '13 at 08:18
  • thx, Another problem, while doing a screen space KD-tree approach, I can't get a right answer. I can't figure out whether the problem lies in the KD-tree or my own problem. My KD-tree algorithm is follow [link](http://rosettacode.org/wiki/K-d_tree#C) – Merlin May 24 '13 at 08:32
  • And if the Z-coordinate is too large, the nearest distance increase too, how to set the distance value that indicate a graphic picked. – Merlin May 24 '13 at 08:54
  • @Merlin: For picking you don't need the Z coordinate. Just project your lines into screen space (`v_c = P · MV · v; v_s = v_c/v_c.w`) drop the Z and W component and build the Kd-tree using just the screen space x and y components (`v_s.xy`). The near/far range of ortho doesn't really matter; for building the Kd-tree the projected Z component would just fall out of range. Since you're using fixed function OpenGL I strongly recommend, not to use OpenGL at all for doing the projection into the Kd-tree. – datenwolf May 24 '13 at 10:28
  • thx, you mean that I can create a 2D KD-tree instead of 3D?And what does 'v_c = P * MV * v; v_s = v_c / v_c *w' mean? Can you explain it ? And does it matter that if I rotate the scene or other MODEL_VIEW transformation? – Merlin May 24 '13 at 13:32
  • I'm sorry that I'm a new guy to OpenGL. Is that I must recreate the KD-tree once I transforma my graphic? 3D to 2D KD-tree, just ignore Z-coordinate? – Merlin May 25 '13 at 08:49
  • @Merlin: Exactly that way. Transform your geometry, then put it into a Kd-tree. – datenwolf May 25 '13 at 10:36
  • If there is mass data, recreate the KD-tree will slow the speed. You said that create a KD-tree that corresponds to what's visible on the screen, is that a selecting problem? – Merlin May 25 '13 at 11:13
  • @Merlin: First I suggested to build a Kd-tree only from the bounding volumes of the things you want to have selectable. If your view is animated and constantly changing, then yes, rebuilding the Kd-tree will consume some time. But having a search tree (you can traverse Kd in *O(n log n)* best case *O(n)*) will always be faster than a simple linear search, which is the only alternative if you were to pursue naive ray picking. Building a 3D Kd-tree may be useful if the only thing happening was merely rotating/shifting the view, and the scene was constant. – datenwolf May 25 '13 at 12:58
  • @Merlin: But with changeable scene a screen space 2D Kd-tree based on objects view axis aligned bounding volumes is a reasonable choice. – datenwolf May 25 '13 at 12:58
  • If I just change the view , rotation, scale or translation, but the graphic data is the same, if the KD-tree need to be rebuilt, my main data is just lines, mass small length lines. – Merlin May 25 '13 at 13:26
  • @Merlin: Well, in that case a 3D Kd-tree into which you backproject may be better, indeed. However the value range of your lines' endpoint coordinates has no effect on the actual size of the Kd-tree. The only important number is the number of elements in the Kd-tree, and that would be the number of lines. – datenwolf May 25 '13 at 14:48
  • A 3D KD-tree is work when z-axis is perpendicular to the screen, y-axis is up and x-axis is right. And when I rotate the scene just around the z-axis, it works too. But when I rotate the scene arount x or y-axis, it doesn't work. Because the near and far parameters of function glOrtho is -1000000.0 and 1000000.0, the x and y coordinate will be big when you rotate the scene around y and x-axis respectively. Is that the reason? – Merlin May 27 '13 at 03:46
  • If I calculate a ray that consists of two points which are the crosspoints generated by the ray and the near and far frustums, and calculate the nearest distance between the ray and the KD-tree elements? – Merlin May 27 '13 at 07:07
  • @Merlin: Each Kd-tree element is bordered by planes, forming a closed volume. In 2D rectangles, in 3D cuboids. And those volumes are contained by the volume formed up a branch in the tree. So what you do is, checking the intersection of the ray with the bounding volumes of the Kd-tree nodes, working down the tree until you hit a/the leaf with a bounding volume through which the ray crosses. – datenwolf May 27 '13 at 09:57
  • How do I check that? The tree's nodes are points. I must check all the points on the ray? – Merlin May 27 '13 at 13:23
  • @Merlin: Every branch – i.e. at points that are not leafs – of a Kd-tree is a plane, subdividing the subspace into two half spaces, each forming one branch. You test against those planes. – datenwolf May 27 '13 at 13:30
  • You meant that we check from the root plane, get the cross point with the ray, check the point on which side, and get the crosspoint with the ray, and then next, until the leaf? – Merlin May 27 '13 at 14:35
  • But the planes aren't infinite. The ray may be not cross with some planes, if so, how to determin the next plane. – Merlin May 28 '13 at 02:17
  • Now I knew the fundamental of ray tracing, but it works badly. Does it matter if there are too much equal values in the same dimension? Now the situation is: When a primitive is selected, it's correct. But most of primitives can't be selected. – Merlin Jun 04 '13 at 08:12
  • @Merlin: Do you have your current code somewhere available? Preferrably in the form of a Git (or Mercurial or Darcs) repository? I really must see the code to answer this. – datenwolf Jun 04 '13 at 10:41
  • My kd tree code reference to [kdtree](http://rosettacode.org/wiki/K-d_tree#C). My raytracer algorithm is link to the [site](http://graphics.stanford.edu/papers/gpu_kdtree/kdtree.pdf). The kdtree stores data only in leaves which used in this paper. Mine kdtree store datas in interior nodes and leaves. – Merlin Jun 04 '13 at 12:40
  • My raytracer algoritm is added at the question. The parameter 'dCurSplit' is the cross position of the ray, it's used to determine which cross point is nearest to the ray orgin. – Merlin Jun 05 '13 at 01:16
  • There is something wrong with the kd tree create medthod, for example, list: {{1, 0, 0}}, {{1, 1, 0}}, {{0, 1, 0}}, {{0, 0, 0}}, the result is wrong, but change the order to: {{0, 0, 0}}, {{1, 0, 0}}, {{1, 1, 0}}, {{0, 1, 0}}, it's right. I can't figure out the reason. – Merlin Jun 05 '13 at 04:14
  • There is something wrong with the function find_median of the [kdtree](http://rosettacode.org/wiki/K-d_tree#C) I used. At the return statement, the judgement is incorrect. – Merlin Jun 06 '13 at 07:08
  • Can I have your some live chat account? For purpose to practise my poor English. – Merlin Jun 24 '13 at 03:20
  • @Merlin: For StackOverflow chat you need a reputation of at least 200 points. There's little (==nothing) I can do about that. – datenwolf Jun 24 '13 at 07:36
0

Well, from memory one way to debug hits == 0 was to use exactly the same pick matrix for a normal render, or in this case just comment out the glRenderMode calls. If you don't see anything drawn, then no part of your scene intersects the pick area and the selection code is just doing what you told it to.

However, datenwolf is right and you really should avoid OpenGL selection mode. It's horrible.

A fairly simple way to implement picking that doesn't require raycasting or Kd-trees is to draw each object in a different color. Assuming every object has a unique identifier number (which you'd need for glLoadName anyway), convert it into an 3 byte RGB color value. Draw the scene into the back buffer only, then read the pixel under the mouse coordinates. That RGB value will be the identifier of the frontmost object.

Hugh Fisher
  • 2,321
  • 13
  • 8
  • There is something wrong with the function find_median of the [kdtree](http://rosettacode.org/wiki/K-d_tree#C) I used. At the return statement, the judgement is incorrect. Can you help me figure out the return judgement. – Merlin Jun 06 '13 at 07:09