第八章 透明效果
在Unity中,我们通常使用两种方法来实现透明效果:透明度测试(Alpha Test)和透明度混合(Alpha Blending)。对于不透明物体不考虑他们的渲染顺序也能得到正确的排序效果,这是由于强大的深度缓冲(depth buffer/z-buffer)的存在。在实时渲染中,深度缓冲是用于解决可见性问题的,他可以决定哪个物体的哪些部分会被渲染在前面,而哪些部分会被其他物体遮挡。它的基本思想是:根据深度缓存中的值来判断该片元距离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在于深度缓冲中的值进行比较(如果开启了深度测试),如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上(有物体挡住了它);否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中(如果开启了深度写入)。
1、UnityShader 的渲染顺序Unity提供了渲染队列来解决渲染顺序的问题:使用SubShader的Queue标签来决定我们的模型将归于哪个渲染队列。Unity内部使用一系列整数索引来表示每个渲染队列,且索引号越小越早被渲染。
2、透明度测试
原理:只要一个片元的透明度不满足条件,那么它对应的片元就会被舍弃。被舍弃的片元将不会在进行任何处理,也不会对颜色缓冲产生任何影响;否则就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。也就是说透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。它产生的效果要么完全透明要么完全不透明。
通常我们会在片元着色器中使用clip函数来进行透明度测试:
函数:void clip(float4 x);void clip(float3 x);void clip(float2 x);void clip(float1 x);void clip(float x);
参数:裁剪时使用的标量或矢量条件。
描述:如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。等同于:
透明度测试代码(片元着色器中):
fixed4 frag(v2f i) : SV_Target{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
//Alpha test
clip(texColor.a - _Cutoff);
if((texColor.a - _Cutoff)<0.0){
discard;
}
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(worldNormal,worldLightDir));
return fixed4(ambient+diffuse,1.0);
}
clip函数会判断它的参数(texColor.a-_Cutoff)是否为负数,如果是就舍弃该片元的输出。透明度测试得到的透明效果很"极端"要么完全透明,要么完全不透明,而且得到的透明效果在边缘处往往参差不齐,我们使用透明度混合来得到更柔滑的效果。
3、透明度混合
原理:这种方法可以得到真正的半透明效果。十一点当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合从而得到新的颜色,但是透明度混合需要关闭深度写入,但没有关闭深度测试,即当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值如果它的深度值距离摄像机更远,那么就不会再进行混合操作,当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。即深度缓冲是只读的。
我们使用Unity提供的混合命令Blend来进行混合。想要实现半透明的效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合。
4、ShaderLab的混合命令
混合的实现:当片元着色器产生一个颜色的时候,可以选择与颜色缓存中的颜色进行混合。混合和两个操作数有关:源颜色和目标颜色。源颜色我们用S表示指的是由片元着色器产生的颜色值,目标着色器我们用D表示指从颜色缓冲中读取到的颜色值。对他们进行混合后得到的输出颜色,我们用O表示,它会重新写入到颜色缓冲中。他们都包含了RGBA四个通道的值。在Unity中,当我们使用Blend(Blend Off除外)时,除了设置混合状态外也开启了混合。
1)混合等式和参数
现在,我们已知两个操作数:源颜色S和目标颜色D,想要得到输出颜色O就必须使用一个等式来计算。这个等式就是混合等式。当进行混合时,我们需要使用两个混合等式:一个用于混合RGB通道,一个用于混合A通道。当设置混合状态时,实际上设置的就是混合等式中的操作和因子。默认情况下,混合等式使用的操作都是加操作。
ShaderLab中设置混合因子的命令
第一个命令只提供了俩个因子,这意味着将使用同样的混合因子来混合RGB通道和A通道,即此时SrcFactorA将等于SrcFactor,DstFactorA将等于DstFactor。因子进行加法混合时使用的混合公式如下:
Orgb = SrcFactor x Srgb + DstFactor x Drgb
Oa = SrcFactorA x Sa + DstFactorA x Da
ShaderLab支持的几种混合因子如下:
使用上面的指令进行设置时,RGB通道的混合因子和A通道的混合因子都是一样的,有时我们希望可以使用不同的参数混合A通道,这时就可以利用Blend SrcFactor DstFactor,SrcFactorA DstFactorA指令。例如,如果我们想要在混合后输出颜色的透明度值就是源颜色的透明度,我们使用命令: Blend ScrAlpha OneMinusScrAlpha, One Zero
2)混合操作
我们可以使用ShaderLab的BlendOp BlendOperation命令,即混合操作命令。
混合操作命令通常是与混合因子命令一起工作的。但当使用Min或Max混合操作时,混合因子实际上是不起任何作用的,它们仅会判断原始的源颜色和目的颜色之间的比较结果。
3)常见的混合类型
Blend SrcAlpha OneMinusSrcAlpha //正常(Normal),即透明度混合
Blend OneMinusDstColor One //柔和相加(Soft Additive)
Blend DstColor Zero //正片叠底(Multiply),即相乘
Blend DstColor SrcColor //两倍相乘(2x Multiply)
BlendOp Min Blend One One //变暗
BlendOp Max Blend One One //变亮
Blend OneMinusDstColor One //滤色
Blend One OneMinusSrcColor //滤色
Blend One One //线性减淡
5、双面渲染的透明效果
如果我们想要得到双面渲染的效果,可以使用Cull指令来控制需要剔除哪个面的渲染图元。
Cull Back | Front | Off
如果设置为Back,那么那些背对着摄像机的渲染图元就不会被渲染,这也是默认情况下的剔除状态;如果设置为Front,那么那些朝向摄像机的渲染图元就不会被渲染;如果设置为Off,就会关闭剔除功能,所有的渲染图元都会被渲染。
1)透明度测试的双面渲染
只需要在Pass的渲染设置中使用Cull指令来关闭剔除即可。
2)透明度混合的双面渲染
把双面渲染的工作分成两个Pass--第一个Pass只渲染背面(Cull Front),第二个Pass只渲染正面。(Cull Back)