1

I attached 4 color buffers to a framebuffer and render in each of them. Each color buffer has the size of the window. I'm trying to read the color of the pixels of one of these color buffers using the coordinates of the mouse pointer.

mouse move event handler

void mouseMoveEvent(QMouseEvent *event)
{
    int x = event->pos().x();
    int y = event->pos().y();

    makeCurrent();
    glBindFramebuffer(GL_READ_FRAMEBUFFER, FBOIndex::GEOMETRY);
    {
        // I save the values I'm interested in in the attachment GL_COLOR_ATTACHMENT3
        // but I always get 0 from any other attachment I try
        glReadBuffer(GL_COLOR_ATTACHMENT3);

        QVector<GLubyte> pixel(3);
        glReadPixels(x, geometry().height() - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

        QString PixelColor = QColor(pixel[0], pixel[1], pixel[2]).name();    
        qDebug() << PixelColor; // => always 0
    }
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
    doneCurrent();
}

But for every color buffer I always read the value 0.

The color buffers are written correctly during the rendering phase, I tested each of them by displaying the texture to which they are attached. I also tested the pixel-reading selected by the mouse pointer to the default framebuffer and it works correctly.

Where am I wrong? Thanks!

EDIT

The seemingly strange thing is that, if I use a "dedicated" framebuffer, I can correctly read the values stored in the texture.

void mouseMoveEvent(QMouseEvent *event)
{
    int x = event->pos().x();
    int y = event->pos().y();

    GLuint fbo;

    makeCurrent();
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    {
        GLuint texture = textures[TextureIndex::COLOUR];
        glBindTexture(GL_TEXTURE_2D, texture);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

        QVector<GLubyte> pixel(3);
        glReadPixels(x, geometry().height() - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

        QString PixelColor = QColor(pixel[0], pixel[1], pixel[2]).name();
        qDebug() << PixelColor; // => correct value
    }
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
    glDeleteFramebuffers(1, &fbo);
    doneCurrent();
}

But clearly it seems useless to use another framebuffer when I already have one with exactly the information I need.

I also tried to read directly the values of the texture (as suggested by @Spektre), but also in this case I always get 0.

void mouseMoveEvent(QMouseEvent *event)
{
    int x = event->pos().x();
    int y = event->pos().y();

    makeCurrent();
    {
        GLuint texture = textures[TextureIndex::COLOUR];
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexSubImage2D(GL_TEXTURE_2D, 0, x, geometry().height() - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

        QString PixelColor = QColor(pixel[0], pixel[1], pixel[2]).name();
        qDebug() << PixelColor; // => always 0
    }
    doneCurrent();
}
Arctic Pi
  • 669
  • 5
  • 19
  • 1
    Your attachments are stored as textures in the FBO so just read the texture but beware this will not work on IntelHD ... Also you are using the glReadPixels wrongly see [depth buffer got by glReadPixels is always 1](https://stackoverflow.com/a/51130948/2521214) – Spektre Dec 31 '18 at 10:40
  • @Spektre thank you for the comment!! How can I read directly the texture in CPU space using the coordinates of the mouse cursor? I read the discussion you suggested, but I didn't get why I should care about the depth buffer in my case. Thank you so much for your help!! – Arctic Pi Dec 31 '18 at 14:54
  • 1. The link is not using FBOs at all. The `glReadPixels` is reading rendered buffer (in that link its depth but you can use any other you need) and you select which one as one of the parameters (your code is missing that !!! and that is why it is not working) 2. [FBO](https://stackoverflow.com/a/43930271/2521214) is rendering into texture instead so in that case you need to use `glGetTexImage` (as `glReadPixels` will not acquire data you want...) that however reads the whole texture for single point or sub image you need to use `glGetTexSubImage` ... – Spektre Dec 31 '18 at 17:01
  • I just have seen this: [opengl es 2.0 android c++ glGetTexImage alternative](https://stackoverflow.com/a/53993894/2521214) so it looks like you can use also `glReadPixels` like you do but you need to add some aditional calls see the code there for comparison/repair of yours ... – Spektre Jan 01 '19 at 10:09
  • Possible duplicate of [opengl es 2.0 android c++ glGetTexImage alternative](https://stackoverflow.com/questions/53993820/opengl-es-2-0-android-c-glgetteximage-alternative) – Spektre Jan 01 '19 at 10:09
  • @Spektre thanks again for your help! ;) The approach proposed in your link is exactly what I'm adopting at the moment (use another "dedicated" framebuffer, just to read the texture), but since I already have a framebuffer with the same values, it seems useless to create a new one. – Arctic Pi Jan 01 '19 at 16:51
  • From the [glReadPixels](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glReadPixels.xhtml) official reference : _glReadPixels read a block of pixels from the frame buffer_ where I suppose that "the frame buffer" is the one currently attached to GL_READ_FRAMEBUFFER – Arctic Pi Jan 01 '19 at 16:51
  • And from the [glReadBuffer](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glReadBuffer.xhtml) official reference : _glReadBuffer specifies a color buffer as the source for subsequent glReadPixels commands [...] the target framebuffer object is that bound to GL_READ_FRAMEBUFFER_ – Arctic Pi Jan 01 '19 at 16:53

1 Answers1

1

My approach is correct, but I was not binding to the correct framebuffer.

FBOIndex::GEOMETRY is an enum value that I use to index a FBOs array, where I store all the framebuffer object names, so in general it is not a correct framebuffer object name.

I have defined a method addFBO(index) that creates a framebuffer and stores it at the position index in the FBOs array. The method returns the framebuffer object name of the generated framebuffer. If a framebuffer already exists at the position index, then the method simply returns the associated framebuffer object name.

So, by changing the code in the following way, I finally get the desired result.

void mouseMoveEvent(QMouseEvent *event)
{
    int x = event->pos().x();
    int y = event->pos().y();

    makeCurrent();
    glBindFramebuffer(GL_READ_FRAMEBUFFER, addFBO(FBOIndex::GEOMETRY));
    {
        glReadBuffer(GL_COLOR_ATTACHMENT3);

        QVector<GLubyte> pixel(3);
        glReadPixels(x, geometry().height() - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));

        QString PixelColor = QColor(pixel[0], pixel[1], pixel[2]).name();    
        qDebug() << PixelColor; // => correct value
    }
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
    doneCurrent();
}
Arctic Pi
  • 669
  • 5
  • 19
  • Notwithstanding, the possibility to directly read the values from the texture with `glTexSubImage2D` without going through a framebuffer is still unresolved for me. I found this [article](http://www.songho.ca/opengl/gl_pbo.html), but I have not had time to try. – Arctic Pi Jan 01 '19 at 18:58