代码之家  ›  专栏  ›  技术社区  ›  Arctic Pi

OpenGL:对象选择

  •  -3
  • Arctic Pi  · 技术社区  · 6 年前

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

    enter image description here

    从图中可以看到,选择在第一个视图中运行良好,但旋转摄影机时,对象未正确选择。

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

    struct CubeData
    {
        QMatrix4x4 model;
        bool selected;
    };
    
    QMap<QString, CubeData> cubes;
    

    初始化

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

    油漆工

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

    鼠标垫

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

    水立方

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

    拉伸试验

    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图像的。

    鼠标垫

    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 回复  |  直到 6 年前
        1
  •  0
  •   Arctic Pi    6 年前

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

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

    那么,把电话改成 glReadPixels 在里面 mousePressEvent 这边

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

    推荐文章