複雜的光照與更復雜的陰影實現過程——ShaderCP9
——20.8.28
這章的內容看了很久,也有很多複雜的內容。中途還有事情耽擱了一會。開學後就繼續好好記錄努力。
我們在遊戲中能看到的讓人覺得真實感的來源之一就是真實的光照以及光照所產生的陰影。下面的內容分為兩個部分一個是光照的部分一個是陰影的生成部分。
說到光照在Unity中渲染路徑就是決定光照是以何種方式應用到Shader中的。所以要通過指定Pass通道的渲染路徑型別 “LightMode” 來進行Shader的光照計算。簡單地說就是設定渲染路徑後Unity便會提供對應的光照資訊給使用者進行光照計算。
目前有三種渲染路徑 1)前向渲染 2)延遲渲染(有新的) 3)頂點照明渲染(被拋棄)
當GPU不支援所選擇的渲染路徑則會降級
設定渲染路徑便是在Tags設定 “LightMode” 有以下支援的的標籤
如果沒有指定渲染路徑可能會出錯,因為此時當作為頂點照明渲染路徑,當中的一些光照變數可能就不會被賦值。
一、前向渲染路徑
每進行一次完整的前向渲染路徑1.我們需要渲染該物件的渲染圖元2.計算兩個緩衝區的資訊 a.深度緩衝區(決定是否可見)b.顏色緩衝區(若可見,更新顏色緩衝區)對燈光範圍內的物品每一個燈光處理一次執行一次Pass
三種處理光照的方式 1)逐頂點處理 2)逐畫素處理 3)球諧函式(SH)
一種光源的處理方式由型別和渲染模式(是否重要的,RenderMode)渲染一個物體要對光源進行排序(對物體的影響程度)要遵循以下規則 1)場景中最亮的平行光總是按逐畫素處理2)渲染模式被設定為Not Important按逐頂點或是SH 3)渲染模式被設定為Important按逐畫素處理 4)如果按以上規則得到的逐畫素光源數量小於Quality Setting(PixelLight Count)將會由更多的光源按照逐畫素處理
注意事項:1.BasePass支援的光照特性 2.BasePass渲染的平行光預設有燈光 AddtivePass渲染的光源預設情況下沒有陰影(即使設定了ShadowType)可以通過 #pragma multi_compile_fwdadd_fullshadows 代替#pragma multi_compile_fwdadd 為點光源和聚光燈開啟陰影 3.環境光(環境光和平行光不同)和自發光在BasePass計算因為只計算一次在AdditivePass會導致多次疊加 4.AdditivePass還要開啟混合不然只有一個光源的影響 5.一個BassPass可定義多次(比如雙面渲染)但只執行一遍而AdditivePass會根據影響該物體的逐畫素光源多次呼叫
以上是一些內建的函式和變數
二、頂點照明渲染
頂點照明可在前向渲染中完成,一個頂點照明的Pass最多8個逐頂點光源
三、延遲渲染路徑
G緩衝 儲存了離攝像機最近的表面的其他資訊 包含兩個單元第一個Pass計算哪些片元可見,第二個Pass利用G緩衝區中的資訊光照計算。效率取決於場景的複雜度,而取決於螢幕空間大小。
優點 1)場景中的光源數目多,用前向渲染會造成效能瓶頸 2)每個光源採用逐畫素處理 缺點 1)不支援抗鋸齒 2)不能處理半透明 3)對顯示卡由需求
分析完渲染路徑之後我們簡單過一下基礎的燈光型別在unity中存在的燈光型別以及它所具有的特點以及屬性 1)平行光(不存在衰減 面向全域性)2)點光源(邊緣的強度為0,中間的衰減值可以用函式表示)3)聚光燈(同上,衰減計算更加複雜)4)面光源(只有烘培有用) 燈光所具有的基本特性 1)位置 2)方向 3)顏色 4)強度 5)衰減(到某點的衰減與距離有關)
UnityShader這本書只有對前向渲染有具體的內容,這裡留一個延遲渲染路徑的小坑(?)
主要就是注意要新增一個庫“AutoLight.cginc” 其中有對之前對應渲染路徑的內建變數定義,否則就找不到 比如_LightMatrix0之類
#ifdef USING_DIRECTIONAL_LIGHT fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //normalize(_WorldSpaceLightPos0.xyz); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif
這部分是在AdditivePass中的中區分平行光以及其他光源的光源方向。
#ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; //atten = 1.0; #endif
這裡部分是最重要的這部分是在AdditivePass中對衰減的定義。在BasePass因為主要逐畫素處理一個最重要的平行光 而平行光不存在衰減所以atten為1 而在AdditivePass需要區分平行光和其他光源 點光源採用對應函式進行衰減。在Unity中為了方便使用一張衰減紋理_LightTexture0來為光源計算衰減,首先要把對應距離燈光的點通過座標運算從世界到光源空間。然後通過點乘也就是頂點距離的平方來進行紋理取樣。來避免開方計算。上面的".rr"相當於是取x並定義float2(x,x)用於取樣。UNITY_ATTEN_CHANNEL是衰減值所在的衰減通道。我們不妨看一下這張_LightTexture0是什麼樣的圖,就能理解為什麼要平方之後再取樣。
這張圖就是按照平方值設計的。可以放到畫圖工具中發現它不是線性的。實際相當於一維座標。因為只有x。
實際上這麼分其實是不夠的因為其他燈光的聚光燈的衰減是不一樣的。這裡暫時留一下(?)
// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight' Shader "Unlit/9-1ForwardRendering" { Properties { _Diffuse("Diffuse", Color)= (1,1,1,1) _Specular("Specular", Color)= (1,1,1,1) _Gloss("Gloss", Range(8.0, 256))= 8 } SubShader { Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(i.worldNormal); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss); fixed atten = 1.0; return fixed4(ambient + (diffuse + specular) * atten, 1.0); } ENDCG } Pass { Tags{ "LightMode"="ForwardAdd" } Blend One One CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdadd #include "Lighting.cginc" #include "UnityCG.cginc" #include "AutoLight.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); #ifdef USING_DIRECTIONAL_LIGHT fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss); #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; //atten = 1.0; #endif return fixed4((diffuse + specular) * atten, 1.0); } ENDCG } }FallBack "Specular" }
主要就是在之前的光照模型上區分了BasePass以及AdditivePass,需要指定渲染路徑以及新增對應的#pragma。最後最主要的是增加燈光的衰減。我們可以看一下實際效果。通過改變點光源的atten。足以看出衰減的真實感。衰減也可以用公式不用取樣。一樣後面繼續研究(?)老留坑專家了。
接下來便是最令人頭大的陰影了。但又是最不可獲取的部分。書中的理解部分會居多。
我們先看一個概念ShadowMap。就是把攝像機放到光源重合的位置然後場景中的陰影就是攝像機看不見的位置。 (本質上是為了得到光源空間的深度圖)
在前向渲染中,平行光開啟陰影就會計算它的陰影對映紋理(shadowmap)。實際上他是一張深度圖(mark一下後面的仔細研究一下深度的應用?)記錄了從光源位置出發能看到離他位置最近的表面位置也就是深度資訊。那具體是怎麼判斷距離光源最近的表面呢?有以下兩種方法。1)就是用上面的這個ShadowMap方法,把攝像機放到光源位置然後通過前向渲染的BasePass/AdditivePass更新深度資訊。但是使用這種正常渲染方式其中涉及狠毒複雜的光照模型計算所以很浪費 2)使用額外的Pass “LightMode”=“ShadowCaster” 渲染目標是陰影對映紋理不是幀快取。首先一樣是把攝像機放到光源位置然後呼叫該Pass(此事獲得的深度資訊是光源空間,因為和光源重合)。介紹了上述兩個獲得表面的方法,在unity中首先會a.找該Pass b.然後找FallBake(因為FallBack定義的預設Shader會有對陰影定義的ShadowCaster通道) c.如果都沒有就沒有陰影。
然後到了最後一步就是獲得了對應的深度怎麼把它應用到陰影上。陰影對映技術就是我們需要看的 主要是由兩種方法 1.傳統方法,把正常的頂點座標轉換到光源空間,然後用xy分量對陰影對映紋理取樣。如果深度小於改頂點的深度值也就是z分量則在陰影裡。 2.螢幕空間的陰影投射技術。用陰影對映紋理以及攝像機(相機擺放位置即Game檢視)的深度紋理通過這兩張圖得到螢幕空間的陰影圖。攝像機記錄的表面深度深度>其轉換到陰影紋理的深度即為可見,但在該燈源的陰影中。然後對陰影圖取樣。
所以總結一下 兩個過程 1.我們想要讓物體接受其他物體的陰影我們就要對陰影圖取樣然後於光照結果相乘。2.我們想要一個物體向其他物體投射陰影則需要把該物體加入陰影對映紋理的計算中。我們下面進入實踐過程。
我們可以看到這張圖發現有一點奇怪,物品命名有陰影但是這個平面卻沒有。主要原因是平面在unity中只有一面渲染另一面不渲染。所以解決方法便是改變平面的MeshRenderer中的CastShadow為TwoSides才能得到正確的陰影。
我們看一下這張圖發現這個方塊沒有接受到平面的陰影但自己卻有陰影而且在Unity設定是正確的即有開啟CastShadow 實際上之所以方塊有陰影是因為採用了FallBack中的“ShadowCaster”的通道但是我們可以從上圖得知平面想要投射陰影需要加入陰影投射紋理。實際上是輸出片元的時候沒有采樣陰影對映紋理。我們可以通過FrameBugger觀察它的深度圖。
上面兩張圖是一樣的陰影圖。但是在渲染方塊的時候卻不一樣。
SHADOW_COORDS(2)//在v2f中的定義 TRANSFER_SHADOW(o);//在vert中 //在frag中 fixed shadow = SHADOW_ATTENUATION(i); return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
首先SHADOW_COORDS(2)用於對陰影取樣紋理的座標裡面的索引值是2也就對應的是TEXCOORD2.在片元函式中的就是進行陰影對映技術把資訊儲存到_ShadowCrood。SHADOW_ATTENUATION對紋理進行取樣。要注意結構體的命名可能會導致定義的巨集不可用。
// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight' // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-2Shadow" { Properties { _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) _Specular ("Specular", Color) = (1, 1, 1, 1) _Gloss ("Gloss", Range(8.0, 256)) = 20 } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "AutoLight.cginc" #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNoraml : TEXCOORD0; float3 worldPos : TEXCOORD1; SHADOW_COORDS(2) }; fixed4 _Specular; fixed4 _Diffuse; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNoraml = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); TRANSFER_SHADOW(o); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNoraml = normalize(i.worldNoraml); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNoraml, worldLightDir)); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(viewDir + worldLightDir); fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(worldNoraml, halfDir)), _Gloss); fixed atten = 1.0; fixed shadow = SHADOW_ATTENUATION(i); return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0); } ENDCG } Pass { Tags { "LightMode"="ForwardAdd" } Blend One One CGPROGRAM #pragma multi_compile_fwdadd #pragma multi_compile_fwdadd_fullshadows #pragma vertex vert #pragma fragment frag #include "AutoLight.cginc" #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNoraml : TEXCOORD0; float3 worldPos : TEXCOORD1; }; fixed4 _Specular; fixed4 _Diffuse; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNoraml = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNoraml = normalize(i.worldNoraml); #ifdef USING_DIRECTIONAL_LIGHT fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNoraml, worldLightDir)); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(viewDir + worldLightDir); fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(worldNoraml, halfDir)), _Gloss); #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; #endif return fixed4((diffuse + specular) * atten , 1.0); } ENDCG } }FallBack "Specular" }
現在我們就有平面的投影了。
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
這個函式是可以直接統一管理衰減以及陰影。不需要定義atten可以直接用。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-3AttenuationAndShadowUseBuildInFunc" { Properties { _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) _Specular ("Specular", Color) = (1, 1, 1, 1) _Gloss ("Gloss", Range(8.0, 256)) = 20 } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "AutoLight.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; SHADOW_COORDS(2) }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); TRANSFER_SHADOW(o); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 halfDir = normalize(viewDir + worldLightDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4(ambient + (diffuse + specular) * atten, 1.0); } ENDCG } Pass { Tags { "LightMode"="ForwardAdd" } CGPROGRAM #pragma multi_compile_fwdadd #pragma multi_compile_fwdadd_fullshadows #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "AutoLight.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; SHADOW_COORDS(2) }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); TRANSFER_SHADOW(o); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 halfDir = normalize(viewDir + worldLightDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4((diffuse + specular) * atten, 1.0); } ENDCG } } }
這樣BasePass和AdditivePass就可以統一。
最後就是上一節的一個小坑也是填上了。就是半透明物體的陰影關係。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-4AlphaTestWithShadow" { Properties { _Color("Color", Color) = (1,1,1,1) _MainTex ("Texture", 2D) = "white" {} _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5 } SubShader { Tags { "RenderType"="TransparentCutOut" "IgnoreProjector"="True" "Queue"="AlphaTest" } Pass { Tags{ "LightMode"="ForwardBase" } Cull Off CGPROGRAM #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "AutoLight.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; SHADOW_COORDS(3) //TEXCOORD3 }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; fixed _Cutoff; v2f vert (appdata 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); 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.rgb * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir)); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4(ambient + diffuse * atten, 1.0); } ENDCG } }
//FallBack "VertexLit"
FallBack "Transparent/Cutout/VertexLit" }
首先是透明測試的投影。效果圖如下。圖一是表示能夠正常的接受陰影。圖二圖三分別是採用不同的FallBack的效果後者相對於前者是比較正確的。最後一張圖是是否開啟CastShadow中的TwoSides左邊是開啟的可以看出正方形的內部投影是和左邊相比體現了處理。
最後是透明度混合的陰影。同樣的增加FallBack
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Unlit/9-5AlphaBlendWithShadow" { Properties { _Color ("Color Tint", Color) = (1, 1, 1, 1) _MainTex ("Main Tex", 2D) = "white" {} _AlphaScale ("Alpha Scale", Range(0, 1)) = 1 } SubShader { Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha 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 _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; SHADOW_COORDS(3) }; 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); // Pass shadow coordinates to pixel shader 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); fixed3 albedo = texColor.rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir)); // UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4(ambient + diffuse * atten, texColor.a * _AlphaScale); } ENDCG } } //FallBack "Transparent/VertexLit" // Or force to apply shadow FallBack "VertexLit" }
採用FallBack "VertexLit"會強制投影同時接受陰影。"Transparent/VertexLit"兩者都不會。主要是半透明物體由於關閉了深度寫入也會影響對應陰影的生成。
這就是最後了。感謝您讀到這裡。Cheers!
PS:這章總算是看完了。還留了很多坑。慢慢補把。