OpenGL:对象选择

时间:2018-12-19 19:24:11

标签: c++ qt opengl selection framebuffer

我正在尝试在OpenGL中实现对象选择,但是我遇到了一些问题。

enter image description here

从图中可以看出,选择在第一个视图中效果很好,但是当您旋转相机时,对象选择不正确。

我以这种方式实现了选择。单击鼠标,将场景绘制在帧缓冲区中,每个对象以不同的颜色(也与背景颜色)描绘,然后检查单击的像素是否具有某些对象的颜色。

这些是相关的代码段。

struct CubeData
{
    QMatrix4x4 model;
    bool selected;
};

QMap<QString, CubeData> cubes;

初始化GL

void initializeGL()
{
    /* ... */

    auto FindColor = [=] ()
    {
        QVector<GLubyte> color;
        bool flag = true;

        while (flag)
        {
            color = RandomColor();
            flag = false;

            if (color == RgbFromColorToByte(background))
            {
                flag = true;
                continue;
            }

            if (cubes.contains(RgbFromByteToString(color)))
            {
                flag = true;
                continue;
            }
        }

        return color;
    };

    // cubes
    float offset = 3.0f;
    float x = -1.0f;
    while (cubes.size() < 3)
    {
        CubeData cube;
        cube.model.translate(x++ * offset, 0, 0);
        cube.selected = false;
        auto color = FindColor();
        cubes[RgbFromByteToString(color)] = cube;
    }
}

paintGL

void paintGL()
{
    /* ... */

    // enable depth test
    glEnable(GL_DEPTH_TEST);
    // clear buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // draw cubes
    for (auto cube : cubes)
    {
        DrawCube(cube);
    }

    update();
}

mousePressEvent

void mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        makeCurrent();
        glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::TEST));
        {
            // enable depth test
            glEnable(GL_DEPTH_TEST);
            // clear buffers
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            // draw test cubes
            DrawTest();

            // test pixel

            QVector<GLubyte> pixel;
            pixel.resize(3);

            glReadPixels(lastX, lastY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

            for (auto key : cubes.keys())
            {
                cubes[key].selected = false;
            }

            QString key = RgbFromByteToString(pixel);
            if (cubes.contains(key))
            {
                cubes[key].selected = true;
            }
        }
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
        doneCurrent();
    }
}

DrawCube

void DrawCube(CubeData cube)
{
    GLuint program = addProgram(ProgramIndex::CUBE);
    glUseProgram(program);

    glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, cube.model.constData());
    glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view.constData());
    glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection.constData());

    glUniform1i(glGetUniformLocation(program, "cube.selected"), cube.selected);

    glDrawArrays(GL_POINTS, 0, 1);
}

DrawTest

void DrawTest()
{
    GLuint program = addProgram(ProgramIndex::TEST);
    glUseProgram(program);

    glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view.constData());
    glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection.constData());

    for (auto key : cubes.keys())
    {
        glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, cubes[key].model.constData());
        glUniform3fv(glGetUniformLocation(program, "cube.TestColor"), 1, RgbFromStringToFloat(key).constData());

        glDrawArrays(GL_POINTS, 0, 1);
    }
}

多维数据集几何着色器

#version 450 core

struct Cube
{
    bool selected;
};

layout (points) in;
layout (triangle_strip, max_vertices = 14) out;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

uniform Cube cube;

out FragData
{
    smooth vec3 color;
} frag;

