顺序无关半透明:Depth Peeling

 
 
  由于光栅化渲染 API 只能保存一个像素的深度(通常为最近像素的深度),所以光栅化渲染天生具有半透明渲染的劣势。通过到相机的距离对图元进行排序可能有所帮助,但排序是计算密集型的,必须在每一帧完成,而且如果图元相互交错,就会出现无法修复的错误。

Depth peeling

  Depth peeling 的中文意思是深度剥离,本质也类似。先渲染最远的一层深度的像素,然后将它剥离掉,然后再重新渲染半透明物体。通过反复渲染场景,在每次传递时,算法使用深度缓冲区仅保留来自下一个最近层的贡献。然后,算法将贡献混合到已经渲染的结果上。
  算法的核心非常简单。事实上,一次半透明物体的渲染可以同时剥离掉两层深度,这种方法需要保存两个深度缓存,被称为 dual depth peeling。

从前往后 blend

  渲染半透明物体,需要用到 alpha blend。以下是 alpha blend 的常见迭代过程:

C_{dst}’=\alpha_{src}C_{src}+(1-\alpha_{src}C_{dst})

  由于 C_{dst}’ 依赖于 C_{dst},就需要半透明物体按照顺序来渲染。而对图元进行排序,存在着交叉的问题。所以 depth peeling 方法通过将图元打散为一个个像素,将像素一层一层从远到近渲染,然后进行混合。
  但是,上述公式的 blend 的顺序是从后往前的顺序渲染。由于 depth test 的存在,实际上从前往后渲染更符合当前 API 的设定。所以要想办法转换顺序,从前往后进行 blend。
  假设有三个像素,从后到前依次为 (C_0,1)(C_1,A_1)(C_2,A_2)。根据从后往前 blend,有

\begin{aligned}
C_1’&=A_1C_1+(1-A_1)C_0\\
C_2’&=A_2C_2+(1-A_2)C_1’\\
\end{aligned}

  其中,C_1′C_2′ 表示第一次和第二次的 blend 结果,将 C_1′ 代入C_2′

\begin{aligned}
C_2’&=A_2C_2+(1-A_2)(A_1C_1+(1-A_1)C_0)\\
&=A_2C_2+(1-A_2)A_1C_1+(1-A_2)(1-A_1)C_0
\end{aligned}

  从上述推导就可以得到迭代公式:

\begin{aligned}
C_{dst}&=A_{dst}(A_{src}C_{src})+C_{dst}\\
A_{dst}&=(1-A_{src})A_{dst}
\end{aligned}

  其中,A_{dst} 初始化为 1,而 A_{src}C_{src} 项可以在像素着色器阶段就完成计算,所以 OpenGL 的 blend 状态可以设置为:

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncDeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA)

  最后,如果有默认背景色的话,需要另一个着色器实现 C_{dst}=A_{dst}C_{bg}+C_{dst}
  

结果

  对于不透明的 dragon 模型,有

  

  渲染这一帧的半透明总共重复了 17 个 tile,前四个如下所示:

  

  最后合并的结果为:

Tags :

About the Author

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注