OIT with weighted average

Started by
1 comment, last by EarthBanana 10 years, 12 months ago

I have decided to switch my engine to OIT for a couple reasons - mainly because of my map styles. I just don't really like sorting geometry much - and the results are never pixel perfect.. anyways after looking at depth peeling, dual depth peeling, a-buffer, weighted average, and weighted sum techniques I decided on weighted average.

I'm a bit confused on the logistics of implementing it - in the white paper (Bavoil and Myers 2008) it states

"The average RGBA color for each pixel is generated by rendering the transparent geometry
into an accumulation buffer implemented as a 16-bit floating-point texture. The result is an

accumulated RGBA color and the number of fragments per pixel (depth complexity) "

So - I'm already using a deferred shading engine - generating another texture is not a big deal - what I don't quite understand is how to write the average RGBA color and number of fragments per pixel to a 16 bit floating point texture.

Here's where I'm a bit confused:

This may be silly - but from my understanding.. for each fragment I would add the current RGBA color to the 16 bit texture -

My first question is how do I do this? Do I just set glBlendEquation to GL_FUNC_ADD and glBlendFunc(GL_ONE, GL_ONE)?

My second question is how do I save the depth complexity to the 16 bit texture also? The only way I can think to do it is making another texture and adding 1 to every texel that I pass over in the fragment shader (with the blend mode set as above)

Any insight to how to do this would be appreciated - thanks

Advertisement

Hi,

Your suggestion is already the solution. So, yeah, you got it right. I'll summarize the steps briefly for you:


Your transparent rendering would happen in the rendering loop after the deferred pass:

  1. Fill your deferred buffer (color, depth, …)
  2. Do the deferred lighting
  3. Render the transparent objects

For the last step, do the following:

  1. Disable depth writing (glDepthMask) -> we want no transparent object to be culled by other transparent objects
  2. Enable depth test (glDepthTest) -> compare with your deferred depth buffer (cull by opaque geometry)
  3. Use additive blending (source: GL_ONE, dest: GL_ONE)
  4. Bind two new render targets; let’s call them AccumColors (four (half) float components) and AccumCount (single float component)
  5. Render the transparent objects into those two targets (explained in a moment)
  6. Bind deferred color buffer as render target
  7. Disable depth test (glDepthTest)
  8. Use back-to-front blending (source: GL_ONE_MINUS_SRC_ALPHA, dest: GL_SRC_ALPHA)
  9. Bind AccumColor and AccumCount as textures
  10. Full screen pass: compute the average color/transparency (explained in a moment) and blend with the deferred color buffer

The idea is to compute the average of the colors, weighted by their transparencies. For blending with the background, we additionally need the average opacity. (FYI: I assume that alpha = 1 means opaque, and alpha = 0 means transparent)

Step 5 computes the sums. This is what the fragment shader does:


Input: vec3 color, float alpha
Output: 
  AccumColor = vec4(color*alpha, alpha);  // color multiplied (=weighted) with alpha!
  AccumCount = 1;

Step 10 compute the average color. Again, the fragment shader:


Input: vec4 AccumColor, float AccumCount

if (AccumCount < 0.00001 || AccumColor.w < 0.00001)
{
  discard;  // nothing here; discard the fragment.
}
else
{
  vec4 avgColor = vec4(
  AccumColor.xyz / AccumColor.w,    // weighted average color
  AccumColor.w / AccumCount);        // average alpha
  // the alpha, used to blend with the background is computed by assuming 
  // that all transparent layers have the average alpha:
  float dstAlpha = 1-pow(max(0,1-avgColor.w), AccumCount);
  // write out the average color and the alpha, used for compositing
  result = vec4 (avgColor.xyz, dstAlpha);
}

Hope that gives some insights. smile.png

Best regards!


Wow - that is literally exactly what I was looking for. Thanks for taking the time to write that out - this should be fairly simple to implement. I will post a picture when I get it working.. should be tomorrow.

Thanks again! Great explanation

This topic is closed to new replies.

Advertisement