void main()
{
    vec3 offset[14];
    offset[0x0] = vec3(-1.0f, +1.0f, +1.0f);
    offset[0x1] = vec3(+1.0f, +1.0f, +1.0f);
    offset[0x2] = vec3(-1.0f, -1.0f, +1.0f);
    offset[0x3] = vec3(+1.0f, -1.0f, +1.0f);
    offset[0x4] = vec3(+1.0f, -1.0f, -1.0f);
    offset[0x5] = vec3(+1.0f, +1.0f, +1.0f);
    offset[0x6] = vec3(+1.0f, +1.0f, -1.0f);
    offset[0x7] = vec3(-1.0f, +1.0f, +1.0f);
    offset[0x8] = vec3(-1.0f, +1.0f, -1.0f);
    offset[0x9] = vec3(-1.0f, -1.0f, +1.0f);
    offset[0xA] = vec3(-1.0f, -1.0f, -1.0f);
    offset[0xB] = vec3(+1.0f, -1.0f, -1.0f);
    offset[0xC] = vec3(-1.0f, +1.0f, -1.0f);
    offset[0xD] = vec3(+1.0f, +1.0f, -1.0f);

    mat4 MVP = projection * view * model;

    vec3 albedo = cube.selected ? vec3(1.0f, 0.0f, 0.0f) : vec3(1.0f);
    // grayscale weights
    vec3 weight = vec3(0.2126f, 0.7152f, 0.0722f);
    // shade power
    float power = 0.5f;

    for (int i = 0; i < 14; i++)
    {
        gl_Position = MVP * vec4(offset[i], 1.0f);

        float shade = dot((normalize(offset[i]) + 1.0f) * 0.5f, weight);
        frag.color = albedo * (shade + (1.0f - shade) * power);

        EmitVertex();
    }

    EndPrimitive();
}

测试几何着色器

#version 450 core

struct Cube
{
    vec3 TestColor;
};

layout (points) in;
layout (triangle_strip, max_vertices = 14) out;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

uniform Cube cube;

out FragData
{
    flat vec3 color;
} frag;

void main()
{
    vec3 offset[14];
    offset[0x0] = vec3(-1.0f, +1.0f, +1.0f);
    offset[0x1] = vec3(+1.0f, +1.0f, +1.0f);
    offset[0x2] = vec3(-1.0f, -1.0f, +1.0f);
    offset[0x3] = vec3(+1.0f, -1.0f, +1.0f);
    offset[0x4] = vec3(+1.0f, -1.0f, -1.0f);
    offset[0x5] = vec3(+1.0f, +1.0f, +1.0f);
    offset[0x6] = vec3(+1.0f, +1.0f, -1.0f);
    offset[0x7] = vec3(-1.0f, +1.0f, +1.0f);
    offset[0x8] = vec3(-1.0f, +1.0f, -1.0f);
    offset[0x9] = vec3(-1.0f, -1.0f, +1.0f);
    offset[0xA] = vec3(-1.0f, -1.0f, -1.0f);
    offset[0xB] = vec3(+1.0f, -1.0f, -1.0f);
    offset[0xC] = vec3(-1.0f, +1.0f, -1.0f);
    offset[0xD] = vec3(+1.0f, +1.0f, -1.0f);

    mat4 MVP = projection * view * model;

    for (int i = 0; i < 14; i++)
    {
        gl_Position = MVP * vec4(offset[i], 1.0f);
        frag.color = cube.TestColor;
        EmitVertex();
    }

    EndPrimitive();
}

我还尝试通过以PPM格式保存其内容来查看整个测试帧缓冲区,但这就是我所得到的。

enter image description here

这是我生成PPM图像的方式。

mousePressEvent

void mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        makeCurrent();
        glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::TEST));
        {
            /* ... */

            int w = geometry().width();
            int h = geometry().height();

            QVector<GLubyte> frame;
            frame.resize(w * h * 3);

            glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &(frame[0]));

            QString name = "test.ppm";
            QFile file(name);
            file.open(QIODevice::WriteOnly);
            {
                QTextStream stream(&file);
                stream << "P3" << endl;
                stream << w << " " << h << endl;
                stream << "255" << endl;

                int i = 0;
                QVector<GLubyte> pixel;

                while (!(pixel = frame.mid(i++ * 3, 3)).isEmpty())
                {
                    stream << pixel[0] << " " << pixel[1] << " " << pixel[2] << endl;
                }
            }
            file.close();
        }
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
        doneCurrent();
    }
}

感谢您的任何建议。

1 个答案:

答案 0 :(得分:-1)

我发现了我犯的(愚蠢的)错误。

QMouseEvent::pos()返回的像素坐标从左上角开始,而glReadPixels接受的像素坐标从左下角开始。

因此,以这种方式在glReadPixels中将通话更改为mousePressEvent

glReadPixels(lastX, geometry().height() - lastY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

解决了问题。