Unity Internal-ScreenSpaceShadows 分析

一、代碼結構

// 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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77085.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77085.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77085.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Token+JWT+Redis 實現鑒權機制

TokenJWTRedis 實現鑒權機制 使用 Token、JWT 和 Redis 來實現鑒權機制是一種常見的做法&#xff0c;尤其適用于分布式應用或微服務架構。下面是一個大致的實現思路&#xff1a; 1. Token 和 JWT 概述 Token&#xff1a;通常是一個唯一的字符串&#xff0c;可以用來標識用戶…

RPC與其他通信技術的區別,以及RPC的底層原理

1、什么是 RPC&#xff1f; 遠程過程調用&#xff08;RPC&#xff09; 是一種協議&#xff0c;它允許程序在不同計算機之間進行通信&#xff0c;讓開發者可以像調用本地函數一樣發起遠程請求。 通過 RPC&#xff0c;開發者無需關注底層網絡細節&#xff0c;能夠更專注于業務邏…

簡潔的 PlantUML 入門教程

評論中太多朋友在問&#xff0c;我的文章中圖例如何完成的。 我一直用plantUML,也推薦大家用&#xff0c;下面給出一個簡潔的PlantUML教程。 &#x1f331; 什么是 PlantUML&#xff1f; PlantUML 是一個用純文本語言畫圖的工具&#xff0c;支持流程圖、時序圖、用例圖、類圖、…

互聯網三高-高性能之JVM調優

1 運行時數據區 JVM運行時數據區是Java虛擬機管理的內存核心模塊&#xff0c;主要分為線程共享和線程私有兩部分。 &#xff08;1&#xff09;線程私有 ① 程序計數器&#xff1a;存儲當前線程執行字節碼指令的地址&#xff0c;用于分支、循環、異常處理等流程控制? ② 虛擬機…

淺談StarRocks 常見問題解析

StarRocks數據庫作為高性能分布式分析數據庫&#xff0c;其常見問題及解決方案涵蓋環境部署、數據操作、系統穩定性、安全管控及生態集成五大核心領域&#xff0c;需確保Linux系統環境、依賴庫及環境變量配置嚴格符合官方要求以避免節點啟動失敗&#xff0c;數據導入需遵循格式…

P1332 血色先鋒隊(BFS)

題目背景 巫妖王的天災軍團終于卷土重來&#xff0c;血色十字軍組織了一支先鋒軍前往諾森德大陸對抗天災軍團&#xff0c;以及一切沾有亡靈氣息的生物。孤立于聯盟和部落的血色先鋒軍很快就遭到了天災軍團的重重包圍&#xff0c;現在他們將主力只好聚集了起來&#xff0c;以抵…

大文件上傳之斷點續傳實現方案與原理詳解

一、實現原理 文件分塊&#xff1a;將大文件切割為固定大小的塊&#xff08;如5MB&#xff09; 進度記錄&#xff1a;持久化存儲已上傳分塊信息 續傳能力&#xff1a;上傳中斷后根據記錄繼續上傳未完成塊 塊校驗機制&#xff1a;通過哈希值驗證塊完整性 合并策略&#xff1a;所…

【動手學深度學習】卷積神經網絡(CNN)入門

【動手學深度學習】卷積神經網絡&#xff08;CNN&#xff09;入門 1&#xff0c;卷積神經網絡簡介2&#xff0c;卷積層2.1&#xff0c;互相關運算原理2.2&#xff0c;互相關運算實現2.3&#xff0c;實現卷積層 3&#xff0c;卷積層的簡單應用&#xff1a;邊緣檢測3.1&#xff0…

Opencv計算機視覺編程攻略-第十一節 三維重建

此處重點討論在特定條件下&#xff0c;重建場景的三維結構和相機的三維姿態的一些應用實現。下面是完整投影公式最通用的表示方式。 在上述公式中&#xff0c;可以了解到&#xff0c;真實物體轉為平面之后&#xff0c;s系數丟失了&#xff0c;因而無法會的三維坐標&#xff0c;…

大廠不再招測試?軟件測試左移開發合理嗎?

&#x1f449;目錄 1 軟件測試發展史 2 測試左移&#xff08;Testing shift left&#xff09; 3 測試右移&#xff08;Testing shift right&#xff09; 4 自動化測試 VS 測試自動化 5 來自 EX 測試的寄語 最近兩年&#xff0c;互聯網大廠的招聘中&#xff0c;測試工程師崗位似…

