Tuesday, September 19, 2017

How to Process Mouse Events in a 3D Scene, Part 1

1. Create the new color buffer

Unfortunately, OpenGL's default framebuffer does not support adding arbitrary color buffers.  The only way to write to multiple color buffers in your fragment shader, is to create an FBO to use instead of the default framebuffer.  Then, you must blit from the FBO to the default framebuffer.

Here's a block of code to do this.  Because we are creating a brand new FBO to draw the scene to, and do not have access to the default framebuffer, we must also create color and depth/stencil buffers for drawing the scene normally.  Here, windowDimensions is a struct with x and y properties giving, well, the window dimensions.

/* handles for the FBO and renderbuffers */
GLuint framebuffer;
GLuint rboColor;

GLuint rboObjectID;
 
GLuint rboDepthStencil;

/* create and bind the FBO */
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

/* create a renderbuffer for drawing colors, and bind it to GL_COLOR_ATTACHMENT0 */
glGenRenderbuffers(1, &rboColor);
glBindRenderbuffer(GL_RENDERBUFFER, rboColor);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, windowDimensions.x, windowDimensions.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColor);

/* create a renderbuffer to hold the Object IDs, and bind it to GL_COLOR_ATTACHMENT1 */
glGenRenderbuffers(1, &rboObjectID);
glBindRenderbuffer(GL_RENDERBUFFER, rboObjectID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, windowDimensions.x, windowDimensions.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, rboObjectID);

/* create a depth/stencil buffer, and bind it to GL_DEPTH_STENCIL_ATTACHMENT */
glGenRenderbuffers(1, &rboDepthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, windowDimensions.x, windowDimensions.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil);



Due to the glBindFramebuffer call, all drawing will go to our new FBO instead of to the default framebuffer.

Again, since FBOs are typically used for offscreen rendering, after drawing the scene we must blit from our FBO to the default framebuffer in order for the user to see the scene.

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(

    0, 0, windowDimensions.x, windowDimensions.y,
    0, 0, windowDimensions.x, windowDimensions.y,
    GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);

glfwSwapBuffers(window); /* or whatever you use to swap buffers */


The first argument to the glBindFramebuffer can be GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_FRAMEBUFFER which sets both.  Here, we set the draw framebuffer to 0, i.e. the default framebuffer, leaving the read framebuffer set to our FBO.  After blitting, we set the draw framebuffer back to our FBO so that drawing happens in the correct spot.

One more detail.  When clearing our FBO, we must make sure to clear both color attachments.  Here we use the glClearBuffer* function, the second argument of which tells which attachment we wish to clear.

float clearColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
unsigned int clearObject[] = {0, 0, 0, 0};
glClearBufferfv(GL_COLOR, 0, clearColor);

glClearBufferuiv(GL_COLOR, 1, clearObject);
glClearBufferfi(GL_DEPTH_STENCIL, 0, 1, 0);


Intro
Part 2
Part 3

No comments:

Post a Comment