Unity Shader实现全透明
和半透明效果
实现透明物体的两个方法:
※ 对于不透明物体,我们不必考虑它们的渲染顺序
也可以得到正确的渲染结果。因为Unity中强大的深度缓冲(Depth Buffer 或称作 Z-Buffer)
可以帮助我们完成物体的前后效果。
※ 对于半透明物体则需要使用透明度混合
,且需要正确的渲染顺序
。
- 透明度测试(Alpha Test)
使用的是一个极端霸道的机制——只要一个片元的透明度不满足设定的阈值,那么直接丢弃该片元。被丢弃的片元不会再进行任何处理(包括颜色缓冲);但是如果满足设定的阈值,那么就会继续按照平常的不透明物体的渲染方式来处理它(即进行深度测试、深度写入(ZWirte)
等)。也就是说透明度测试
不需要关闭深度写入
。
——最终的结果 要么完全透明(看不见);要么完全不透明(就是不透明)。
- 透明度混合(Alpha Blending)
该方法是可以得到真正的半透明效果!它会将当前的片元的透明度作为混合银子,与存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入
!这就要我们格外小心物体的渲染顺序(Render Queue)
。但此时是没有关闭深度测试
的。也就是说——对于透明度混合
来说,深度缓冲是只读的。
因此,当使用透明度混合来渲染一个片元的时候,还是会比较他的深度值与深度缓冲中的深度值,如果距离摄像机更远,那么就不会再进行混合操作;如果距离摄像机更远,那么另一个不透明物体就会被渲染到该片元前面将其遮挡,最终的效果也是正常的。
深度缓冲的工作原理:
对于每一个开启了深度测试
的片元,在渲染之前会将自身的深度值
与深度缓冲中的值
进行比较,最终会得到两个结果:
- 距离摄像机
更远
的片元——不被渲染 - 距离摄像机
更近
的片元——会被渲染
这是因为,在深度测试
之后,如果片元的深度值更近,那么会覆盖深度缓冲中原有的深度值,且直接丢弃原有片元,最终只渲染深度缓冲中的片元。
最终我们的物体有了如下两种情况:
- 半透明物体A
远于
不透明物体B
最终Depth_B会被覆盖深度缓冲中的值,这样可以的得到正确的半透明效果。
- 半透明物体A
近于
不透明物体B
那么对于不同的渲染顺序,会有两种情况:
①如果先对A进行深度测试,再到B——由于A是半透明物体关闭了深度写入,那么A不会对深度缓冲写入数据。到物体B后,由于此时深度缓冲中并没有数据,那么物体B的深度值DepthB会写入到深度缓冲之中。最后的效果回事B再A的前面,最后得到的结果是恰恰相反的,是错误的。
②如果先对B进行深度测试,再到A——DepthB会首先填入深度缓冲之中,而之后A由于关闭了深度写入,因此不会刷新覆盖深度缓冲,最终的渲染效果与上面情况类似(都是错误的)。
综上,我们应该在不透明物体渲染完之后,再渲染半透明物体。
渲染顺序的重要性
对于两个不透明物体,可以参照上述深度缓冲的工作原理。但是由于我们的半透明物体是关闭深度写入的!
如果:半透明物体A 近于
半透明物体B
并且:根据不同的渲染顺序
那么:可以得到如下两个渲染结果:
- 先渲染B,再渲染A——物体B的颜色值ColorB会首先正常写入
颜色缓冲
中(半透明物体),然后ColorA会和颜色缓冲中的ColorB进行颜色混合
。最终,可以得到正确的半透明效果。 - 先渲染A,再渲染B——ColorA先写入颜色缓冲,再到ColorB与ColorA进行混合。结果会是相反的,也就是看上去是物体B在物体A前面,显然是错的!
正确的渲染“姿势”
!① 先渲染所有不透明物体,并开启不透明物体的深度测试和深度写入
!② 将半透明物体按照从后往前的顺序渲染
,并且开启深度测试 关闭深度写入
Unity Shader的渲染顺序
为了解决上述的②,Unity为此提供了渲染队列(Render Queue)
来解决这个问题
在Shader代码中的实现方法:
使用透明度测试实现全透明
效果的代码:
SubShader{
Tags{
"Queue"="AlphaTest"}
Pass{
...
}
}
使用透明度测试实现半透明
效果的代码:
SubShader{
Tags{
"Queue"="Transparent"}
//ZWirte Off
Pass{
ZWirter Off
//关闭深度写入
//如果写在SubShader之下,那么会对SubShader之下的所有Pass起作用
//如果写在Pass之下,仅对该Pass起作用
...
}
}