I have a simple scene which renders a grid, a plane and a cube. I also have two different shaders. One is a flat shader which renders objects in a random color, the other is a noise shader which renders objects with a noise effect. I want to be able to:
- Render the plane and the cube via the noise shader and the grid with the flat shader.
- Once the user clicks the scene, I want to render the entire scene to a (offscreen) renderbuffer using flat shader only and print the clicked color acquired via
glReadPixels
. - Continue rendering the scene using both shaders.
Basically I want to implement color picking. It does not seem to work for the cube and the plane though. The pixel values are always greyscale values from the noise shader.
This is what my drawing routine looks like:
void Draw(const bool offscreen = false)
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 projection = glm::perspective(glm::radians(camera.zoom), (float)viewport_width / (float)viewport_height, 0.1f, 100.0f);
glm::mat4 view = camera.get_view_matrix();
if (offscreen)
{
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
box.Draw_offscreen(&flat_shader);
plane.Draw_offscreen(&flat_shader);
}
else
{
noise_shader.use();
noise_shader.set_mat4("projection", projection);
noise_shader.set_mat4("view", view);
noise_shader.set_float("iTime", delta_time);
plane.Draw(&noise_shader);
box.Draw(&noise_shader);
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
}
glfwSwapBuffers(window);
}
So if offscreen is false
the scene looks like this (normal rendering):
And this is what the scene looks like when offscreen
is true
:
This is how I create the fbo:
void init_offscreen_buffer()
{
glGenFramebuffers(1, &fbo_off);
glGenRenderbuffers(1, &render_buf);
glBindRenderbuffer(GL_RENDERBUFFER, render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, viewport_width, viewport_height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);
// I also checked for FRAMEBUFFER_COMPLETE
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
Now when the user clicks the scene I run pick_color_id
which prints the color on the pixel clicked.
void pick_color_id(double x, double y)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
cout << "---------------------------------------------------------" << endl;
cout << "Mouse click position: " << x << "; " << y << endl;
cout << "Target pixel color: " << (unsigned int)pixel_color[0] << ";" << (unsigned int)pixel_color[1] << ";" << (unsigned int)pixel_color[2] << endl;
cout << "---------------------------------------------------------" << endl;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
As I see it after I bind the buffer, it should be the render target and it should contain the flat colors for the plane and the cube. Actually it always prints greyscale colors like in the noise shader.
I think that there is something wrong with my fbo setup or usage (maybe both). What am I missing?
GL_READ_FRAMEBUFFER
notGL_DRAW_FRAMEBUFFER
forglReadPixels
. – Rabbid76