一、代碼結構
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)Shader "Hidden/Internal-ScreenSpaceShadows" {Properties {_ShadowMapTexture ("", any) = "" {} // 陰影貼圖紋理(級聯陰影圖集)_ODSWorldTexture("", 2D) = "" {} // 存儲世界空間坐標或額外數據的紋理}CGINCLUDE// 聲明陰影貼圖相關變量UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);float4 _ShadowMapTexture_TexelSize; // 陰影貼圖紋素尺寸,用于計算偏移#define SHADOWMAPSAMPLER_AND_TEXELSIZE_DEFINEDsampler2D _ODSWorldTexture; // 世界空間坐標紋理#include "UnityCG.cginc" // Unity通用CG函數#include "UnityShadowLibrary.cginc" // 陰影相關庫函數// 級聯陰影混合配置(0 表示禁用混合)#define UNITY_USE_CASCADE_BLENDING 0#define UNITY_CASCADE_BLEND_DISTANCE 0.1 // 級聯過渡距離// 頂點著色器輸入結構struct appdata {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)float3 ray0 : TEXCOORD1;float3 ray1 : TEXCOORD2;#elsefloat3 ray : TEXCOORD1;#endifUNITY_VERTEX_INPUT_INSTANCE_ID};// 片段著色器輸入結構struct v2f {float4 pos : SV_POSITION;// xy uv / zw screenposfloat4 uv : TEXCOORD0;// View space ray, for perspective casefloat3 ray : TEXCOORD1;// Orthographic view space positions (need xy as well for oblique matrices)float3 orthoPosNear : TEXCOORD2;float3 orthoPosFar : TEXCOORD3;UNITY_VERTEX_INPUT_INSTANCE_IDUNITY_VERTEX_OUTPUT_STEREO};// 頂點著色器(當前為空,需根據需求實現)v2f vert (appdata v) {}// 聲明深度紋理(用于計算相機空間坐標)UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);// 級聯投影的尺寸比例(相對于第一個級聯)float4 unity_ShadowCascadeScales;// 根據陰影劃分方式定義級聯權重計算函數#if defined (SHADOWS_SPLIT_SPHERES) // 使用球形分割的級聯#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights_splitSpheres(wpos)#else // 使用默認的級聯方式(基于距離)#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights(wpos, z)#endif// 根據級聯數量定義陰影坐標計算函數#if defined (SHADOWS_SINGLE_CASCADE) // 單級聯模式#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord_SingleCascade(wpos)#else // 多級聯模式#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord(wpos,cascadeWeights)#endif// 獲取級聯權重(多級聯模式)inline fixed4 getCascadeWeights(float3 wpos, float z) { }// 獲取級聯權重(球形分割模式)inline fixed4 getCascadeWeights_splitSpheres(float3 wpos) { }// 獲取陰影坐標(多級聯模式)inline float4 getShadowCoord( float4 wpos, fixed4 cascadeWeights ) { }// 獲取陰影坐標(單級聯模式)inline float4 getShadowCoord_SingleCascade( float4 wpos ) { }// 根據深度和逆投影矩陣計算相機空間坐標(PS階段計算)inline float3 computeCameraSpacePosFromDepthAndInvProjMat(v2f i) { }// 根據頂點著色器輸出信息計算相機空間坐標(VS階段計算)inline float3 computeCameraSpacePosFromDepthAndVSInfo(v2f i) { }// 計算相機空間坐標(根據子著色器配置選擇實現方式)inline float3 computeCameraSpacePosFromDepth(v2f i) { }// 硬陰影片段著色器fixed4 frag_hard (v2f i) : SV_Target { }// 軟陰影片段著色器(PCF)fixed4 frag_pcfSoft(v2f i) : SV_Target { }ENDCG// 子著色器:硬陰影(SM 2.0 及以下)SubShader {Tags{ "ShadowmapFilter" = "HardShadow" }Pass {//具體代碼在下方}}// 子著色器:硬陰影(強制PS階段逆投影,通用但精度較低)SubShader {Tags{ "ShadowmapFilter" = "HardShadow_FORCE_INV_PROJECTION_IN_PS" }Pass{//具體代碼在下方}}// 子著色器:軟陰影(PCF,SM 3.0 及以上)SubShader {Tags {"ShadowmapFilter" = "PCF_SOFT"}Pass {//具體代碼在下方}}// 子著色器:軟陰影(強制PS階段逆投影,SM 3.0 及以上)SubShader{Tags{ "ShadowmapFilter" = "PCF_SOFT_FORCE_INV_PROJECTION_IN_PS" }Pass{//具體代碼在下方}}Fallback Off // 無備用著色器
}
二、HardShadow
// 子著色器:硬陰影(SM 2.0 及以下)SubShader {Tags{ "ShadowmapFilter" = "HardShadow" }Pass {ZWrite Off ZTest Always Cull Off // 禁用深度寫入和剔除CGPROGRAM#pragma vertex vert#pragma fragment frag_hard#pragma multi_compile_shadowcollector // 多編譯陰影收集器變體// 使用頂點著色器傳遞的中間數據計算相機空間坐標inline float3 computeCameraSpacePosFromDepth(v2f i) {return computeCameraSpacePosFromDepthAndVSInfo(i);}ENDCG}}
1.vert
v2f vert (appdata v)
{v2f o;UNITY_SETUP_INSTANCE_ID(v); // 如果使用了GPU實例化技術,則設置實例ID,以便在頂點著色器中正確訪問實例相關的數據。UNITY_TRANSFER_INSTANCE_ID(v, o); // 將實例ID從輸入結構體傳輸到輸出結構體,使得片元著色器可以訪問這個ID。UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // 初始化與立體渲染相關的輸出數據,確保左右眼圖像正確生成。float4 clipPos;#if defined(STEREO_CUBEMAP_RENDER_ON) // 如果定義了STEREO_CUBEMAP_RENDER_ON宏,表示正在對立體環境貼圖進行渲染。clipPos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, v.vertex)); // 先將頂點坐標從對象空間轉換到世界空間,然后轉換到裁剪空間。
#else // 否則,默認情況下的處理方式。clipPos = UnityObjectToClipPos(v.vertex); // 直接將頂點坐標從對象空間轉換為裁剪空間。
#endifo.pos = clipPos; // 將計算得到的裁剪空間坐標賦值給輸出結構體的pos成員。o.uv.xy = v.texcoord; // 從輸入結構體中獲取紋理坐標,并賦值給輸出結構體的uv成員的xy分量。// unity_CameraInvProjection at the PS level. o.uv.zw = ComputeNonStereoScreenPos(clipPos); // 計算屏幕空間位置,并賦值給uv成員的zw分量,注意這里指的是非立體渲染的情況。// Perspective case
#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)// 如果啟用了立體渲染的實例化或多重視圖功能,根據當前是左眼還是右眼選擇正確的光線向量。o.ray = unity_StereoEyeIndex == 0 ? v.ray0 : v.ray1;
#else// 默認情況下,直接使用傳入的光線向量。o.ray = v.ray;
#endif
}
2.frag_hard
fixed4 frag_hard (v2f i) : SV_Target
{UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); // 在頂點處理后設置立體視覺的眼睛索引,這對于正確采樣陰影貼圖紋理數組中的對應切片是必需的。float4 wpos; // 聲明一個float4類型的變量wpos,用于存儲世界空間位置。float3 vpos; // 聲明一個float3類型的變量vpos,用于存儲相機空間位置。#if defined(STEREO_CUBEMAP_RENDER_ON) // 如果定義了STEREO_CUBEMAP_RENDER_ON宏,則表示正在進行立體全景圖渲染。// 使用提供的世界坐標紋理來獲取世界空間位置wpos.xyz = tex2D(_ODSWorldTexture, i.uv.xy).xyz; // 從_ODSWorldTexture紋理中根據uv坐標采樣得到世界空間位置的xyz分量。wpos.w = 1.0f; // 設置w分量為1.0f,以確保正確的齊次坐標轉換。// 將世界坐標轉換為相機空間坐標vpos = mul(unity_WorldToCamera, wpos).xyz; // 通過乘以unity_WorldToCamera矩陣將wpos從世界空間轉換到相機空間。
#else // 如果沒有啟用立體全景圖渲染。// 計算相機空間的位置vpos = computeCameraSpacePosFromDepth(i); // 根據深度信息計算相機空間位置。// 將相機空間位置轉換回世界空間位置wpos = mul (unity_CameraToWorld, float4(vpos,1)); // 通過乘以unity_CameraToWorld矩陣將vpos從相機空間轉換回到世界空間。
#endif// 獲取級聯權重fixed4 cascadeWeights = GET_CASCADE_WEIGHTS(wpos, vpos.z); // 根據世界空間位置和深度計算級聯陰影映射的權重。// 獲取陰影坐標float4 shadowCoord = GET_SHADOW_COORDINATES(wpos, cascadeWeights); // 根據世界空間位置和級聯權重計算陰影坐標。// 執行單次采樣以確定是否處于陰影中fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord); // 使用陰影坐標在_ShadowMapTexture上進行采樣,判斷當前像素是否處于陰影中。// 根據陰影結果混合顏色shadow = lerp(_LightShadowData.r, 1.0, shadow); // 使用線性插值(lerp)函數,基于陰影結果混合光照陰影數據和全亮的顏色。fixed4 res = shadow; // 將shadow結果賦值給輸出顏色res。return res; // 返回最終的顏色結果。
}
3.computeCameraSpacePosFromDepthAndVSInfo
/**
* 根據深度紋理和視圖相關的參數計算一個給定點在相機空間中的坐標
*/
inline float3 computeCameraSpacePosFromDepthAndVSInfo(v2f i)
{// 從_CameraDepthTexture紋理中根據uv坐標采樣得到當前像素的深度值zdepth。float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);// 將非線性深度值轉換為0到1范圍內的線性深度值。對于正交投影,直接使用原始深度值zdepth。// unity_OrthoParams.w用來區分是否是正交投影:0表示透視投影,1表示正交投影。float depth = lerp(Linear01Depth(zdepth), zdepth, unity_OrthoParams.w);#if defined(UNITY_REVERSED_Z)// 如果定義了UNITY_REVERSED_Z宏,則反轉深度值,因為Unity在某些平臺上使用反向Z緩沖區。zdepth = 1 - zdepth;
#endif// 計算視角位置,針對透視投影的情況:// 使用i.ray(從頂點著色器傳過來的光線方向)乘以depth(深度值),得到視角下的位置vposPersp。float3 vposPersp = i.ray * depth;// 針對正交投影的情況:// 使用線性插值lerp函數,基于zdepth在i.orthoPosNear(近裁剪面位置)和i.orthoPosFar(遠裁剪面位置)之間進行插值,得到視角下的位置vposOrtho。float3 vposOrtho = lerp(i.orthoPosNear, i.orthoPosFar, zdepth);// 根據是否為正交投影選擇合適的視角位置:// unity_OrthoParams.w為1時選擇vposOrtho(正交投影情況),否則選擇vposPersp(透視投影情況)。float3 camPos = lerp(vposPersp, vposOrtho, unity_OrthoParams.w);// 返回計算得到的相機空間坐標。return camPos.xyz;
}
4.GET_CASCADE_WEIGHTS
// 如果定義了SHADOWS_SPLIT_SPHERES宏,則使用getCascadeWeights_splitSpheres函數計算級聯權重。
#if defined (SHADOWS_SPLIT_SPHERES)// 定義宏GET_CASCADE_WEIGHTS,當調用時將使用世界坐標wpos作為參數調用getCascadeWeights_splitSpheres函數。#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights_splitSpheres(wpos)
#else// 否則,默認情況下使用getCascadeWeights函數計算級聯權重,該函數需要世界坐標wpos和深度值z作為參數。#define GET_CASCADE_WEIGHTS(wpos, z) getCascadeWeights(wpos, z)
#endif// 如果定義了SHADOWS_SINGLE_CASCADE宏,則處理單個級聯的情況。
#if defined (SHADOWS_SINGLE_CASCADE)// 定義宏GET_SHADOW_COORDINATES,當調用時將僅使用世界坐標wpos作為參數調用getShadowCoord_SingleCascade函數。#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord_SingleCascade(wpos)
#else// 否則,默認情況下使用getShadowCoord函數獲取陰影坐標,該函數需要世界坐標wpos和級聯權重cascadeWeights作為參數。#define GET_SHADOW_COORDINATES(wpos,cascadeWeights) getShadowCoord(wpos,cascadeWeights)
#endif
5.getCascadeWeights_splitSpheres
/*** 基于片段的世界位置以及每個級聯分割球體的位置來獲取級聯權重。* 返回一個僅有一個組件設置為對應適當級聯的float4值。** @param wpos 片元的世界坐標。* @return 四個浮點數,每個代表一個級聯是否適用(0或1)。*/
inline fixed4 getCascadeWeights_splitSpheres(float3 wpos)
{// 計算當前世界位置到每個分割球體中心的距離向量。float3 fromCenter0 = wpos.xyz - unity_ShadowSplitSpheres[0].xyz;float3 fromCenter1 = wpos.xyz - unity_ShadowSplitSpheres[1].xyz;float3 fromCenter2 = wpos.xyz - unity_ShadowSplitSpheres[2].xyz;float3 fromCenter3 = wpos.xyz - unity_ShadowSplitSpheres[3].xyz;// 計算這些距離向量的平方長度。float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));// 如果距離平方小于相應的分割球體半徑平方,則認為該級聯適用。fixed4 weights = float4(distances2 < unity_ShadowSplitSqRadii);// 確保只有一個級聯被選中,通過飽和減法消除其他級聯的影響。weights.yzw = saturate(weights.yzw - weights.xyz);return weights;
}
6.getCascadeWeights
/*** 根據片段的世界位置和深度值獲取級聯權重。* 返回一個僅有一個組件設置為對應適當級聯的float4值。** @param wpos 片元的世界坐標。* @param z 深度值。* @return 四個浮點數,每個代表一個級聯是否適用(0或1)。*/
inline fixed4 getCascadeWeights(float3 wpos, float z)
{// 檢查深度z是否大于等于每個級聯的近裁剪面距離。fixed4 zNear = float4(z >= _LightSplitsNear);// 檢查深度z是否小于每個級聯的遠裁剪面距離。fixed4 zFar = float4(z < _Light_SplitsFar);// 計算級聯權重,只有在z同時滿足zNear和zFar條件時,權重才為1。fixed4 weights = zNear * zFar;return weights;
}
7.getShadowCoord_SingleCascade
/*** 同getShadowCoord函數,但針對單個級聯進行了優化。** @param wpos 片元的世界坐標。* @return 針對單個級聯優化后的陰影貼圖坐標。*/
inline float4 getShadowCoord_SingleCascade(float4 wpos)
{// 直接將世界坐標轉換為第一個級聯的陰影坐標,并設置w分量為0。return float4(mul(unity_WorldToShadow[0], wpos).xyz, 0);
}
8.getShadowCoord
/*** 根據給定的世界位置和z深度返回陰影映射坐標。* 這些坐標屬于包含所有級聯的地圖的陰影映射圖集。** @param wpos 片元的世界坐標。* @param cascadeWeights 級聯權重。* @return 陰影貼圖坐標。*/
inline float4 getShadowCoord(float4 wpos, fixed4 cascadeWeights)
{// 將世界坐標轉換為每個級聯的陰影坐標。float3 sc0 = mul(unity_WorldToShadow[0], wpos).xyz;float3 sc1 = mul(unity_WorldToShadow[1], wpos).xyz;float3 sc2 = mul(unity_WorldToShadow[2], wpos).xyz;float3 sc3 = mul(unity_WorldToShadow[3], wpos).xyz;// 根據級聯權重混合這些陰影坐標。float4 shadowMapCoordinate = float4(sc0 * cascadeWeights[0] + sc1 * cascadeWeights[1] + sc2 * cascadeWeights[2] + sc3 * cascadeWeights[3], 1);
#if defined(UNITY_REVERSED_Z)// 如果啟用了反向Z緩沖區,則調整z坐標以避免精度問題。float noCascadeWeights = 1 - dot(cascadeWeights, float4(1, 1, 1, 1));shadowMapCoordinate.z += noCascadeWeights;
#endifreturn shadowMapCoordinate;
}
三、HardShadow_FORCE_INV_PROJECTION_IN_PS
// ----------------------------------------------------------------------------------------// 子著色器:硬陰影(強制PS階段逆投影,通用但精度較低)SubShader {Tags{ "ShadowmapFilter" = "HardShadow_FORCE_INV_PROJECTION_IN_PS" }Pass{ZWrite Off ZTest Always Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag_hard#pragma multi_compile_shadowcollector// 在PS階段使用逆投影矩陣計算坐標(兼容性更高但性能較低)inline float3 computeCameraSpacePosFromDepth(v2f i) {return computeCameraSpacePosFromDepthAndInvProjMat(i);}ENDCG}}
1.vert
同HardShadow
2.frag_hard
同HardShadow
3.computeCameraSpacePosFromDepthAndInvProjMat
/**
* 從深度信息和逆投影矩陣獲取相機空間坐標。
*/
inline float3 computeCameraSpacePosFromDepthAndInvProjMat(v2f i)
{// 使用提供的_CameraDepthTexture紋理和uv坐標采樣得到當前像素的深度值zdepth。float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);#if defined(UNITY_REVERSED_Z)// 如果定義了UNITY_REVERSED_Z宏,則反轉深度值,因為Unity在某些平臺上使用反向Z緩沖區。zdepth = 1 - zdepth;#endif// 視角位置計算,適用于傾斜裁剪的投影情況:// 這種方法不如通過插值光線和深度計算的方法精確或快速,但它能處理更復雜的投影方式。// 根據i.uv.zw(通常包含未變換的屏幕空間坐標)和zdepth構造clipPos。float4 clipPos = float4(i.uv.zw, zdepth, 1.0);// 將clipPos轉換為NDC(標準化設備坐標),范圍從[0,1]轉換到[-1,1]。clipPos.xyz = 2.0f * clipPos.xyz - 1.0f;// 使用unity_CameraInvProjection矩陣將裁剪空間坐標轉換回相機空間坐標。float4 camPos = mul(unity_CameraInvProjection, clipPos);// 透視除法:將xyz分量除以w分量,得到最終的相機空間坐標。camPos.xyz /= camPos.w;// 因為相機空間中的z軸方向通常是相反的,所以需要對z分量取反。camPos.z *= -1;// 返回計算得到的相機空間坐標。return camPos.xyz;
}
四、PCF_SOFT
SubShader {Tags {"ShadowmapFilter" = "PCF_SOFT"}Pass {ZWrite Off ZTest Always Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag_pcfSoft#pragma multi_compile_shadowcollector#pragma target 3.0 // 需要SM 3.0 支持// 使用頂點著色器傳遞的中間數據計算坐標inline float3 computeCameraSpacePosFromDepth(v2f i) {return computeCameraSpacePosFromDepthAndVSInfo(i);}ENDCG}}
1.vert
同HardShadow
2.frag_pcfSoft
/*** 軟陰影 (SM 3.0)*/
fixed4 frag_pcfSoft(v2f i) : SV_Target // 定義片元著色器函數frag_pcfSoft,接收一個v2f類型的輸入結構體i,并返回一個fixed4類型的結果。
{UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); // 在頂點處理后設置立體視覺的眼睛索引,這對于正確采樣陰影貼圖紋理數組中的對應切片是必需的。float4 wpos; // 聲明一個float4類型的變量wpos,用于存儲世界空間位置。float3 vpos; // 聲明一個float3類型的變量vpos,用于存儲相機空間位置。#if defined(STEREO_CUBEMAP_RENDER_ON) // 如果定義了STEREO_CUBEMAP_RENDER_ON宏,則表示正在進行立體全景圖渲染。wpos.xyz = tex2D(_ODSWorldTexture, i.uv.xy).xyz; // 使用提供的_ODSWorldTexture紋理和uv坐標采樣得到世界空間位置的xyz分量。wpos.w = 1.0f; // 設置w分量為1.0f,以確保正確的齊次坐標轉換。vpos = mul(unity_WorldToCamera, wpos).xyz; // 將世界坐標轉換為相機空間坐標。
#elsevpos = computeCameraSpacePosFromDepth(i); // 根據深度信息計算相機空間位置。// 樣本屬于的級聯wpos = mul(unity_CameraToWorld, float4(vpos,1)); // 將相機空間位置轉換回世界空間位置。
#endiffixed4 cascadeWeights = GET_CASCADE_WEIGHTS(wpos, vpos.z); // 獲取級聯權重,確定當前像素屬于哪個陰影級聯。float4 coord = GET_SHADOW_COORDINATES(wpos, cascadeWeights); // 計算陰影坐標。float3 receiverPlaneDepthBias = 0.0;
#ifdef UNITY_USE_RECEIVER_PLANE_BIAS// 接收平面深度偏移:需要基于第一個級聯中的陰影坐標計算,否則在級聯邊界處的導數會出錯。float3 coordCascade0 = getShadowCoord_SingleCascade(wpos); // 獲取單個級聯的陰影坐標。float biasMultiply = dot(cascadeWeights, unity_ShadowCascadeScales); // 根據級聯權重計算偏移乘數。receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coordCascade0.xyz, biasMultiply); // 計算接收平面深度偏移。
#endif#if defined(SHADER_API_MOBILE)half shadow = UnitySampleShadowmap_PCF5x5(coord, receiverPlaneDepthBias); // 對于移動平臺,使用5x5的PCF進行陰影采樣。
#elsehalf shadow = UnitySampleShadowmap_PCF7x7(coord, receiverPlaneDepthBias); // 對于其他平臺,使用7x7的PCF進行陰影采樣。
#endifshadow = lerp(_LightShadowData.r, 1.0f, shadow); // 使用線性插值混合陰影結果。// 如果啟用了級聯混合并且不是使用分割球或單一級聯的情況,則在此進行級聯間的混合。//// 目前不支持分割球,并且當只有一個級聯時不需要混合。
#if UNITY_USE_CASCADE_BLENDING && !defined(SHADOWS_SPLIT_SPHERES) && !defined(SHADOWS_SINGLE_CASCADE)half4 z4 = (float4(vpos.z,vpos.z,vpos.z,vpos.z) - _LightSplitsNear) / (_LightSplitsFar - _LightSplitsNear); // 計算z值在各個級聯中的比例。half alpha = dot(z4 * cascadeWeights, half4(1,1,1,1)); // 計算當前像素所屬的級聯alpha值。UNITY_BRANCHif (alpha > 1 - UNITY_CASCADE_BLEND_DISTANCE) // 如果alpha值超過指定范圍,則進行級聯混合。{// 將alpha調整到0..1范圍內,以便在混合距離內平滑過渡。alpha = (alpha - (1 - UNITY_CASCADE_BLEND_DISTANCE)) / UNITY_CASCADE_BLEND_DISTANCE;// 采樣下一個級聯cascadeWeights = fixed4(0, cascadeWeights.xyz); // 更新級聯權重,指向下一個級聯。coord = GET_SHADOW_COORDINATES(wpos, cascadeWeights); // 重新獲取陰影坐標。#ifdef UNITY_USE_RECEIVER_PLANE_BIASbiasMultiply = dot(cascadeWeights, unity_ShadowCascadeScales); // 重新計算偏移乘數。receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coordCascade0.xyz, biasMultiply); // 重新計算接收平面深度偏移。
#endifhalf shadowNextCascade = UnitySampleShadowmap_PCF3x3(coord, receiverPlaneDepthBias); // 對下一個級聯進行陰影采樣。shadowNextCascade = lerp(_LightShadowData.r, 1.0f, shadowNextCascade); // 混合陰影結果。shadow = lerp(shadow, shadowNextCascade, alpha); // 根據alpha值在兩個級聯之間進行線性插值。}
#endifreturn shadow; // 返回最終的陰影結果。
}
3.computeCameraSpacePosFromDepthAndVSInfo
同HardShadow
五、PCF_SOFT_FORCE_INV_PROJECTION_IN_PS
SubShader{Tags{ "ShadowmapFilter" = "PCF_SOFT_FORCE_INV_PROJECTION_IN_PS" }Pass{ZWrite Off ZTest Always Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag_pcfSoft#pragma multi_compile_shadowcollector#pragma target 3.0// 在PS階段使用逆投影矩陣計算坐標inline float3 computeCameraSpacePosFromDepth(v2f i) {return computeCameraSpacePosFromDepthAndInvProjMat(i);}ENDCG}}
1.vert
同HardShadow
2.frag_pcfSoft
同PCF_SOFT
3.computeCameraSpacePosFromDepthAndInvProjMat
同HardShadow_FORCE_INV_PROJECTION_IN_PS