Implementing order-independent transparency using dual depth peeling

In this recipe, we will implement dual depth peeling. The main idea behind this method is to peel two depth layers at the same time. This results in a much better performance with the same output, as dual depth peeling peels two layers at a time; one from the front and one from the back.

Getting ready

The code for this recipe is contained in the Chapter6/DualDepthPeeling folder.

How to do it…

The steps required to implement dual depth peeling are as follows:

  1. Create an FBO and attach six textures in all: two for storing the front buffer, two for storing the back buffer, and two for storing the depth buffer values.
    glGenFramebuffers(1, &dualDepthFBOID); 
    glGenTextures (2, texID);
    glGenTextures (2, backTexID);
    glGenTextures (2, depthTexID);
    for(int i=0;i<2;i++) {
    glBindTexture(GL_TEXTURE_RECTANGLE, depthTexID[i]);
    //set texture parameters
    glTexImage2D(GL_TEXTURE_RECTANGLE , 0, GL_FLOAT_RG32_NV, WIDTH, HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
    glBindTexture(GL_TEXTURE_RECTANGLE,texID[i]);
    //set texture parameters
    glTexImage2D(GL_TEXTURE_RECTANGLE , 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
    glBindTexture(GL_TEXTURE_RECTANGLE,backTexID[i]);
        //set texture parameters
    glTexImage2D(GL_TEXTURE_RECTANGLE , 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
    }
  2. Bind the six textures to the appropriate attachment points on the FBO.
    glBindFramebuffer(GL_FRAMEBUFFER, dualDepthFBOID);	
    for(int i=0;i<2;i++) {
      glFramebufferTexture2D(GL_FRAMEBUFFER, attachID[i], GL_TEXTURE_RECTANGLE, depthTexID[i], 0);
      glFramebufferTexture2D(GL_FRAMEBUFFER, attachID[i]+1, GL_TEXTURE_RECTANGLE, texID[i], 0);
      glFramebufferTexture2D(GL_FRAMEBUFFER, attachID[i]+2, GL_TEXTURE_RECTANGLE, backTexID[i], 0);
    }
  3. Create another FBO for color blending and attach a new texture to it. Also attach this texture to the first FBO and check the FBO completeness.
      glGenTextures(1, &colorBlenderTexID);
      glBindTexture(GL_TEXTURE_RECTANGLE, colorBlenderTexID);
      //set texture parameters
      glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, 0);
      glGenFramebuffers(1, &colorBlenderFBOID);
      glBindFramebuffer(GL_FRAMEBUFFER, colorBlenderFBOID);	
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, colorBlenderTexID, 0);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT6, GL_TEXTURE_RECTANGLE, colorBlenderTexID, 0);
      GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
      if(status == GL_FRAMEBUFFER_COMPLETE )
        printf("FBO setup successful !!! 
    ");
      else
        printf("Problem with FBO setup");
      glBindFramebuffer(GL_FRAMEBUFFER, 0);
  4. In the render function, first disable depth testing and enable blending and then bind the depth FBO. Initialize and clear DrawBuffer to write on the render target attached to GL_COLOR_ATTACHMENT1 and GL_COLOR_ATTACHMENT2.
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBindFramebuffer(GL_FRAMEBUFFER, dualDepthFBOID);
    glDrawBuffers(2, &drawBuffers[1]);
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
  5. Next, set GL_COLOR_ATTACHMENT0 as the draw buffer, enable min/max blending (glBlendEquation(GL_MAX)), and initialize the color attachment using fragment shader (see Chapter6/DualDepthPeeling/shaders/dual_init.frag). This completes the first step of dual depth peeling, that is, initialization of the buffers.
        glDrawBuffer(drawBuffers[0]);
        glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);	
        glClear(GL_COLOR_BUFFER_BIT);
        glBlendEquation(GL_MAX);
        DrawScene(MVP, initShader);
  6. Next, set GL_COLOR_ATTACHMENT6 as the draw buffer and clear it with background color. Then, run a loop that alternates two draw buffers and then uses min/max blending. Then draw the scene again.
    glDrawBuffer(drawBuffers[6]);
    glClearColor(bg.x, bg.y, bg.z, bg.w);
    glClear(GL_COLOR_BUFFER_BIT);
    int numLayers = (NUM_PASSES - 1) * 2;
    int currId = 0;
    for (int layer = 1; bUseOQ || layer < numLayers; layer++) {
      currId = layer % 2;
      int prevId = 1 - currId;
      int bufId = currId * 3;
      glDrawBuffers(2, &drawBuffers[bufId+1]);
      glClearColor(0, 0, 0, 0);
      glClear(GL_COLOR_BUFFER_BIT);
      glDrawBuffer(drawBuffers[bufId+0]);
      glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
      glClear(GL_COLOR_BUFFER_BIT);
      glDrawBuffers(3, &drawBuffers[bufId+0]);
      glBlendEquation(GL_MAX);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_RECTANGLE, depthTexID[prevId]);
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_RECTANGLE, texID[prevId]);
      DrawScene(MVP, dualPeelShader, true,true);
  7. Finally, enable additive blending (glBlendFunc(GL_FUNC_ADD)) and then draw a full screen quad with the blend shader. This peels away fragments from the front as well as the back layer of the rendered geometry and blends the result on the current draw buffer.
    glDrawBuffer(drawBuffers[6]);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    if (bUseOQ) {
       glBeginQuery(GL_SAMPLES_PASSED_ARB, queryId);
    }
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_RECTANGLE, backTexID[currId]);
    blendShader.Use();
      DrawFullScreenQuad();
    blendShader.UnUse();
       }
  8. In the final step, we unbind the FBO and enable rendering on the default back buffer (GL_BACK_LEFT). Next, we bind the outputs from the depth peeling and blending steps to their appropriate texture location. Finally, we use a final blending shader to combine the two peeled and blended fragments.
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glDrawBuffer(GL_BACK_LEFT);
    glBindTexture(GL_TEXTURE_RECTANGLE, colorBlenderTexID);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_RECTANGLE, depthTexID[currId]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_RECTANGLE, texID[currId]);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_RECTANGLE, colorBlenderTexID);
    finalShader.Use(); 
      DrawFullScreenQuad();
    finalShader.UnUse();

