透明物体的绘制 - 混合与深度测试
当场景出现透明和不透明物体有前后遮挡关系时,我们要让透明物体透过自身能看到后面的不透明物体时,就需要混合两者的颜色,混合公式:
混合源颜色与目标颜色
Blend SrcFactor DstFactor
SrcFactor 当前渲染的物体源颜色alpha值
DstFactor 屏幕已渲染的像素底色alpha值
混合公式,这里的目标颜色alpha值用时1-源颜色alpha,这个模式称为OneMinusSrcAlpha:
SrcColorRGB * SrcFactor + DstSrcColorRGB * (1 - SrcFactor)
Z-buffer 深度缓冲区
记录所有像素中当前离摄像机最近那个像素深度,像素深度就是指像素渲染的物体在坐标空间中的Z值
Frame buffer 帧(颜色)缓冲区
记录所有像素中当前离摄像机最近那个像素颜色(包括透明混合后面物体后的颜色)
深度测试 Ztest
检测当前像素的深度与深度缓冲区的深度
深度写入 Zwrite
将当前像素的深度写入深度缓冲区
当然,混合颜色是在判断完前后遮挡关系在渲染时进行的,这个步骤很简单
但是有一些复杂的问题(牵连到深度测试和深度缓冲)需要在渲染这之前解决,这是是否能正确渲染的关键所在,分别是:
1.不同类型物体的渲染顺序
2.同类型物体的渲染顺序 - 指的是透明物体
不同类型物体的渲染顺序
当透明物体和不透明物体有前后关系时,透明在前,不透明在后,两种渲染顺序会有两种结果:
1.先渲染后面的不透明物体,再渲染前面的透明物体
先渲染后面的不透明物体,会将不透明物体进行zTest,将深度值更新到z-Buffer,并将物体颜色更新到frame Buffer,再渲染前面的透明物体,同样深度测试,更新z-Buffer,从frame buffer取颜色进行混合,再更新回去,这样的顺序没问题
2.先渲染前面的透明物体,再渲染后面的不透明物体
先渲染前面的透明物体,会将透明物体进行zTest,将深度值更新到z-Buffer,并将物体颜色更新到frame Buffer,再渲染后面的不透明物体,同样深度测试,深度比前面大,就会抛弃不透明物体的颜色,就会透明物体看不到后面不透明物体,遮挡了后面物体
所以,物体类型是有渲染排序的,一般序列是背景->不透明物体->透明物体->半透明物体
渲染序列数值越大,越后面渲染,不透明物体2000,透明物体3000
同类型物体的渲染顺序 - 指的是透明物体
上面说了不同类型的物体渲染顺序,当同类型物体渲染时,无序也会出问题
我们假设渲染完不透明物体,然后正在渲染透明物体,分无序随机、从前往后、从后往前三种情况:
1.无序随机和从前往后的情况和上面的不同类型渲染中先渲染前面的透明再渲染后面的不透明情况是一样的,都会因为前面渲染的物体的深度缓冲值,在深度测试中后面物体的像素被抛弃掉,出现不正确的情况
2.从后往前这种情况是正确的,但是需要对物体先排序,再从排好序的物体队列中,从后往前取出来渲染,但是排序会耗费时间,每帧都排序对渲染时间影响非常大,当物体越多,排序的数量就越多,没有这个步骤怎么办?
我们可以开启Ztest,关闭Zwrite来模拟达到最好的渲染结果,这是最简单的方法,当然有上面的排序算法,我们先看看当前这种方法的解决思路
Tip:
Ztest开启用来检测当前物体深度与深度缓冲区的深度
Ztest关闭就是不检测直接绘制
Zwrite开启会写入缓冲区,更新深度,前提是写入条件符合(例如当前物体深度比缓冲区深度小或者大)
Zwrite关闭就是不写入缓冲区,不管是深度比缓冲区大还是小
我们从Ztest开启,Zwrite开启这种情况说起,因为这是问题的起源:
假设先渲染了前面的一个不透明物体,因为开启了Zwrite,所以前面的透明物体的深度值会被更新到z-Buffer中,再渲染后面的不透明物体时,就被丢弃渲染了。
(在这里说一下网上很多的文章都没点出重点,尤其LearnOpenGL文章中的图片,图片中透明窗户可以看到后面的不透明立方体,但是看不到透明的窗户。这是因为先渲染了后面不透明物体,然后再渲染透明物体时,先渲染了前面这个透明窗户,再渲染后面另一个透明窗户,这个时候深度测试丢弃了后面的透明窗户像素,所以才会出现这种图片。而文章的解释是,深度测试缺少透明度的判断,所以无法处理这种情况,不会进行前后物体的颜色混合。需要进行排序来解决,这个前后因果逻辑非常迷,就会让人无法理解,其实就是渲染顺序导致的,所以才需要排序。)
再说回透明物体Ztest开启,Zwrite关闭为什么能简单解决这种问题,因为深度测试开启,能检测当前透明物体深度值与深度缓冲区中的值,确认是否存在遮挡关系。
如果有遮挡,就是有不透明物体在,这就要分情况了,透明物体在后面,抛弃渲染,透明物体在前面,就进行颜色混合,并更新到颜色缓冲区,但是不会更新深度值,最靠前的还是不透明物体。
如果无遮挡,就将透明物体颜色更新到颜色缓冲区,但是也不更新深度值,深度缓冲区是Default值
这是正常情况。
当遇到同样是透明物体时,前面的透明物体遮挡后面的透明物体,同一个像素位置没有其他不透明物体,然后因为前面的透明物体没有更新过深度缓冲区,后面的透明物体进行深度测试时,就会通过,然后后面透明物体的颜色就和颜色缓冲区的值进行混合,更新会颜色缓冲区,这样就可以渲染出透明效果了。
但是这种先渲染前面在渲染后面的结果是不对的,因为混合时使用后面的物体Alpha值比例来混合的,前面的物体颜色透明比例是1-后面物体颜色Alpha混合,就会出现后面物体在前面,前面的在后面的渲染结果。
所以透明物体渲染的顺序也是要排序,从后往前渲染才是正确的