使用SelectBuffer OpenGL选择多个名称

时间:2016-11-11 20:42:07

标签: c opengl

我使用选择缓冲区使用OpenGL的Picking机制相当困难。具体来说,我希望能够在所选对象中进行子选择。我的测试世界散布着多个对象,每个对象呈现四个"面孔",如下所示:

void drawObject(int i)
{
    renderFace0(i);
    renderFace1(i);
    renderFace2(i);
    renderFace3(i);
}

到目前为止一切都很顺利。参数i只是说出索引是什么,因为我的"对象"只是存储在数组中的数据。每个对象都是根据它的索引唯一标识的。然后我添加了pick功能,它更新当前指向的对象(我使用屏幕中心作为拾取对象的点):

void pickObject()
{
    unsigned int selectBuffer[200];
    int viewport[4];
    int hits;

    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(200, selectBuffer);

    glRenderMode(GL_SELECT);

    glInitNames();
    glPushName(0);

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

    gluPerspective(45.0f, (double)width / (double)height, 0.05f, 100.0f);

    double centerX = (double)width / 2.0;
    double centerY = (double)height / 2.0;

    gluPickMatrix(centerX, centerY, 5.0f, 5.0f, viewport);
    transformWorld();
    int i;
    for(i = 0; i < NUM_OBJECTS; i ++)
    {
        glLoadName(i + 1);
        drawObject(i);
    }

    hits = glRenderMode(GL_RENDER);
    if(hits > 0)
    {
        int znear;
        znear = selectBuffer[2];
        selected = selectBuffer[3];

        for(i = 1; i < hits; i ++)
        {
            if(selectBuffer[4 * i + 2] > znear)
            {
                znear = selectBuffer[4 * i + 2];
                selected = selectBuffer[4 * i + 3];
            }
        }

    } else selected = -1;

    glPopName();
    glPopMatrix();
    glFlush();
}

此时我应该注意到我完全在C中工作。再次,这一切都很好。正确选择了最近的对象(我知道这是因为我在渲染函数中使用selected变量为当前选中的红色对象着色)。但这真的不够信息,因为仅仅基于此,屏幕中心的指针可以指向该对象的任何一个面。我希望能够在对象上选择单个面,同时还能够识别对象本身已被选中。到目前为止,我尝试过的两件事情是:1)使用glPushNameglPopName代替glLoadName来绘制pickObject函数中的每个对象,然后使用{{1 }}和glPushNameglPopName函数中的每个单独的face,但是没有产生我想要的结果,以及2)创建一个名为drawObject的完全独立的函数,我运行正确在pickFace函数之后。它做同样的事情,但只渲染当前选出的对象并设置变量pickObject而不是selectedFace。我的猜测是,这是实现我想要的最低效的方式,但是,尽管它有效,但我想知道是否有更好的选择。

1 个答案:

答案 0 :(得分:0)

嗯,这个答案实际上让我发布费用相当愚蠢,因为这是一个简单的解决方案,我不知何故一直在忽视。如果我已经纠正了之后从选择缓冲区中检索id的方式,那么对每个对象使用glPushNameglPopName,因为我们每个面都会有效。所以drawObject函数变成了这个:

void drawObject(int i)
{
    glPushName(0);
    renderFace0(i);
    glPopName();

    glPushName(1);
    renderFace1(i);
    glPopName();

    glPushName(2);
    renderFace2(i);
    glPopName();

    glPushName(3);
    renderFace3(i);
    glPopName();
}

&#39; pickObject&#39;的更正功能是:

int i;
for(i = 0; i < NUM_OBJECTS; i ++)
{
    glPushName(i);
    drawObject(i);
    glPopName();
}

hits = glRenderMode(GL_RENDER);
if(hits > 0)
{
    int znear;
    znear = selectBuffer[2];
    selected = selectBuffer[3];
    selectedFace = selectBuffer[4];

    for(i = 1; i < hits; i ++)
    {
        if(selectBuffer[5 * i + 2] > znear)
        {
            znear = selectBuffer[5 * i + 2];
            selected = selectBuffer[5 * i + 3];
            selectedFace = selectBuffer[5 * i + 4];
        }
    }

} else selected = -1;

我错过的一个重大变化是将索引中的i * 4更改为i * 5,因为我在每个选择中都要查找两个名称,所以选择缓冲区的布局如下: {number of names hit, far value, near value, first name, second name, ...}然后重复该模式。所以我的步长必须从4改为5,因为每次命中有两个名称,现在每次命中选择缓冲区中有5个数据值,而不是4个。