OpenGL: выбор объекта - PullRequest
       49

OpenGL: выбор объекта

0 голосов
/ 19 декабря 2018

Я пытаюсь реализовать выбор объектов в OpenGL, но у меня есть некоторые проблемы.

enter image description here

Как видно из рисунка,Выделение работает хорошо в первом виде, но когда вы поворачиваете камеру, объект выбирается неправильно.

Я реализовал выделение таким образом.По щелчку мыши я рисую сцену в кадровом буфере, каждый объект изображен своим цветом (и также отличается от цвета фона), а затем проверяю, имеет ли пиксель, на который щелкнули, цвет некоторых объектов.

Это соответствующие фрагменты кода.

struct CubeData
{
    QMatrix4x4 model;
    bool selected;
};

QMap<QString, CubeData> cubes;

initializeGL

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 голосов
/ 20 декабря 2018

Я обнаружил (глупую) ошибку, которую я совершил.

Координаты пикселей, возвращаемые QMouseEvent :: pos () , начинаются в верхнем левом углу, в то время как те, которые принимаются glReadPixels начинается с нижнего левого угла.

Таким образом, изменение вызова на glReadPixels в mousePressEvent таким образом

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

решило проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...