简体   繁体   English

OpenGL 3D拾取

[英]OpenGL 3D Picking

I read many sample code about opengl picking. 我阅读了许多有关opengl挑选的示例代码。 Nearly all of them use gluPerspective funcion for projection.I'm using glOrtho instead of gluPerspective function. 几乎所有人都使用gluPerspective函数进行投影。我使用glOrtho代替了gluPerspective函数。

And my Selection function is as below(DrawBuffer is my paint code): 我的选择函数如下(DrawBuffer是我的绘画代码):

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? 另一个问题是:当我使用glDrawArrays函数绘制多条线时,如何调用glLoadName来标记每条线?

My raytracer algorithm is as follow: 我的raytracer算法如下:

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);
    }
}

When I using glDrawArrays function to draw many lines, how can I call glLoadName to flag each of them? 当我使用glDrawArrays函数绘制多条线时,如何调用glLoadName来标记每条线?

You can't. 你不能 And frankly: You should not use OpenGL selection mode in the first place! 坦率地说:首先您不应该使用OpenGL选择模式! 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. 更好的替代方法是将选择光线投射到场景中,或者(如果使用现代OpenGL)使用边界框(或其他类型的边界体积)中应用的变换反馈缓冲区将几何体分类到屏幕空间Kd-tree中您可以在其中快速选择要单击的内容。

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. 好了,从内存中调试命中== 0的一种方法是对普通渲染使用完全相同的拾取矩阵,或者在这种情况下,只需注释掉glRenderMode调用即可。 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. 但是,datenwolf是正确的,您确实应该避免使用OpenGL选择模式。 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. 不需要光线投射或Kd树的实现拾取的一种相当简单的方法是用不同的颜色绘制每个对象。 Assuming every object has a unique identifier number (which you'd need for glLoadName anyway), convert it into an 3 byte RGB color value. 假设每个对象都有一个唯一的标识符号(无论如何,glLoadName都需要),将其转换为3字节RGB颜色值。 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. 该RGB值将是最前面的对象的标识符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM