Dual Depth Peeling 在WebGL中的实现例程

Dual Depth Peeling 在WebGL中的实现例程

Dual Depth Peeling 应该不是什么实现Order Independent Transparency的前沿科技了。但是鉴于它在众多OIT算法里是不近似而是可以得到精确blending结果的一个,我感觉在非游戏的3d应用场景(比如GIS,BIM,CAD等)中应该还是很有价值。最近我在全职工作中在webgl中实现了Dual Depthing Peeling,但是不知道为啥相关的开源代码(完整能跑或是至少有个图的)不怎么好找。(原文章中的例程我也没找到)按理说这个比较老的算法应该挺容易找到可以抄的吧。所以我就写一了一个小例程,和这篇文章,希望对想在WebGL或OpenGL ES实现的同学有帮助。


Dual Depth Peeling 原论文

Raw WebGL 例程代码

dual depth peeling 不同数量的draw pass (层数=pass数乘2)


Dual Depth Peeling 就是个 Single Depth Peeling 的小改进版本。顾名思义,它就是通过多次绘制,每次绘制离相机最靠近的和最远的各一层,像剥洋葱一样(一层一层一层一层地拨开你的XX)。

我们需要创建一个depth buffer来保存上一次剥到的最近(front)和最远(back)的depth值。在这个范围之外的fragment直接扔掉因为已经剥过画过了。在这个范围里面的(不包括边界)扔到color buffer里并取离当前front和back最近的各一层写到depth buffer里准备下次绘制。而刚好等于当前front和back depth值得两层就是当前pass要绘制的。

同时需要两个color buffer分别保存目前为止从前面剥和从后面剥的fragment blending的颜色。之所以需要两个是因为从前和从后的blending function是不同的。


值得注意的地方

  • 2 buffers Ping-pong

depth, font color, back color 三个 render target的blending equation和function是不同的。原文已经提到对于DX11+,OpenGL4+能够提供FBO下不同Render Target各自不同的blending equation。但是不幸我正在写WebGL。对于不能支持单独blending equation的平台,我们就需要准备两套buffer来ping pong。因为一个render target不能同时读和写。

然后我们对这一整个FBO使用MAX blending。depth使用RG32F存储vec2(-nearDepth, farDepth). 这样在MAX blending下离得最远的front和最近的back会被最终保存。front color在shader里手动blend,并且因为front color的值是单调递增,MAX blending也没有问题。只有back color不适用于此。所以每个pass要单独把back color画到一个back blender的buffer里。

  • WebGL draw buffer COLOR_ATTACHMENT 组合顺序等的限制

参见这个stackoverflow回答,讲的很详细了:stackoverflow.com/quest

  • 场景中的其他不透明物体

这种情况下,我们需要在FBO上再加一个depth attachment,用来提前绘制不透明物体的depth。在用dual depth peeling 绘制透明物体时启用硬件的depth test并将depth mask设为true,已达到被不透明物体遮挡以及与其blend的目的。这块代码例程里没写,在这里写个大概的样子

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 order blending的一个疑问

原文章中关于这个顺序的blending equation给的是

frontColor.a = lastFrontColor.a * (1.f - color.a);

但我随便带个alpha值进去算出来和传统的back-to-front的公示算出来不一致

dst.a = src.a +(1-src.a)*dst.a

所以我在例程中用的是这个实现

float alphaMultiplier = 1.f - lastFrontColor.a;
frontColor.a = 1.f - alphaMultiplier * (1.f - color.a);

但是并不确定,如果我这里犯错了希望各位高手在评论里指出来,多谢了。



汽车模型来源:turbosquid.com/3d-model

编辑于 2018-10-16

文章被以下专栏收录