c++ - opengl中如何用鼠标点击获取帧缓冲区中绘制图形的像素信息

标签 c++ qt opengl

我最近需要在QT+OPENGL环境下实现一个功能,就是在自己定义的framebuffer(我定义为fbo)中画了一个立方体(大概在view的中心),并涂成红色.默认缓冲区中没有绘制任何内容,所以我在 View 中看不到我的立方体(但它是在帧缓冲区 fbo 中绘制的)

我希望当我点击 View 中间(立方体的位置)时,我可以显示立方体的颜色----红色,而不是 View 背景的颜色。

以下是我遇到的问题: 我只能用鼠标获取 View 的背景颜色,在帧缓冲区中绘制的立方体没有被拾取(立方体是红色的)。

initializeGL() 函数中,我初始化并配置了帧缓冲区 fbo 以及绘制立方体所需的一些配置。

paintGL() 函数中,我将定义的帧缓冲区绑定(bind)到 GL_FRAMEBUFFER,然后进行绘图。

mousePressEvent(QMouseEvent *e)函数中,我做了拾取的工作。这是QT鼠标点击的响应函数。我把glReadPixels读到的信息放到数组pix中打印出来。

void OpenglWidget::initializeGL()
{   
        core1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_0_Core>();    
        core = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
        core->glGenFramebuffers(1, &fbo);
        core->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        // Create the texture object for the primitive information buffer
        core->glGenTextures(1, &m_pickingTexture);
        glBindTexture(GL_TEXTURE_2D, m_pickingTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, SCR_WIDTH, SCR_HEIGHT,
                    0, GL_RGB_INTEGER, GL_UNSIGNED_INT, NULL);
        core1->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                    m_pickingTexture, 0);
        // Create the texture object for the depth buffer
        core->glGenTextures(1, &m_depthTexture);
        core->glBindTexture(GL_TEXTURE_2D, m_depthTexture);
        core->glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT,
                    0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
        core1->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
                    m_depthTexture, 0);
        // Disable reading to avoid problems with older GPUs
        core->glReadBuffer(GL_NONE);
        // Verify that the FBO is correct
        GLenum Status = core->glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (Status == GL_FRAMEBUFFER_COMPLETE) {
            qDebug() << "ok";
        }
        // Restore the default framebuffer
        core->glBindTexture(GL_TEXTURE_2D, 0);
        core->glBindFramebuffer(GL_FRAMEBUFFER, 0);

        GLfloat verticPosition[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
    };
    GLint beamIndices[] = {
        0,1,2,3,4,5,6,7
    };

    core->glGenVertexArrays(1, &VAOBeam);
    core->glGenBuffers(1, &VBOBeam);
    core->glGenBuffers(1, &EBOBeam);
    core->glBindVertexArray(VAOBeam);
    core->glBindBuffer(GL_ARRAY_BUFFER, VBOBeam);
    core->glBufferData(GL_ARRAY_BUFFER,sizeof(float) * 3 * 36, verticPosition, GL_STATIC_DRAW);
    core->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOBeam);
    core->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * 8, beamIndices, GL_STATIC_DRAW);

    core->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    core->glEnableVertexAttribArray(0);
    core->glBindBuffer(GL_ARRAY_BUFFER, 0);
    core->glBindVertexArray(0);
}

void OpenglWidget::paintGL()
{
    core->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glClearColor(0,0,1,1);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    float frametime =GetFrameTime();
    mCamera.Update(frametime);
    Shader GLprogram =  Shader("E:\\QT\\OpenglTest\\vs.vert","E:\\QT\\OpenglTest\\fs.frag");
    GLprogram.use();
    core->glBindVertexArray(VAOBeam); 
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, mCamera.TempTranslateVrc);
    glm::mat4 view = glm::mat4(1.0f);
    //view = glm::lookAt(glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.5f,0.5f,0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    view = glm::lookAt(mCamera.mPos,mCamera.mViewCenter,mCamera.mUp);
    glm::mat4 projection = glm::mat4(1.0f);
    projection = glm::perspective(glm::radians(45.0f), (float)1.0, 0.1f, 100.0f);
    GLprogram.setMatrix4f("model",model);
    GLprogram.setMatrix4f("view",view);
    GLprogram.setMatrix4f("projection",projection);
    core->glDrawArrays(GL_TRIANGLES,0,36);
    core->glUseProgram(0);
    update();
}
void OpenglWidget::mousePressEvent(QMouseEvent *e)
{
    core1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_0_Core>();
    float pix[4] = {0.0};
    mCamera.getInitPos(e->x(),e->y());//This is the camera's setup function, no need to worry about it.
    core1->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
    core1->glReadBuffer(GL_COLOR_ATTACHMENT1);
    core1->glReadPixels(e->x() , 500 - e->y() + 65, 1, 1, GL_RGBA, GL_FLOAT, pix);
    qDebug() << e->x()<< 500 - e->y() <<pix[0] << pix[1] << pix[2] << pix[3];
}

我只能用鼠标获取view的背景颜色,在frame buffer中绘制的立方体没有被拾取(立方体是红色的)。

最佳答案

问题是因为在附加到 GLCOLOR ATTACHMENT1 的颜色缓冲区中,从未被渲染到。

第一个颜色缓冲区的枚举器常量的名称,可以通过 glFramebufferTexture2D 附加到帧缓冲区的是 GL_COLOR_ATTACHMENT0,而不是 GL_COLOR_ATTACHMENT1:

void OpenglWidget::initializeGL()
{
    // [...]

    core1->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
        GL_COLOR_ATTACHMENT0, // <---- GL_COLOR_ATTACHMENT0 instead of GL_COLOR_ATTACHMENT1
        GL_TEXTURE_2D,
        m_pickingTexture, 0);

    // [...]
}
void OpenglWidget::mousePressEvent(QMouseEvent *e)
{
    // [...]        

    core1->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
    core1->glReadBuffer(GL_COLOR_ATTACHMENT0); // <---- GL_COLOR_ATTACHMENT0

    // [ ...]
}

请注意,默认情况下,它会渲染到帧缓冲区的第一个颜色缓冲区 (GL_COLOR_ATTACHMENT0)。如果你想渲染到另一个颜色缓冲区,而不是第一个,那么你必须通过 glDrawBuffers 明确指定一个颜色缓冲区(列表)。 .


参见 OpenGL 4.6 API Core Profile Specification; 17.4.1 Selecting Buffers for Writing; page 513 :

17.4.1 Selecting Buffers for Writing

[...] The set of buffers of a framebuffer object to which all fragment colors are written is controlled with the commands

void DrawBuffers( sizei n, const enum *bufs );
void NamedFramebufferDrawBuffers( uint framebuffer, sizei n, const enum *bufs );

[...] For framebuffer objects, in the initial state the draw buffer for fragment color zero is COLOR_ATTACHMENT0. For both the default framebuffer and framebuffer objects, the initial state of draw buffers for fragment colors other then zero is NONE.

关于c++ - opengl中如何用鼠标点击获取帧缓冲区中绘制图形的像素信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54006634/

相关文章:

c++ - 为什么 clang++ 链接到 gcc?

c++ - 不为 emplace() 定义构造函数的解决方法

c++ - Lua 在 C++ 中的奇怪行为我无法理解?

python - 如何在 Python/PyQt 中模拟真实键盘的按键?

android - Qt 5.1 Android - 如何访问 NFC?

c++ - opengl图形的QT小图

c++ - glDrawElements 错误 : nvoglv32. dll OpenGL

c++ - 从嵌套抽象父类调用构造函数的问题

c++ - 在 Qt 中分配对象时处理异常

java - 找不到指定的文件?