windows10下PointNet官方代碼Pytorch實現

PointNet模型運行 1.下載源碼并安裝環境 GitCode - 全球開發者的開源社區,開源代碼托管平臺GitCode是面向全球開發者的開源社區,包括原創博客,開源代碼托管,代碼協作,項目管理等。與開發者社區互動,提升您的研發效率和質量。https://gitcode.com/gh_mirrors/po/pointnet.pyto…

git pull 和 git fetch

關于 git pull 和 git fetch 的區別 1. git fetch 作用&#xff1a;從遠程倉庫獲取最新的分支信息和提交記錄&#xff0c;但不會自動合并或修改當前工作目錄中的內容。特點&#xff1a; 它只是更新本地的遠程分支引用&#xff08;例如 remotes/origin/suyuhan&#xff09;&am…

前端開發中的單引號(‘ ‘)、雙引號( )和反引號( `)使用

前端開發中的單引號&#xff08;’ &#xff09;、雙引號&#xff08;" "&#xff09;和反引號&#xff08; &#xff09;使用 在前端開發中&#xff0c;單引號&#xff08;’ &#xff09;、雙引號&#xff08;" "&#xff09;和反引號&#xff08; &…

程序化廣告行業(69/89):DMP與PCP系統核心功能剖析

程序化廣告行業&#xff08;69/89&#xff09;&#xff1a;DMP與PCP系統核心功能剖析 在數字化營銷浪潮中&#xff0c;程序化廣告已成為企業精準觸達目標受眾的關鍵手段。作為行業探索者&#xff0c;我深知其中知識的繁雜與重要性。一直以來&#xff0c;都希望能和大家一同學習…

Amodal3R ,南洋理工推出的 3D 生成模型

Amodal3R 是一款先進的條件式 3D 生成模型&#xff0c;能夠從部分可見的 2D 物體圖像中推斷并重建完整的 3D 結構與外觀。該模型建立在基礎的 3D 生成模型 TRELLIS 之上&#xff0c;通過引入掩碼加權多頭交叉注意力機制與遮擋感知注意力層&#xff0c;利用遮擋先驗知識優化重建…

LLM面試題八

推薦算法工程師面試題 二分類的分類損失函數&#xff1f; 二分類的分類損失函數一般采用交叉熵(Cross Entropy)損失函數&#xff0c;即CE損失函數。二分類問題的CE損失函數可以寫成&#xff1a;其中&#xff0c;y是真實標簽&#xff0c;p是預測標簽&#xff0c;取值為0或1。 …

30天學Java第7天——IO流

概述 基本概念 輸入流&#xff1a;從硬盤到內存。&#xff08;輸入又叫做 讀 read&#xff09;輸出流&#xff1a;從內存到硬盤。&#xff08;輸出又叫做 寫 write&#xff09;字節流&#xff1a;一次讀取一個字節。適合非文本數據&#xff0c;它是萬能的&#xff0c;啥都能讀…

面試可能會遇到的問題回答(嵌入式軟件開發部分)

寫在前面&#xff1a; 博主也是剛入社會的小牛馬&#xff0c;如果下面有寫的不好或者寫錯的地方歡迎大家指出~ 一、四大件基礎知識 1、計算機組成原理 &#xff08;1&#xff09;簡單介紹一下中斷是什么。 ①回答&#xff1a; ②難度系數&#xff1a;★★ ③難點分析&…

層歸一化詳解及在 Stable Diffusion 中的應用分析

在深度學習中&#xff0c;歸一化&#xff08;Normalization&#xff09;技術被廣泛用于提升模型訓練的穩定性和收斂速度。本文將詳細介紹幾種常見的歸一化方式&#xff0c;并重點分析它們在 Stable Diffusion 模型中的實際使用場景。 一、常見的歸一化技術 名稱歸一化維度應用…

深入理解Socket編程:構建簡單的計算器服務器

一、Socket通信基礎 1. Socket通信基本流程 服務器端流程&#xff1a; 創建Socket (socket()) 綁定地址和端口 (bind()) 監聽連接 (listen()) 接受連接 (accept()) 數據通信 (read()/write()) 關閉連接 (close()) 客戶端流程&#xff1a; 創建Socket (socket()) 連接…