透明效果
渲染顺序
Unity为了解决渲染顺序的问题提供了渲染队列这一解决方案。我们可以使用SubShader的Queue标签来决定我们的模型将归于哪个渲染队列。透明度测试AlphaTest,透明度混合Transparent。
透明度测试
SubShader{
Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}//渲染队列设置为AlphaTest,RenderType可以让Unity把这个Shader归入提前定义的组(这里就是TransparentCutout组),以指明该shader是一个使用了透明度测试的shader。IgnoreProjector设为true,则该shader不会受投影器(Projectors)的影响
Pass{
Tags{"LightMode" = "ForwardBase"}//定义了正确的LightMode才能得到一些unity内置光照变量,例如_LightColor0
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff; //因为其范围在[0,1]所以用fixed精度存储
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f {
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) :SV_Target{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
clip(texColor.a - _Cutoff);//判断texColor.a - _Cutoff是否为负数,若为负数则舍弃该片元输出
fixed3 albedo = texColor * _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);
}
ENDCG
}
}
效果图:
透明测试得到的效果很极端,要么完全透明,要么完全不透明,像在物体上挖了一个洞。
透明度混合
SubShader{
Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
Pass{
Tags{"LightMode" = "ForwardBase"}//定义了正确的LightMode才能得到一些unity内置光照变量,例如_LightColor0
Zwrite Off //关闭深度写入
Blend SrcAlpha OneMinusSrcAlpha //开启并设置混合模式 目标颜色的混合因子设置为OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f {
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) :SV_Target{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
透明度混合使用当前片元的透明度作为混合因子,与已存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是需要我么你关闭深度写入,这使得我们需要非常小心物体的渲染顺序。
效果图:
但当物体的本身有复杂的遮挡关系,或是包含了复杂的非凸网格的时候,就会有各种各样的排序村错误而产生的错误的透明效果。开启深度写入则可以解决这个问题。
开启深度写入的半透明效果
Pass{
Zwrite On
ColorMask 0//ColorMask用于设置颜色通道的写掩码 ColorMask为0时,意味着该pass不写入任何颜色通道 即不会输出任何颜色
}
在之前的基础上增加一个Pass,这个Pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓存中。这个方法的缺点是,多使用一个Pass会对性能造成一定的影响。
效果图: