OpenGL深度剥离算法(Depth Peeling)半透明实现

时间:2021-02-09 12:55:46

  半透明渲染新技术:http://www.cnblogs.com/lancidie/archive/2011/08/18/2144797.html

  OpenGL渲染透明的三角面片的问题:http://bbs.csdn.net/topics/390785998

  Alpha混合物体的深度排序:http://blog.csdn.net/xoyojank/article/details/3918091


两种比较低层的算法:

      1、深度缓存

      2、油画家算法:首先将场景中的多边形根据深度进行排序,然后按照顺序进行描绘,一般是先绘制远处的场景,再绘制近处的场景。但如果两个多边形相交,就没法对他们进行排序了。甚至我们都不需要两个不同的物体来复现这个问题。组成玻璃杯的那些三角形会怎样?要让它们正确显示,需要在前面的绘制之前先绘制后面的三角面片,所以需要对每个三角形进行排序。

    问题是,对每个三角形进行排序的代价太大!就算我们能接受,这也不是在所有场合下都能得到正确结果的。比如说两个透明的三角形相交时的情况。  


问题原因:比如一个球体什么的,这种物体是不能用OpenGL传统的透明排序和融合的方法处理的,必须用到depth peeling等一些高级的技术,osg有这样的例子。但是如果您不想自惹麻烦的话,最好还是将这个对象切开为多个node并且分别设置TRANSPARENT_BIN,避免自遮挡的情况。
    透明物体要按深度排序,因此是不能存在自交叉的。这是图形学里经典的做法了,如果想要OIT(Order Independent Transparency)的方法,那么自己去实现depth peeling,否则还是老老实实地把您的圆柱拆开成至少4个半圆柱,以避免自交叉。

喔,这已经涉及到一些高级的图形学内容了,两个方案:
1、经典的透明排序方法,但是要把母体切开,而且保证任何角度都不会有自交叠
2、基于shader的depth peeling方法,增加渲染次数但是可以自己分层分别透明


   单一的深度缓存不能解决透明物体的渲染问题,因为一个透明物体不仅要被在它之前的不透明物体所遮挡,又不能挡着它之后的其他物件。对于透明物件的渲染,OpenGL提供了Alpha--Blending技术来实现。然而,对于复杂的不透明与透明物体共存的需求,单一的深度缓存和Alpha-Blending并不能解决问题。因为透明物体的特性要求必须按照正确顺序的绘制来对它进行渲染,都在无法得到正确的结果。

高级别的透明算法:

     1、加权平均值算法(Weighted Average)

      使用简单的透明混合公式来实现无序透明渲染的算法,它通过扩展透明混合公式,来实现无序透明物件的渲染,从而得到一定程度上逼真的结果。

NVIDIA公司的Louis Bavoil在此基础上提出了新的算法,使用物体的不透明度作为加权值的加权平均值算法。此算法的主要思想如下:

      对于某个位置的像素点,如果所有的不透明物件是相同的颜色,那么渲染的结果与它们的渲染顺序无关。那么,对于不相同颜色值的情况,如果我们用某一个颜色来替换这些颜色,比如这些颜色的平均值。对于这种情况,我们使用各个颜色的不透明度作为权重来计算出它们的平均值。

此算法的优点很明显,效率高,速度快,只需要对物体进行一次的渲染,然后加上一次全屏的后处理。但是缺点也是同样的明显,透明结果只是一个近似值,而不是确切的正确结果。然而,它的效果仍然远远好过不做任何处理的简单透明混合,而且高效性也使得它有一定的应用空间,如游戏开发。


     2、深度剥离(Depth Peeling)

      深度剥离是一种对深度值进行排序的技术。它的原理比较直观,标准的深度检测使场景中的Z值最小的点输出到屏幕上,就是离我们最近的顶点。但还有离我们第二近的顶点,第三近的顶点存在。要想显示它们,可以用多遍渲染的方法。第一遍渲染时,按照正常方式处理,这样就得到了离我们最近的表面中的每个顶点的z值。在第二遍渲染时,把现在每个顶点的深度值和刚才的那个深度值进行比较,凡是小于等于第一遍得到的Z值,把它们剥离,后面的过程依次类推即可。

      引用映射的原理很简单,如果光源和目标点之间的连线没有任何物体阻挡的话,则目标点没有在阴影中;如果有物体遮挡,则目标点处于阴影中。而ShadowMap,就是一张记录了每个像素处用于比较遮挡关系信息的纹理。

     深度剥离算法就是利用了它的这一特性模拟深度测试,从而实现对颜色层的剥离操作。具体算法如下:

     1、首先用深度缓冲的深度测试功能渲染透明物件。保护得到的颜色值,这就是最前的一层,同时将深度值拷贝到有阴影映射功能的深度纹理中。

     2、打开深度纹理的比较功能,使得深度值比较大的颜色通过测试,这时加上深度缓存本身的最小深度值功能,我们就能得到最前一层的下一层的颜色值和对应的深度值

       重复2的操作直到所有的颜色都成功的剥离出来,然后再将它们按照从后往前的顺序进行渲染,这样就可以得到正确的结果了。

       该算法能保证得到正确的结果,但是其效率却是限制其使用的瓶颈。从上面的描述可知,它需要对透明物体进行N遍的渲染,这里的N是透明物件的深度复杂度。

      这种效果在OpenGL里实现起来比较简单,可以使用ARB扩展功能中的API函数。