【1】问题:Opengl绘制半透明物体时,会出现显示问题
其他人的博客也有介绍:
1.opengl渲染透明的三角面片的问题
可以看出上图的斯坦福兔子渲染出来有很严重的阴影斑点问题。
【2】 传统解决办法
1. 油画家算法
将场景中的三角面片根据深度进行排序,然后按照顺序进行描绘,一般是先绘制远处的场景,再绘制近处的场景。
缺点:但如果两个多边形相交,就没法对他们进行排序了。甚至我们都不需要两个不同的物体来复现这个问题。组成玻璃杯的那些三角形会怎样?要让它们正确显示,需要在前面的绘制之前先绘制后面的三角面片,所以需要对每个三角形进行排序。
2. 母体切分
将母体切分成几大块,每块中的面片没有相互遮挡,相当于对所有面片粗排序了。
缺点:对于复杂模型,不好分块,操作困难。
3.加权平均值算法(Weighted Average)
使用简单的透明混合公式来实现无序透明渲染的算法,它通过扩展透明混合公式,来实现无序透明物件的渲染,从而得到一定程度上逼真的结果。【1】
NVIDIA公司的Louis Bavoil在此基础上提出了新的算法,使用物体的不透明度作为加权值的加权平均值算法。此算法的主要思想如下:
对于某个位置的像素点,如果所有的不透明物件是相同的颜色,那么渲染的结果与它们的渲染顺序无关。那么,对于不相同颜色值的情况,如果我们用某一个颜色来替换这些颜色,比如这些颜色的平均值。对于这种情况,我们使用各个颜色的不透明度作为权重来计算出它们的平均值。
此算法的优点很明显,效率高,速度快,只需要对物体进行一次的渲染,然后加上一次全屏的后处理。
缺点:透明结果只是一个近似值,而不是确切的正确结果。然而,它的效果仍然远远好过不做任何处理的简单透明混合,而且高效性也使得它有一定的应用空间,如游戏开发。
4.、深度剥离(Depth Peeling)
深度剥离是一种对深度值进行排序的技术。它的原理比较直观,标准的深度检测使场景中的Z值最小的点输出到屏幕上,就是离我们最近的顶点。但还有离我们第二近的顶点,第三近的顶点存在。要想显示它们,可以用多遍渲染的方法。第一遍渲染时,按照正常方式处理,这样就得到了离我们最近的表面中的每个顶点的z值。在第二遍渲染时,把现在每个顶点的深度值和刚才的那个深度值进行比较,凡是小于等于第一遍得到的Z值,把它们剥离,后面的过程依次类推即可。
引用映射的原理很简单,如果光源和目标点之间的连线没有任何物体阻挡的话,则目标点没有在阴影中;如果有物体遮挡,则目标点处于阴影中。而ShadowMap,就是一张记录了每个像素处用于比较遮挡关系信息的纹理。
深度剥离算法就是利用了它的这一特性模拟深度测试,从而实现对颜色层的剥离操作。具体算法如下:
1、首先用深度缓冲的深度测试功能渲染透明物件。保护得到的颜色值,这就是最前的一层,同时将深度值拷贝到有阴影映射功能的深度纹理中。
2、打开深度纹理的比较功能,使得深度值比较大的颜色通过测试,这时加上深度缓存本身的最小深度值功能,我们就能得到最前一层的下一层的颜色值和对应的深度值。
重复2的操作直到所有的颜色都成功的剥离出来,然后再将它们按照从后往前的顺序进行渲染,这样就可以得到正确的结果了。这种效果在OpenGL里实现起来比较简单,可以使用ARB扩展功能中的API函数。
缺点:该算法能保证得到正确的结果,但是其效率却是限制其使用的瓶颈。从上面的描述可知,它需要对透明物体进行N遍的渲染,这里的N是透明物件的深度复杂度。
【3】本文方法
原来的模型如上图所示。
设定三角面片,顶点逆时针方向对应得面为外表面。
首先开启背面剔除功能。
先渲染里面,将三角面片顶点排序由(0,1,2),改为顶点排序(0,2,1)。此时,因为开启了背面剔除功能,我们只能看到模型远离我们的那部分的内部。如下图所示的黄色部分。
再渲染外面,三角面片得顶点排序调整为原来的(0,1,2),因为剔除了背景,所以这个阶段我们只能看到紫色部分,如下图:
两个阶段叠加起来,设置为透明模型,效果如下图:
效果图:
代码:
void CShowShader::ShaderTransparence(TriMesh*Tmesh)
{
if (Tmesh == NULL) return;
Tmesh-