Dual depth peeling is not anything bleeding-edge for Order independent transparency. But since it could provide accurate blending results, though geometry intensive, is still valuable for non-game 3d scenarios like GIS, BIM, CAD etc. I recently implemented dual depth peeling in WebGL for my fulltime work and I’m kind of confused at why so few open-source implementation could be found on such a not new topic ( I didn’t find the source code for the original paper either ). As a result I came up with this article and a raw WebGL demo. It would be helpful if you also want to implement this oit method. Plus you will learn some workaround you have to make if you are also targeting at WebGL or OpenGL ES.
- Original dual depth peeling paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.193.3485&rep=rep1&type=pdf
- Raw WebGL example code: https://github.com/tsherif/webgl2examples/blob/master/oit-dual-depth-peeling.html
- Live demo: https://tsherif.github.io/webgl2examples/oit-dual-depth-peeling.html
Dual depth peeling is a slightly improved version of single depth peeling paper.
The idea is straight forward as shown by the name: we draw the objects multiple times. For each pass, we peel one layer from front and one layer from back (front or back is purely depending on their depth, not if it’s from front face or back face).
We manually maintain a depth buffer for front and back depth value so for each pass we know how deep we are for peeling. We discard all fragments outside current front and back depth range (since they are already peeled and dumped to color buffer). We draw all fragments within this range (not equal to bound, exclusive) to depth buffer and leave the nearest next one so we know which fragment to draw for next pass. And we will draw fragments has the same depth value as current front or back depth to front and back color buffer.
The color buffer of front and back fragments should be separated cuz blending function is dependent on the blending order.
Things worth notification
- Ping-pong for platform not supporting separate blending function for each color attach of a frame buffer object
The depth, front, and back buffer each needs a different blending function.
As mentioned in the paper, DX11+ and OpenGL4+ has such features. Unfortunately I’m targeting at WebGL. As a workaround I need two set for each buffer to ping-pong since read from and write to the same render target can lead to undefined behavior.
We then use MAX blending for this FBO. We use a RG32F texture to store depth as vec2(-frontDepth, backDepth) so that the furthest front fragment and nearest back fragment can be preserved after blending. For front color buffer, since it’s always increasing, we do the blending in shader and the MAX blending is enough. For back color buffer, we have to dump the color of it to a back blender buffer after finishing every pass.
- WebGL draw buffer COLOR_ATTACHMENT listing limitation
- Blend with other opaque objects
In order to do this we need to have an extra regular depth attachment to our frame buffer object, with opaque object depth already drawn to it. So when doing depth peeling we can enable the hardware depth test and set the depth mask to false to blend and occlude with opaque objects correctly. Some pseudo code like this:
this.depthTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.depthTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
// ...
gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depthTexture, 0);
// ...
- Front to back blending function confuse
The front-to-back order blending function given in the paper is:
frontColor.a = lastFrontColor.a * (1.f - color.a);
But if assigning some value to alpha I don’t find it match the result with regular back-to-front blending function (dst.a = src.a +(1-src.a)*dst.a). So I use the following function for front-to-back blending.
float alphaMultiplier = 1.f - lastFrontColor.a;
frontColor.a = 1.f - alphaMultiplier * (1.f - color.a);
I’m a bit confused at this part. Let me know in the comment if I made a mistake here.
Vehicle model credit: https://www.turbosquid.com/3d-models/free-duesen-bayern-mystar-190-3d-model/1062796