1

I am creating a color picker OpenGL application for images with ImGUI. I have managed to load an image by loading the image into a glTexImage2D and using ImGUI::Image(). Now I would like to implement a method, which can determine the color of the pixel in case of a left mouse click.

Here is the method I loading the texture, then assigning it to a framebuffer:

bool LoadTextureFromFile(const char *filename, GLuint *out_texture, int *out_width, int *out_height,ImVec2 mousePosition ) {

    // Reading the image into a GL_TEXTURE_2D
    int image_width = 0;
    int image_height = 0;
    unsigned char *image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
    if (image_data == NULL)
        return false;

    GLuint image_texture;
    glGenTextures(1, &image_texture);
    glBindTexture(GL_TEXTURE_2D, image_texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    stbi_image_free(image_data);
    glBindTexture(GL_TEXTURE_2D, 0);

    *out_texture = image_texture;
    *out_width = image_width;
    *out_height = image_height;

    // Assigning texture to Frame Buffer
    unsigned int fbo;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, image_texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, image_texture, 0);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE){
        std::cout<< "Frame buffer is done."<< std::endl;
    }
    return true;
}

Unfortunately, the above code results in a completely blank screen. I guess, there is something I missed during setting the framebuffer.

Here is the method, where I would like to sample the framebuffer texture by using the mouse coordinates:

    void readPixelFromImage(ImVec2 mousePosition) {
        unsigned char pixels[4];
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        glReadPixels(GLint(mousePosition.x), GLint(mousePosition.y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
        std::cout << "r: " << static_cast<int>(pixels[0]) << '\n';
        std::cout << "g: " << static_cast<int>(pixels[1]) << '\n';
        std::cout << "b: " << static_cast<int>(pixels[2]) << '\n';
        std::cout << "a: " << static_cast<int>(pixels[3]) << '\n' << std::endl;
    }

Any help is appreciated!

Fox1942
  • 276
  • 2
  • 18
  • beware mouse coordinates refer to window coordinates which are different from OpenGL coordinates... you most likely should invert the `y` position if `ys` is `y` size of your view then use `ys-mousePosition.y` ... In case you got panels and stuff in the window you need to account for that too... You can also use pick textels directly from texture... Also its important to pick the glReadPixels while the image s rendered and from the same thread,... as you are most likely picking in some event its possible your image is blank at the time. – Spektre Dec 21 '20 at 09:17
  • see [OpenGL 3D-raypicking with high poly meshes](https://stackoverflow.com/a/51764105/2521214) there is example of `glReadPixel` the direct framebufer (no attachments of PBO/FBO) to pick the texture directly you can use `glGetTexImage` – Spektre Dec 21 '20 at 09:22

1 Answers1

1

There is indeed something missing in your code:

You set up a new Framebuffer that contains just a single texture buffer. This is okay, so the glCheckFramebufferStatus equals GL_FRAMEBUFFER_COMPLETE. But there is no render buffer attached to your framebuffer. If you want your image rendered on screen, you should use the default framebuffer. This framebuffer is created from your GL context. However, the documentation says: Default framebuffers cannot change their buffer attachments, [...] https://www.khronos.org/opengl/wiki/Framebuffer. So attaching a texture or renderbuffer to the default FB is certainly not possible. You could, however, generate a new FB as you did, render to it, and finally render the outcome (or blit the buffers) to your default FB. Maybe a good starting point for this technique is https://learnopengl.com/Advanced-Lighting/Deferred-Shading

Moreover, if you intend to just read back rendered values from your GPU, it is more performant to use a renderbuffer instead of a texture. You can even have multiple renderbuffers attached to your framebuffer (as in deferred shading). Example: you could use a second renderbuffer to render an object/instance id (so, the renderbuffer will be single channel integer), and your first renderbuffer will be used for normal drawing. Reading the second renderbuffer with glReadPixels you can directly read which instance was drawn at e.g. the mouse position. This way, you can enable mouse picking very efficiently.

F K
  • 315
  • 2
  • 9