UnityShader基础(三)——基础透明

        透明是一个大坑,个人看法,游戏开发中能避免就避免,即便是真要做,但也应该尽量使用简单模型,个人理解。

一、单Pass的透明度效果         

        1.1 透明度测试:若大于阈值,保留片元,若小于直接剔除。

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("基础纹理",2D)="white"{}
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(0,256))=20
        
        _AlphaTest("透明度测试阈值",float)=0.5
        
    }
    SubShader
    {
        
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            fixed _Gloss;
            fixed _AlphaTest;
            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:TEXCOORD3;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos=mul(unity_ObjectToWorld,v.vertex);
              
                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                fixed3 worldNormal=normalize(i.worldNormal);
                
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
                
                //纹理采样
                fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;

                //透明度测试,以纹理采样作为依据
                clip(texResult.a-_AlphaTest);
                
                fixed3 diffuse=_LightColor0*texResult
                            *(0.5*dot(worldLight,worldNormal)+0.5);
                
                fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
                
                fixed3 halfDir=normalize(viewDir+worldLight);
                
                fixed3 specular=_LightColor0.rgb*_Specular
                            *pow(saturate(dot(worldNormal,halfDir)),_Gloss);
                
                return fixed4(ambient+diffuse+specular,1);
            }
            
            ENDCG
        }
    }
}

         1.2 透明度混合,关闭深度写入

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("基础纹理",2D)="white"{}
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(0,256))=20
        
        _AlphaBlend("透明度混合阈值",float)=0.5
        
    }
    SubShader
    {
        Tags{"Queue"="Transparent" "IgnoreProjector"="True"}
        
        Pass
        {
            Tags{"LightMode"="ForwardBase" }
            
            ZWrite off
            
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            fixed _Gloss;

            fixed _AlphaBlend;

            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:TEXCOORD3;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos=mul(unity_ObjectToWorld,v.vertex);
                
                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                fixed3 worldNormal=normalize(i.worldNormal);
                
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
                
                //纹理采样
                fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;               
                
                fixed3 diffuse=_LightColor0*texResult
                            *(0.5*dot(worldLight,worldNormal)+0.5);
                
                fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
                
                fixed3 halfDir=normalize(viewDir+worldLight);
                
                fixed3 specular=_LightColor0.rgb*_Specular
                            *pow(saturate(dot(worldNormal,halfDir)),_Gloss);
                fixed4 color=fixed4(ambient+diffuse+specular,1);
                return fixed4(color.rgb,texResult.a*_AlphaBlend);
                //结果一样在某些情况下
                return fixed4(color.rgb,_AlphaBlend);
            }
            
            ENDCG
        }
    }
}

                Unity内部默认会剔除背面,所以我们看到的透明效果非常怪。

        1.3 关闭深度写入,关闭剔除        

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("基础纹理",2D)="white"{}
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(0,256))=20

        _AlphaBlend("透明度混合阈值",float)=0.5

    }
    SubShader
    {
        Tags
        {
            "Queue"="Transparent" "IgnoreProjector"="True"
        }

        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }

            ZWrite off
            Cull Front
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            fixed _Gloss;

            fixed _AlphaBlend;

            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:TEXCOORD3;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                fixed3 worldNormal = normalize(i.worldNormal);

                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                //纹理采样
                fixed4 texResult = tex2D(_MainTex, i.uv) * _Color;

                fixed3 diffuse = _LightColor0 * texResult
                    * (0.5 * dot(worldLight, worldNormal) + 0.5);

                fixed3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);

                fixed3 halfDir = normalize(viewDir + worldLight);

                fixed3 specular = _LightColor0.rgb * _Specular
                    * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
                fixed4 color = fixed4(ambient + diffuse , 1);
                return fixed4(color.rgb, texResult.a * _AlphaBlend);
                //结果一样在某些情况下
                return fixed4(color.rgb, _AlphaBlend);
            }
            ENDCG
        }
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }

            ZWrite off
            Cull back
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            fixed _Gloss;

            fixed _AlphaBlend;

            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:TEXCOORD3;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                fixed3 worldNormal = normalize(i.worldNormal);

                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                //纹理采样
                fixed4 texResult = tex2D(_MainTex, i.uv) * _Color;

                fixed3 diffuse = _LightColor0 * texResult
                    * (0.5 * dot(worldLight, worldNormal) + 0.5);

                fixed3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);

                fixed3 halfDir = normalize(viewDir + worldLight);

                fixed3 specular = _LightColor0.rgb * _Specular
                    * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
                fixed4 color = fixed4(ambient + diffuse , 1);
                return fixed4(color.rgb, texResult.a * _AlphaBlend);
                //结果一样在某些情况下
                return fixed4(color.rgb, _AlphaBlend);
            }
            ENDCG
        }
    }
}

            1.4 透明度混合,开启深度写入,开启剔除                            

Shader "Custom/Test2"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("基础纹理",2D)="white"{}
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(0,256))=20
        
        _AlphaBlend("透明度混合阈值",float)=0.5
        
    }
    SubShader
    {
        Tags{"Queue"="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 "UnityLightingCommon.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            fixed _Gloss;

            fixed _AlphaBlend;

            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:TEXCOORD3;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos=mul(unity_ObjectToWorld,v.vertex);

                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                fixed3 worldNormal=normalize(i.worldNormal);
                
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
                
                //纹理采样
                fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;               
                
                fixed3 diffuse=_LightColor0*texResult
                            *(0.5*dot(worldLight,worldNormal)+0.5);
                
                fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
                
                fixed3 halfDir=normalize(viewDir+worldLight);
                
                //fixed3 specular=_LightColor0.rgb*_Specular
                   //         *pow(saturate(dot(worldNormal,halfDir)),_Gloss);
                fixed4 color=fixed4(ambient+diffuse,1);
                return fixed4(color.rgb,texResult.a*_AlphaBlend);
                //结果一样在某些情况下
                return fixed4(color.rgb,_AlphaBlend);
            }
            
            ENDCG
        }
    }
}

            

                关于为什么要用ColorMask 0 ,它的作用是对通过写入遮罩阻止RGBA通道的写入。

                当我们在Pass中什么CG代码都不写时,Unity会自动填充最低级的Shader ,即在屏幕上输出一坨内置默认的白色,我们在第二个Pass中做混合操作,自然不希望第一个Pass的颜色会产生干扰,为了防止内置Shader的干扰,使用ColorMask 0;

                甚至连ZWrite On也可以不写,Unity默认开启了深度写入,除非你亲自关闭。

                当我们写了CGPROGRAM和ENDCG,哪怕里面什么都没写,Unity也不会自动填充。那有些人可能说了,那我直接这样做不写ColorMask 0,然后CGPROGRAM里面什么也不写也行吧。答案是不行,当Unity检测到流水线什么也没有输入和输出时,会自动打上紫色表示Shader丢失。而且,你什么也不写,就意味着深度值也无法写入,因为流水线拿不到输入,缓冲区自然什么也没有。有些人可能又会说了,那我ColorMask 0和CGPROGRAM同时写呢,只能说你在质疑开发Unity的程序员的基本功,结果当然是Shader丢失输出紫色。

二、双Pass渲染的透明效果

        2.1 透明度测试

                没什么好说的,一句命令把剔除关了就行

Pass
{
    ...
    Cull Off
    ...              
}

        2.2 透明度混合

                透明度混合讲的就是一个先后顺序,对于这种只需要两个Pass,一个先渲染背面,一个正面。也没什么好说的,第一个Pass加一句Cull Front,第二个Pass加一句Cull Back,其他不变。

      

三、一些注意事项和问题

        当你不开启透明度混合时,只是单纯调节片元着色器输出的Alpha值,这样不会有任何结果上的变化。        

fixed4 frag(v2f i):SV_Target
{                
        fixed3 color=tex2D(_MainTex,i.uv.xy)*_Color;                                                                                          
        return fixed4(color,0.5);                                 
}
fixed4 frag(v2f i):SV_Target
{                
        fixed3 color=tex2D(_MainTex,i.uv.xy)*_Color;                                                                                          
        return fixed4(color,1);                                 
}

        这两个代码没有任何区别,因为你没有开启透明度混合。

        还有模型本身的问题:          

        

        这个模型就和立方体不一样,结果非常怪异。而左边是单Pass的结果,右边是双Pass的结果。至于左边的一片白,一片黑,这个目前猜测的结果混合不充分造成的影响。但我让深度测试一直通过,结果也没有变化。不知道为什么。       

四、总结透明物体的渲染

        为了实现真正意义上的透明,必须关闭模型剔除和深度写入,否则模型本身就会出现遮挡信息以及背面没有透明效果,至于是单Pass还是双Pass,看实际情况。

        至于实现一些简单的透明效果,是否开启深度写入和剔除,看模型,看需求。尽量简单模型做透明,混合的效果非常吃模型本身。

        

猜你喜欢

转载自blog.csdn.net/SliverAsh_/article/details/126823422