2

我阅读了许多关于 opengl 拾取的示例代码。几乎所有这些都使用 gluPerspective 函数进行投影。我使用 glOrtho 而不是 gluPerspective 函数。

我的选择函数如下(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]);
    }
}

但它不起作用,我无法弄清楚原因?另一个问题是:当我使用 glDrawArrays 函数绘制多条线时,如何调用 glLoadName 来标记每条线?

我的光线追踪器算法如下:

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

2 回答 2

1

当我使用 glDrawArrays 函数绘制多条线时,如何调用 glLoadName 来标记每条线?

你不能。坦率地说:你不应该首先使用 OpenGL 选择模式!它很慢,没有当前的驱动程序支持它(你总是会用它回到软件仿真模式),它不能(很好)与着色器一起使用并且使用起来很麻烦。

一个更好的选择是将选择光线反向投影到场景中,或者(如果使用现代 OpenGL)使用在边界框(或其他类型的边界体积)中应用的变换反馈缓冲区,将几何体从您可以在其中快速选择已单击的内容。

于 2013-05-21T11:21:56.860 回答
0

好吧,从内存中调试 hits == 0 的一种方法是使用完全相同的选取矩阵进行正常渲染,或者在这种情况下只需注释掉 glRenderMode 调用。如果您没有看到任何绘制的内容,那么您的场景中没有任何部分与选择区域相交,并且选择代码只是按照您的指示进行操作。

但是,datenwolf 是对的,您确实应该避免使用 OpenGL 选择模式。这太糟糕了。

一种不需要光线投射或 Kd-trees 来实现拾取的相当简单的方法是用不同的颜色绘制每个对象。假设每个对象都有一个唯一的标识符号(无论如何您都需要 glLoadName),将其转换为 3 字节 RGB 颜色值。仅将场景绘制到后台缓冲区中,然后读取鼠标坐标下的像素。该 RGB 值将是最前面对象的标识符。

于 2013-05-22T02:52:24.963 回答