How it works…

Dual depth peeling works in a similar fashion as the front-to-back peeling. However, the difference is in the way it operates. It peels away depths from both the front and the back layer at the same time using min/max blending. First, we initialize the fragment depth values using the fragment shader (Chapter6/DualDepthPeeling/shaders/dual_init.frag) and min/max blending.

vFragColor.xy = vec2(-gl_FragCoord.z, gl_FragCoord.z);

This initializes the blending buffers. Next, a loop is run but instead of peeling depth layers front-to-back, we first peel back depths and then the front depths. This is carried out in the fragment shader (Chapter6/DualDepthPeeling/shaders/dual_peel.frag) along with max blending.

float fragDepth = gl_FragCoord.z;
vec2 depthBlender = texture(depthBlenderTex, gl_FragCoord.xy).xy;
vec4 forwardTemp = texture(frontBlenderTex, gl_FragCoord.xy);
//initialize variables …
if (fragDepth < nearestDepth || fragDepth > farthestDepth) {
   vFragColor0.xy = vec2(-MAX_DEPTH);
   return;
}
if(fragDepth > nearestDepth && fragDepth < farthestDepth) {
  vFragColor0.xy = vec2(-fragDepth, fragDepth);
  return;
}
vFragColor0.xy = vec2(-MAX_DEPTH);

if (fragDepth == nearestDepth) {
  vFragColor1.xyz += vColor.rgb * alpha * alphaMultiplier;
  vFragColor1.w = 1.0 - alphaMultiplier * (1.0 - alpha);
} else {
  vFragColor2 += vec4(vColor.rgb,alpha);
}

The blend shader (Chapter6/DualDepthPeeling/shaders/blend.frag) simply discards fragments whose alpha values are zero. This ensures that the occlusion query is not incremented, which would give a wrong number of samples than the actual fragment used in the depth blending.

vFragColor = texture(tempTexture, gl_FragCoord.xy);
if(vFragColor.a == 0)
  discard;

Finally, the last blend shader (Chapter6/DualDepthPeeling/shaders/final.frag) takes the blended fragments from the front and back blend textures and blends the results to get the final fragment color.

vec4 frontColor = texture(frontBlenderTex, gl_FragCoord.xy);
vec3 backColor = texture(backBlenderTex, gl_FragCoord.xy).rgb;
vFragColor.rgb = frontColor.rgb + backColor * frontColor.a;

There's more…

The demo application for this demo is similar to the one shown in the previous recipe. If dual depth peeling is enabled, we get the result as shown in the following figure:

There's more…

Pressing the Space bar enables/disables dual depth peeling. If dual peeling is disabled, the result is as follows:

There's more…

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.17.174.239