Rendering a reflective object using dynamic cube mapping

Now we will see how to use dynamic cube mapping to render a real-time scene to a cubemap render target. This allows us to create reflective surfaces. In modern OpenGL, offscreen rendering (also called render-to-texture) functionality is exposed through FBOs.

Getting ready

In this recipe, we will render a box with encircling particles. The code is contained in the Chapter3/DynamicCubemap directory.

How to do it…

Let us get started with the recipe as follows:

  1. Create a cubemap texture object.
    glGenTextures(1, &dynamicCubeMapID);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_CUBE_MAP, dynamicCubeMapID);
    glTexParameterf(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    for (int face = 0; face < 6; face++) {
      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA,CUBEMAP_SIZE, CUBEMAP_SIZE, 0, GL_RGBA, GL_FLOAT, NULL);
    }
  2. Set up an FBO with the cubemap texture as an attachment.
    glGenFramebuffers(1, &fboID);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); 
    glGenRenderbuffers(1, &rboID);
    glBindRenderbuffer(GL_RENDERBUFFER, rboID);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, CUBEMAP_SIZE, CUBEMAP_SIZE);
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboID);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, dynamicCubeMapID, 0);
    GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE) {
      cerr<<"Frame buffer object setup error."<<endl;
      exit(EXIT_FAILURE);
    } else {
      cerr<<"FBO setup successfully."<<endl;
    }
  3. Set the viewport to the size of the offscreen texture and render the scene six times without the reflective object to the six sides of the cubemap using FBO.
    glViewport(0,0,CUBEMAP_SIZE,CUBEMAP_SIZE);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_CUBE_MAP_POSITIVE_X, dynamicCubeMapID, 0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glm::mat4   MV1 = glm::lookAt(glm::vec3(0),glm::vec3(1,0,0),glm::vec3(0,-1,0));  
    DrawScene( MV1*T, Pcubemap);
    
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, dynamicCubeMapID, 0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glm::mat4 MV2 = glm::lookAt(glm::vec3(0),glm::vec3(-1,0,0), glm::vec3(0,-1,0)); 
    DrawScene( MV2*T, Pcubemap);
    
    ...//similar for rest of the faces
       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  4. Restore the viewport and the modelview matrix, and render the scene normally.
       glViewport(0,0,WIDTH,HEIGHT);
       DrawScene(MV, P);
  5. Set the cubemap shader and then render the reflective object.
    glBindVertexArray(sphereVAOID);
    cubemapShader.Use();
    T = glm::translate(glm::mat4(1), p);
    glUniformMatrix4fv(cubemapShader("MVP"), 1, GL_FALSE, glm::value_ptr(P*(MV*T)));
    glUniform3fv(cubemapShader("eyePosition"), 1, glm::value_ptr(eyePos));
    glDrawElements(GL_TRIANGLES,indices.size(),GL_UNSIGNED_SHORT,0);
    cubemapShader.UnUse();

How it works…

Dynamic cube mapping renders the scene six times from the reflective object using six cameras at the reflective object's position. For rendering to the cubemap texture, an FBO is used with a cubemap texture attachment. The cubemap texture's GL_TEXTURE_CUBE_MAP_POSITIVE_X target is bound to the GL_COLOR_ATTACHMENT0 color attachment of the FBO. The last parameter of glTexImage2D is NULL since this call just allocates the memory for offscreen rendering and the real data will be populated when the FBO is set as the render target.

The scene is then rendered to the cubemap texture without the reflective object by placing six cameras at the reflective object's position in the six directions. The cubemap projection matrix (Pcubemap) is given a 90 degree fov.

Pcubemap = glm::perspective(90.0f,1.0f,0.1f,1000.0f);

This renders the scene into the cubemap texture. For each side, a new MVP matrix is obtained by multiplying the new MV matrix (obtained by using glm::lookAt function). This is repeated for all six sides of the cube. Next, the scene is rendered normally and the reflective object is finally rendered using the generated cubemap to render the reflective environment. Rendering each frame six times into an offscreen target hinders performance, especially if there are complex objects in the world. Therefore this technique should be used with caution.

The cubemap vertex shader outputs the object space vertex positions and normals.

#version 330 core
layout(location=0) in vec3 vVertex;
layout(location=1) in vec3 vNormal;
uniform mat4 MVP;
smooth out vec3 position;
smooth out vec3 normal;
void main() { 
position = vVertex;
normal = vNormal;
   gl_Position = MVP*vec4(vVertex,1);
}

The cubemap fragment shader uses the object space vertex positions to determine the view vector. The reflection vector is then obtained by reflecting the view vector at the object space normal.

#version 330 core
layout(location=0) out vec4 vFragColor;
uniform samplerCube cubeMap;
smooth in vec3 position;
smooth in vec3 normal;
uniform vec3 eyePosition;
void main() { 
  vec3 N = normalize(normal);
  vec3 V = normalize(position-eyePosition);
  vFragColor = texture(cubeMap, reflect(V,N));
}

There's more…

The demo application implementing this recipe renders a reflective sphere with eight cubes pulsating around it, as shown in the following figure:

There's more…

In this recipe, we could also use layered rendering by using the geometry shader to output to a different Framebuffer object layer. This can be achieved by outputting to the appropriate gl_Layer attribute from the geometry shader and setting the appropriate viewing transformation. This is left as an exercise for the reader.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.207.129