1. 开启深度写入的半透效果

前面提到过,在进行半透物体的渲染时需要关闭深度写入,但是如果物体本身存在复杂的遮挡关系时,关闭深度写入的渲染就会出现问题。

unity3d UI半透明遮罩 unity平面透明_着色器


如上图红框中的部分,这种情况下,可以使用开启深度写入的半透渲染方式。

开启深度写入的半透渲染包含两个Pass:

  • 第一个Pass开启深度写入,但不输出颜色,目的仅仅是将模型的深度信息写入深度缓存,从而得到正确的遮挡关系
  • 第二个Pass进行正常的透明度混合

这里我们通过 ZWrite On 开启深度写入

同时会用到一个新的指令——ColorMask,该指令用于设置颜色通道的写掩码,讲人话就是通知Unity哪些颜色通道不要写入,比如可以设置为 ColorMask RG 即表示不写入RG通道的颜色,只写入BA通道的颜色,设置为ColorMask A 即表示不写入Alpha值,特别的,当设置为 ColorMask 0 时,表示不写入任何颜色值,但此时如果开启了深度写入的话,会正常写入深度信息

Shader如下:

Shader "MyShader/Chapter_8/Chapter_8_KNot_Shader"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _Alpha("Alpha", Range(0, 1)) = 0.5
    }
    
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
        
        Pass
        {
            ZWrite On
            ColorMask 0
        }
    
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };
            
            fixed4 _Color;
            fixed _Alpha;
            
            v2f vert(a2v i)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 _worldNormal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 _diffuse = _LightColor0.rgb * _Color.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);
                return fixed4(_ambient + _diffuse, _Alpha);
            }
            
            ENDCG
        }
    
    }

}

效果如下

unity3d UI半透明遮罩 unity平面透明_UnityShader_02

2. 双面渲染的半透效果

在之前的透明度测试和透明度混合的例子中,渲染得到的效果还存在一个明显的问题,就时物体看起来像是只有一个片,这是因为对于透明(或者半透)的物体,应该能透过透明的部分看到物体内部的样子,而之前的Shader中并没有对这种情况做处理

unity3d UI半透明遮罩 unity平面透明_unity3d UI半透明遮罩_03


如上图中,被透明度测试剔除的部分,直接看到了后面的地面,但实际上应该看到的是立方体的内部。为了解决这种问题,我们可以使用双面渲染的半透渲染方式。

在双面渲染的半透渲染方式中,同样需要两个Pass:

  • 第一个Pass进行正面剔除,只渲染背面,通过设置 Cull Front 来实现
  • 第一个Pass进行背面剔除,只渲染正面,通过设置 Cull Back 来实现

这两个Pass中的其他代码都与前文完全一样。另外需要注意的是,由于SubShader是自上而下顺序执行Pass,因此在开启了深度写入的情况下(比如透明度测试),为保证正确的深度关系,需要Cull Front的Pass一定在Cull Back的Pass之上。

双面渲染的透明度测试Shader:

Shader "MyShader/Chapter_8/Chapter_8_AlphaTest_BothSide_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
        _AlphaTest("AlphaTest", range(0, 1)) = 0
    }
    
    SubShader
    {
        Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}
        
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            Cull Front
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaTest;
            
            v2f vert(a2v i)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 _worldNormal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed4 _sampledColor = tex2D(_MainTex, i.uv);
                clip(_sampledColor.w - _AlphaTest);
                fixed3 _diffuse = _LightColor0.rgb * _sampledColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);
                return fixed4(_ambient + _diffuse, _sampledColor.w);
            }
            
            ENDCG
        }
    
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            Cull Back
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaTest;
            
            v2f vert(a2v i)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 _worldNormal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed4 _sampledColor = tex2D(_MainTex, i.uv);
                clip(_sampledColor.w - _AlphaTest);
                fixed3 _diffuse = _LightColor0.rgb * _sampledColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);
                return fixed4(_ambient + _diffuse, _sampledColor.w);
            }
            
            ENDCG
        }
    
    }

}

效果如下:

unity3d UI半透明遮罩 unity平面透明_着色器_04

双面渲染的透明度混合Shader:

Shader "MyShader/Chapter_8/Chapter_8_AlphaBlend_BothSide_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
    }
    
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
        
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            
            Cull Front
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert(a2v i)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 _worldNormal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                float4 _mainColor = tex2D(_MainTex, i.uv);
                fixed3 _diffuse = _LightColor0.rgb * _mainColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);
                return fixed4(_ambient + _diffuse, _mainColor.w);
            }
            
            ENDCG
        }
    
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
            
            Cull Back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert(a2v i)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 _worldNormal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                float4 _mainColor = tex2D(_MainTex, i.uv);
                fixed3 _diffuse = _LightColor0.rgb * _mainColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);
                return fixed4(_ambient + _diffuse, _mainColor.w);
            }
            
            ENDCG
        }
    
    }

}

效果如下:

unity3d UI半透明遮罩 unity平面透明_unity3d UI半透明遮罩_05