iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 20
0
自我挑戰組

初見Unity Shader系列 第 20

透明物體的陰影

文章內使用Unity 2019 LTS

昨天Day19的做出來陰影效果並沒有考慮到透明物體,所以今天來補完透明物體這一塊...

目標

  • 透明物體的陰影

對隱形物體...

透明混合

在之前的半透明物體中,提過深度緩衝與半透明這兩個會產生矛盾的地方,而陰影貼圖又需要利用深度緩衝來幫忙製作,所以讓半透明物體產生陰影會是一件奇怪的事(光會透過去),所以如果要像Day19利用Fallaback來處理陰影,會使用:

...
Fallback "Transparent/VertexLit"
...

這裡面的Fallback就沒有關於ShadowCaster的處理,如果有需要讓半透明物體可以投射影子,還是可以用:

...
Fallback "VertexLit"
...

透明測試

同時也是在半透明物體這一篇的下面,很簡短的補充透明測試是甚麼,回顧一下,透明測試像是深度測試類似,開發者可以設定一個界線,只要沒有過該界線的Alpha值就直接淘汰。

以下是利用透明測試做出來的半透明效果Shader:

Shader "Learning/Ch8/alpha-test-both-sided"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        // 預設0.5,只要貼圖的Alpha值小0.5就直接剃除
        _Cutoff ("Cutoff", Range(0, 1)) = 0.5
    }
    SubShader
    {
        Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
        
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            
            // 關閉面剃除
            // 當不足Alpha值的那面被剃除時
            // 需要看得到後面的通過測試的
            Cull off
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _Cutoff;

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert(appdata_base app)
            {
                v2f o;

                o.pos = UnityObjectToClipPos(app.vertex);

                o.worldPos = mul(unity_ObjectToWorld, app.vertex).xyz;

                o.worldNormal = UnityObjectToWorldNormal(app.normal);

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

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 norm = normalize(i.worldNormal);
                fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);
                
                // 把沒痛過透明測試的片段剃除
                clip(texColor.a - _Cutoff);

                fixed3 albedo = texColor.rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(norm, lightDir));

                return fixed4(ambient + diffuse, 1.0);
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

這裡我們可以利用透明測試做出陰影效果,把陰影效果跟透明測試合起來:

Shader "Learning/AlphaTestShadow" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
	}
	SubShader {
		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			Cull Off
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _Cutoff;
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
				SHADOW_COORDS(3)
			};
			
			v2f vert(appdate_base 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);
			 	
			 	TRANSFER_SHADOW(o);
			 	
			 	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);
				
				fixed3 albedo = texColor.rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
							 	

				fixed shadow = SHADOW_ATTENUATION(i);
			 	
				return fixed4((ambient + diffuse) * shadow, 1.0);
			}
			
			ENDCG
		}
	}
    // 換成這個來做出陰影投射
	FallBack "Transparent/Cutout/VertexLit"
}

Reference


上一篇
簡單的陰影實作方法
下一篇
立方體貼圖、Skybox、環境貼圖
系列文
初見Unity Shader30

尚未有邦友留言

立即登入留言