2024-11-01 16:55:46 +08:00
#ifndef UNIVERSAL_SHADOWS_INCLUDED
#define UNIVERSAL_SHADOWS_INCLUDED
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Shadow/ShadowSamplingTent.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/GlobalSamplers.hlsl"
#include "Core.hlsl"
#include "Shadows.deprecated.hlsl"
#define MAX_SHADOW_CASCADES 4
#if !defined(_RECEIVE_SHADOWS_OFF)
2025-07-17 17:21:42 +08:00
#if defined(_MAIN_LIGHT_SHADOWS) || defined(_MAIN_LIGHT_SHADOWS_CASCADE) || defined(_MAIN_LIGHT_SHADOWS_SCREEN)
2024-11-01 16:55:46 +08:00
# define MAIN_LIGHT_CALCULATE_SHADOWS
2025-07-17 17:21:42 +08:00
#if defined(_MAIN_LIGHT_SHADOWS) || (defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT))
2024-11-01 16:55:46 +08:00
# define REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
2025-07-17 17:21:42 +08:00
#endif
#endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
#if defined(_ADDITIONAL_LIGHT_SHADOWS)
2024-11-01 16:55:46 +08:00
# define ADDITIONAL_LIGHT_CALCULATE_SHADOWS
2025-07-17 17:21:42 +08:00
#endif
2024-11-01 16:55:46 +08:00
#endif
#if defined(UNITY_DOTS_INSTANCING_ENABLED)
#define SHADOWMASK_NAME unity_ShadowMasks
#define SHADOWMASK_SAMPLER_NAME samplerunity_ShadowMasks
#define SHADOWMASK_SAMPLE_EXTRA_ARGS , unity_LightmapIndex.x
#else
#define SHADOWMASK_NAME unity_ShadowMask
#define SHADOWMASK_SAMPLER_NAME samplerunity_ShadowMask
#define SHADOWMASK_SAMPLE_EXTRA_ARGS
#endif
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
# define SAMPLE_SHADOWMASK ( uv ) SAMPLE_TEXTURE2D_LIGHTMAP ( SHADOWMASK_NAME , SHADOWMASK_SAMPLER_NAME , uv SHADOWMASK_SAMPLE_EXTRA_ARGS ) ;
#elif !defined (LIGHTMAP_ON)
2025-07-17 17:21:42 +08:00
#define SAMPLE_SHADOWMASK(uv) unity_ProbesOcclusion;
2024-11-01 16:55:46 +08:00
#else
# define SAMPLE_SHADOWMASK ( uv ) half4 ( 1 , 1 , 1 , 1 ) ;
#endif
#define REQUIRES_WORLD_SPACE_POS_INTERPOLATOR
#if defined(LIGHTMAP_ON) || defined(LIGHTMAP_SHADOW_MIXING) || defined(SHADOWS_SHADOWMASK)
#define CALCULATE_BAKED_SHADOWS
#endif
SCREENSPACE_TEXTURE ( _ScreenSpaceShadowmapTexture ) ;
2025-07-17 17:21:42 +08:00
TEXTURE2D ( _SoftShadowMaskTexture ) ;
2024-11-01 16:55:46 +08:00
TEXTURE2D_SHADOW ( _MainLightShadowmapTexture ) ;
TEXTURE2D_SHADOW ( _AdditionalLightsShadowmapTexture ) ;
SAMPLER_CMP ( sampler_LinearClampCompare ) ;
// GLES3 causes a performance regression in some devices when using CBUFFER.
#ifndef SHADER_API_GLES3
CBUFFER_START ( LightShadows )
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
// Last cascade is initialized with a no-op matrix. It always transforms
// shadow coord to half3(0, 0, NEAR_PLANE). We use this trick to avoid
// branching since ComputeCascadeIndex can return cascade index = MAX_SHADOW_CASCADES
float4x4 _MainLightWorldToShadow [ MAX_SHADOW_CASCADES + 1 ] ;
float4 _CascadeShadowSplitSpheres0 ;
float4 _CascadeShadowSplitSpheres1 ;
float4 _CascadeShadowSplitSpheres2 ;
float4 _CascadeShadowSplitSpheres3 ;
float4 _CascadeShadowSplitSphereRadii ;
float4 _MainLightShadowOffset0 ; // xy: offset0, zw: offset1
float4 _MainLightShadowOffset1 ; // xy: offset2, zw: offset3
float4 _MainLightShadowParams ;
// (x: shadowStrength, y: >= 1.0 if soft shadows, 0.0 otherwise, z: main light fade scale, w: main light fade bias)
float4 _MainLightShadowmapSize ; // (xy: 1/width and 1/height, zw: width and height)
float4 _AdditionalShadowOffset0 ; // xy: offset0, zw: offset1
float4 _AdditionalShadowOffset1 ; // xy: offset2, zw: offset3
float4 _AdditionalShadowFadeParams ;
// x: additional light fade scale, y: additional light fade bias, z: 0.0, w: 0.0)
float4 _AdditionalShadowmapSize ; // (xy: 1/width and 1/height, zw: width and height)
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
# if ! USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
2024-11-01 16:55:46 +08:00
// Point lights can use 6 shadow slices. Some mobile GPUs performance decrease drastically with uniform
// blocks bigger than 8kb while others have a 64kb max uniform block size. This number ensures size of buffer
// AdditionalLightShadows stays reasonable. It also avoids shader compilation errors on SHADER_API_GLES30
// devices where max number of uniforms per shader GL_MAX_FRAGMENT_UNIFORM_VECTORS is low (224)
float4 _AdditionalShadowParams [ MAX_VISIBLE_LIGHTS ] ; // Per-light data
float4x4 _AdditionalLightsWorldToShadow [ MAX_VISIBLE_LIGHTS ] ; // Per-shadow-slice-data
2025-07-17 17:21:42 +08:00
# endif
# endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
# ifndef SHADER_API_GLES3
2024-11-01 16:55:46 +08:00
CBUFFER_END
#endif
#if defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS)
2025-07-17 17:21:42 +08:00
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
2024-11-01 16:55:46 +08:00
StructuredBuffer < float4 > _AdditionalShadowParams_SSBO ; // Per-light data - TODO: test if splitting _AdditionalShadowParams_SSBO[lightIndex].w into a separate StructuredBuffer<int> buffer is faster
StructuredBuffer < float4x4 > _AdditionalLightsWorldToShadow_SSBO ; // Per-shadow-slice-data - A shadow casting light can have 6 shadow slices (if it's a point light)
2025-07-17 17:21:42 +08:00
#endif
2024-11-01 16:55:46 +08:00
#endif
float4 _ShadowBias ; // x: depth bias, y: normal bias
#define BEYOND_SHADOW_FAR(shadowCoord) shadowCoord.z <= 0.0 || shadowCoord.z >= 1.0
// Should match: UnityEngine.Rendering.Universal + 1
#define SOFT_SHADOW_QUALITY_OFF half(0.0)
#define SOFT_SHADOW_QUALITY_LOW half(1.0)
#define SOFT_SHADOW_QUALITY_MEDIUM half(2.0)
#define SOFT_SHADOW_QUALITY_HIGH half(3.0)
struct ShadowSamplingData
{
half4 shadowOffset0 ;
half4 shadowOffset1 ;
float4 shadowmapSize ;
half softShadowQuality ;
} ;
ShadowSamplingData GetMainLightShadowSamplingData ( )
{
ShadowSamplingData shadowSamplingData ;
// shadowOffsets are used in SampleShadowmapFiltered for low quality soft shadows.
shadowSamplingData . shadowOffset0 = _MainLightShadowOffset0 ;
shadowSamplingData . shadowOffset1 = _MainLightShadowOffset1 ;
// shadowmapSize is used in SampleShadowmapFiltered otherwise
shadowSamplingData . shadowmapSize = _MainLightShadowmapSize ;
shadowSamplingData . softShadowQuality = _MainLightShadowParams . y ;
return shadowSamplingData ;
}
ShadowSamplingData GetAdditionalLightShadowSamplingData ( int index )
{
ShadowSamplingData shadowSamplingData = ( ShadowSamplingData ) 0 ;
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
// shadowOffsets are used in SampleShadowmapFiltered for low quality soft shadows.
shadowSamplingData . shadowOffset0 = _AdditionalShadowOffset0 ;
shadowSamplingData . shadowOffset1 = _AdditionalShadowOffset1 ;
// shadowmapSize is used in SampleShadowmapFiltered otherwise.
shadowSamplingData . shadowmapSize = _AdditionalShadowmapSize ;
shadowSamplingData . softShadowQuality = _AdditionalShadowParams [ index ] . y ;
# endif
return shadowSamplingData ;
}
// ShadowParams
// x: ShadowStrength
// y: 1.0 if shadow is soft, 0.0 otherwise
half4 GetMainLightShadowParams ( )
{
return _MainLightShadowParams ;
}
// ShadowParams
// x: ShadowStrength
// y: >= 1.0 if shadow is soft, 0.0 otherwise. Higher value for higher quality. (1.0 == low, 2.0 == medium, 3.0 == high)
// z: 1.0 if cast by a point light (6 shadow slices), 0.0 if cast by a spot light (1 shadow slice)
// w: first shadow slice index for this light, there can be 6 in case of point lights. (-1 for non-shadow-casting-lights)
half4 GetAdditionalLightShadowParams ( int lightIndex )
{
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
2025-07-17 17:21:42 +08:00
# if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
2024-11-01 16:55:46 +08:00
return _AdditionalShadowParams_SSBO [ lightIndex ] ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
return _AdditionalShadowParams [ lightIndex ] ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
# else
2025-07-17 17:21:42 +08:00
// Same defaults as set in AdditionalLightsShadowCasterPass.cs
return half4 ( 0 , 0 , 0 , - 1 ) ;
2024-11-01 16:55:46 +08:00
# endif
}
half SampleScreenSpaceShadowmap ( float4 shadowCoord )
{
shadowCoord . xy / = max ( 0.00001 , shadowCoord . w ) ; // Prevent division by zero.
// The stereo transform has to happen after the manual perspective divide
shadowCoord . xy = UnityStereoTransformScreenSpaceTex ( shadowCoord . xy ) ;
2025-07-17 17:21:42 +08:00
# if defined ( UNITY_STEREO_INSTANCING_ENABLED ) || defined ( UNITY_STEREO_MULTIVIEW_ENABLED )
2024-11-01 16:55:46 +08:00
half attenuation = SAMPLE_TEXTURE2D_ARRAY ( _ScreenSpaceShadowmapTexture , sampler_PointClamp , shadowCoord . xy , unity_StereoEyeIndex ) . x ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half attenuation = half ( SAMPLE_TEXTURE2D ( _ScreenSpaceShadowmapTexture , sampler_PointClamp , shadowCoord . xy ) . x ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
return attenuation ;
}
2025-07-17 17:21:42 +08:00
real SampleShadowmapFilteredLowQuality (
TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) , float4 shadowCoord , ShadowSamplingData samplingData )
2024-11-01 16:55:46 +08:00
{
// 4-tap hardware comparison
real4 attenuation4 ;
2025-07-17 17:21:42 +08:00
attenuation4 . x = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
shadowCoord . xyz + float3 ( samplingData . shadowOffset0 . xy , 0 ) ) ) ;
attenuation4 . y = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
shadowCoord . xyz + float3 ( samplingData . shadowOffset0 . zw , 0 ) ) ) ;
attenuation4 . z = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
shadowCoord . xyz + float3 ( samplingData . shadowOffset1 . xy , 0 ) ) ) ;
attenuation4 . w = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
shadowCoord . xyz + float3 ( samplingData . shadowOffset1 . zw , 0 ) ) ) ;
2024-11-01 16:55:46 +08:00
return dot ( attenuation4 , real ( 0.25 ) ) ;
}
2025-07-17 17:21:42 +08:00
real SampleShadowmapFilteredMediumQuality (
TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) , float4 shadowCoord , ShadowSamplingData samplingData )
2024-11-01 16:55:46 +08:00
{
real fetchesWeights [ 9 ] ;
real2 fetchesUV [ 9 ] ;
SampleShadow_ComputeSamples_Tent_5x5 ( samplingData . shadowmapSize , shadowCoord . xy , fetchesWeights , fetchesUV ) ;
2025-07-17 17:21:42 +08:00
return fetchesWeights [ 0 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 0 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 1 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 1 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 2 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 2 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 3 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 3 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 4 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 4 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 5 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 5 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 6 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 6 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 7 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 7 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 8 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 8 ] . xy , shadowCoord . z ) ) ;
2024-11-01 16:55:46 +08:00
}
2025-07-17 17:21:42 +08:00
real SampleShadowmapFilteredHighQuality (
TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) , float4 shadowCoord , ShadowSamplingData samplingData )
2024-11-01 16:55:46 +08:00
{
real fetchesWeights [ 16 ] ;
real2 fetchesUV [ 16 ] ;
SampleShadow_ComputeSamples_Tent_7x7 ( samplingData . shadowmapSize , shadowCoord . xy , fetchesWeights , fetchesUV ) ;
2025-07-17 17:21:42 +08:00
return fetchesWeights [ 0 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 0 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 1 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 1 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 2 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 2 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 3 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 3 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 4 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 4 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 5 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 5 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 6 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 6 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 7 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 7 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 8 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 8 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 9 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 9 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 10 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 10 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 11 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 11 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 12 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 12 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 13 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 13 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 14 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 14 ] . xy , shadowCoord . z ) )
+ fetchesWeights [ 15 ] * SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap ,
float3 ( fetchesUV [ 15 ] . xy , shadowCoord . z ) ) ;
2024-11-01 16:55:46 +08:00
}
2025-07-17 17:21:42 +08:00
real SampleShadowmapFiltered (
TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) , float4 shadowCoord , ShadowSamplingData samplingData )
2024-11-01 16:55:46 +08:00
{
real attenuation = real ( 1.0 ) ;
if ( samplingData . softShadowQuality == SOFT_SHADOW_QUALITY_LOW )
{
2025-07-17 17:21:42 +08:00
attenuation = SampleShadowmapFilteredLowQuality (
TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
2024-11-01 16:55:46 +08:00
}
2025-07-17 17:21:42 +08:00
else if ( samplingData . softShadowQuality == SOFT_SHADOW_QUALITY_MEDIUM )
2024-11-01 16:55:46 +08:00
{
2025-07-17 17:21:42 +08:00
attenuation = SampleShadowmapFilteredMediumQuality (
TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
2024-11-01 16:55:46 +08:00
}
else // SOFT_SHADOW_QUALITY_HIGH
{
2025-07-17 17:21:42 +08:00
attenuation = SampleShadowmapFilteredHighQuality (
TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
2024-11-01 16:55:46 +08:00
}
return attenuation ;
}
2025-07-17 17:21:42 +08:00
real SampleShadowmap (
TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) , float4 shadowCoord , ShadowSamplingData samplingData ,
half4 shadowParams , bool isPerspectiveProjection = true )
2024-11-01 16:55:46 +08:00
{
// Compiler will optimize this branch away as long as isPerspectiveProjection is known at compile time
if ( isPerspectiveProjection )
shadowCoord . xyz / = shadowCoord . w ;
real attenuation ;
real shadowStrength = shadowParams . x ;
// Quality levels are only for platforms requiring strict static branches
# if defined ( _SHADOWS_SOFT_LOW )
attenuation = SampleShadowmapFilteredLowQuality ( TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
# elif defined ( _SHADOWS_SOFT_MEDIUM )
attenuation = SampleShadowmapFilteredMediumQuality ( TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
# elif defined ( _SHADOWS_SOFT_HIGH )
attenuation = SampleShadowmapFilteredHighQuality ( TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
# elif defined ( _SHADOWS_SOFT )
if ( shadowParams . y > SOFT_SHADOW_QUALITY_OFF )
{
attenuation = SampleShadowmapFiltered ( TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData ) ;
}
else
{
attenuation = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap , shadowCoord . xyz ) ) ;
}
# else
2025-07-17 17:21:42 +08:00
attenuation = real ( SAMPLE_TEXTURE2D_SHADOW ( ShadowMap , sampler_ShadowMap , shadowCoord . xyz ) ) ;
2024-11-01 16:55:46 +08:00
# endif
attenuation = LerpWhiteTo ( attenuation , shadowStrength ) ;
// Shadow coords that fall out of the light frustum volume must always return attenuation 1.0
// TODO: We could use branch here to save some perf on some platforms.
return BEYOND_SHADOW_FAR ( shadowCoord ) ? 1.0 : attenuation ;
}
half ComputeCascadeIndex ( float3 positionWS )
{
float3 fromCenter0 = positionWS - _CascadeShadowSplitSpheres0 . xyz ;
float3 fromCenter1 = positionWS - _CascadeShadowSplitSpheres1 . xyz ;
float3 fromCenter2 = positionWS - _CascadeShadowSplitSpheres2 . xyz ;
float3 fromCenter3 = positionWS - _CascadeShadowSplitSpheres3 . xyz ;
2025-07-17 17:21:42 +08:00
float4 distances2 = float4 ( dot ( fromCenter0 , fromCenter0 ) , dot ( fromCenter1 , fromCenter1 ) ,
dot ( fromCenter2 , fromCenter2 ) , dot ( fromCenter3 , fromCenter3 ) ) ;
2024-11-01 16:55:46 +08:00
half4 weights = half4 ( distances2 < _CascadeShadowSplitSphereRadii ) ;
weights . yzw = saturate ( weights . yzw - weights . xyz ) ;
return half ( 4.0 ) - dot ( weights , half4 ( 4 , 3 , 2 , 1 ) ) ;
}
float4 TransformWorldToShadowCoord ( float3 positionWS )
{
2025-07-17 17:21:42 +08:00
# ifdef _MAIN_LIGHT_SHADOWS_CASCADE
2024-11-01 16:55:46 +08:00
half cascadeIndex = ComputeCascadeIndex ( positionWS ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half cascadeIndex = half ( 0.0 ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
float4 shadowCoord = mul ( _MainLightWorldToShadow [ cascadeIndex ] , float4 ( positionWS , 1.0 ) ) ;
return float4 ( shadowCoord . xyz , 0 ) ;
}
half MainLightRealtimeShadow ( float4 shadowCoord )
{
# if ! defined ( MAIN_LIGHT_CALCULATE_SHADOWS )
2025-07-17 17:21:42 +08:00
return half ( 1.0 ) ;
# elif ( defined ( _MAIN_LIGHT_SHADOWS_SCREEN ) && ! defined ( _SURFACE_TYPE_TRANSPARENT ) )
2025-07-17 12:37:05 +08:00
float attenuation = SampleScreenSpaceShadowmap ( shadowCoord ) ;
return attenuation ;
2024-11-01 16:55:46 +08:00
# else
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData ( ) ;
half4 shadowParams = GetMainLightShadowParams ( ) ;
return SampleShadowmap ( TEXTURE2D_ARGS ( _MainLightShadowmapTexture , sampler_LinearClampCompare ) , shadowCoord , shadowSamplingData , shadowParams , false ) ;
# endif
}
// returns 0.0 if position is in light's shadow
// returns 1.0 if position is in light
half AdditionalLightRealtimeShadow ( int lightIndex , float3 positionWS , half3 lightDirection )
{
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
ShadowSamplingData shadowSamplingData = GetAdditionalLightShadowSamplingData ( lightIndex ) ;
half4 shadowParams = GetAdditionalLightShadowParams ( lightIndex ) ;
int shadowSliceIndex = shadowParams . w ;
if ( shadowSliceIndex < 0 )
return 1.0 ;
half isPointLight = shadowParams . z ;
UNITY_BRANCH
if ( isPointLight )
{
// This is a point light, we have to find out which shadow slice to sample from
float cubemapFaceId = CubeMapFaceID ( - lightDirection ) ;
shadowSliceIndex + = cubemapFaceId ;
}
2025-07-17 17:21:42 +08:00
# if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
2024-11-01 16:55:46 +08:00
float4 shadowCoord = mul ( _AdditionalLightsWorldToShadow_SSBO [ shadowSliceIndex ] , float4 ( positionWS , 1.0 ) ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
float4 shadowCoord = mul ( _AdditionalLightsWorldToShadow [ shadowSliceIndex ] , float4 ( positionWS , 1.0 ) ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
return SampleShadowmap ( TEXTURE2D_ARGS ( _AdditionalLightsShadowmapTexture , sampler_LinearClampCompare ) , shadowCoord , shadowSamplingData , shadowParams , true ) ;
# else
2025-07-17 17:21:42 +08:00
return half ( 1.0 ) ;
2024-11-01 16:55:46 +08:00
# endif
}
half GetMainLightShadowFade ( float3 positionWS )
{
float3 camToPixel = positionWS - _WorldSpaceCameraPos ;
float distanceCamToPixel2 = dot ( camToPixel , camToPixel ) ;
float fade = saturate ( distanceCamToPixel2 * float ( _MainLightShadowParams . z ) + float ( _MainLightShadowParams . w ) ) ;
return half ( fade ) ;
}
half GetAdditionalLightShadowFade ( float3 positionWS )
{
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
float3 camToPixel = positionWS - _WorldSpaceCameraPos ;
float distanceCamToPixel2 = dot ( camToPixel , camToPixel ) ;
float fade = saturate ( distanceCamToPixel2 * float ( _AdditionalShadowFadeParams . x ) + float ( _AdditionalShadowFadeParams . y ) ) ;
return half ( fade ) ;
# else
2025-07-17 17:21:42 +08:00
return half ( 1.0 ) ;
2024-11-01 16:55:46 +08:00
# endif
}
half MixRealtimeAndBakedShadows ( half realtimeShadow , half bakedShadow , half shadowFade )
{
2025-07-17 17:21:42 +08:00
# if defined ( LIGHTMAP_SHADOW_MIXING )
2024-11-01 16:55:46 +08:00
return min ( lerp ( realtimeShadow , 1 , shadowFade ) , bakedShadow ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
return lerp ( realtimeShadow , bakedShadow , shadowFade ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
}
half BakedShadow ( half4 shadowMask , half4 occlusionProbeChannels )
{
// Here occlusionProbeChannels used as mask selector to select shadows in shadowMask
// If occlusionProbeChannels all components are zero we use default baked shadow value 1.0
// This code is optimized for mobile platforms:
// half bakedShadow = any(occlusionProbeChannels) ? dot(shadowMask, occlusionProbeChannels) : 1.0h;
half bakedShadow = half ( 1.0 ) + dot ( shadowMask - half ( 1.0 ) , occlusionProbeChannels ) ;
return bakedShadow ;
}
2025-07-17 17:21:42 +08:00
half MainLightShadow ( float4 shadowCoord , float2 screenUV , float3 positionWS , half4 shadowMask ,
half4 occlusionProbeChannels )
2024-11-01 16:55:46 +08:00
{
2025-07-17 17:21:42 +08:00
# if defined ( _SCREEN_SPACE_SHADOW_LOW_RESOLUTION_PCF )
// mip 可能造成更多 PCF
half realtimeShadow = half ( SAMPLE_TEXTURE2D ( _ScreenSpaceShadowmapTexture , sampler_PointClamp , float3 ( screenUV . xy , 2 ) ) . x ) ;
UNITY_BRANCH
if ( abs ( realtimeShadow - 0.5 ) < 0.499 )
{
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData ( ) ;
half4 shadowParams = GetMainLightShadowParams ( ) ;
realtimeShadow = SampleShadowmap ( TEXTURE2D_ARGS ( _MainLightShadowmapTexture , sampler_LinearClampCompare ) , shadowCoord , shadowSamplingData , shadowParams , false ) ;
}
# elif defined ( _SOFTSHADOW_MASK )
half realtimeShadow = half ( SAMPLE_TEXTURE2D ( _SoftShadowMaskTexture , sampler_PointClamp , screenUV . xy ) . x ) ;
2025-07-17 12:37:05 +08:00
UNITY_BRANCH
if ( abs ( realtimeShadow - 0.5 ) < 0.499 )
{
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData ( ) ;
half4 shadowParams = GetMainLightShadowParams ( ) ;
2025-07-17 17:21:42 +08:00
realtimeShadow = SampleShadowmap ( TEXTURE2D_ARGS ( _MainLightShadowmapTexture , sampler_LinearClampCompare ) , shadowCoord , shadowSamplingData , shadowParams , false ) ;
2025-07-17 12:37:05 +08:00
}
2025-07-17 17:21:42 +08:00
# else
half realtimeShadow = MainLightRealtimeShadow ( shadowCoord ) ;
2025-07-17 12:37:05 +08:00
# endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
# ifdef CALCULATE_BAKED_SHADOWS
2024-11-01 16:55:46 +08:00
half bakedShadow = BakedShadow ( shadowMask , occlusionProbeChannels ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half bakedShadow = half ( 1.0 ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
# ifdef MAIN_LIGHT_CALCULATE_SHADOWS
2024-11-01 16:55:46 +08:00
half shadowFade = GetMainLightShadowFade ( positionWS ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half shadowFade = half ( 1.0 ) ;
2025-07-17 17:21:42 +08:00
# endif
return MixRealtimeAndBakedShadows ( realtimeShadow , bakedShadow , shadowFade ) ;
}
half MainLightShadow ( float4 shadowCoord , float3 positionWS , half4 shadowMask , half4 occlusionProbeChannels )
{
half realtimeShadow = MainLightRealtimeShadow ( shadowCoord ) ;
# ifdef CALCULATE_BAKED_SHADOWS
half bakedShadow = BakedShadow ( shadowMask , occlusionProbeChannels ) ;
# else
half bakedShadow = half ( 1.0 ) ;
# endif
# ifdef MAIN_LIGHT_CALCULATE_SHADOWS
half shadowFade = GetMainLightShadowFade ( positionWS ) ;
# else
half shadowFade = half ( 1.0 ) ;
# endif
2024-11-01 16:55:46 +08:00
return MixRealtimeAndBakedShadows ( realtimeShadow , bakedShadow , shadowFade ) ;
}
2025-07-17 17:21:42 +08:00
half AdditionalLightShadow ( int lightIndex , float3 positionWS , half3 lightDirection , half4 shadowMask ,
half4 occlusionProbeChannels )
2024-11-01 16:55:46 +08:00
{
half realtimeShadow = AdditionalLightRealtimeShadow ( lightIndex , positionWS , lightDirection ) ;
2025-07-17 17:21:42 +08:00
# ifdef CALCULATE_BAKED_SHADOWS
2024-11-01 16:55:46 +08:00
half bakedShadow = BakedShadow ( shadowMask , occlusionProbeChannels ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half bakedShadow = half ( 1.0 ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
2025-07-17 17:21:42 +08:00
# ifdef ADDITIONAL_LIGHT_CALCULATE_SHADOWS
2024-11-01 16:55:46 +08:00
half shadowFade = GetAdditionalLightShadowFade ( positionWS ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
half shadowFade = half ( 1.0 ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
return MixRealtimeAndBakedShadows ( realtimeShadow , bakedShadow , shadowFade ) ;
}
float4 GetShadowCoord ( VertexPositionInputs vertexInput )
{
2025-07-17 17:21:42 +08:00
# if defined ( _MAIN_LIGHT_SHADOWS_SCREEN ) && ! defined ( _SURFACE_TYPE_TRANSPARENT )
2024-11-01 16:55:46 +08:00
return ComputeScreenPos ( vertexInput . positionCS ) ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
return TransformWorldToShadowCoord ( vertexInput . positionWS ) ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
}
float3 ApplyShadowBias ( float3 positionWS , float3 normalWS , float3 lightDirection )
{
float invNdotL = 1.0 - saturate ( dot ( lightDirection , normalWS ) ) ;
float scale = invNdotL * _ShadowBias . y ;
// normal bias is negative since we want to apply an inset normal offset
positionWS = lightDirection * _ShadowBias . xxx + positionWS ;
positionWS = normalWS * scale . xxx + positionWS ;
return positionWS ;
}
///////////////////////////////////////////////////////////////////////////////
// Deprecated /
///////////////////////////////////////////////////////////////////////////////
// Renamed -> _MainLightShadowParams
#define _MainLightShadowData _MainLightShadowParams
// Deprecated: Use GetMainLightShadowFade or GetAdditionalLightShadowFade instead.
float GetShadowFade ( float3 positionWS )
{
float3 camToPixel = positionWS - _WorldSpaceCameraPos ;
float distanceCamToPixel2 = dot ( camToPixel , camToPixel ) ;
float fade = saturate ( distanceCamToPixel2 * float ( _MainLightShadowParams . z ) + float ( _MainLightShadowParams . w ) ) ;
return fade * fade ;
}
// Deprecated: Use GetShadowFade instead.
float ApplyShadowFade ( float shadowAttenuation , float3 positionWS )
{
float fade = GetShadowFade ( positionWS ) ;
return shadowAttenuation + ( 1 - shadowAttenuation ) * fade * fade ;
}
// Deprecated: Use GetMainLightShadowParams instead.
half GetMainLightShadowStrength ( )
{
return _MainLightShadowData . x ;
}
// Deprecated: Use GetAdditionalLightShadowParams instead.
half GetAdditionalLightShadowStrenth ( int lightIndex )
{
# if defined ( ADDITIONAL_LIGHT_CALCULATE_SHADOWS )
2025-07-17 17:21:42 +08:00
# if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
2024-11-01 16:55:46 +08:00
return _AdditionalShadowParams_SSBO [ lightIndex ] . x ;
2025-07-17 17:21:42 +08:00
# else
2024-11-01 16:55:46 +08:00
return _AdditionalShadowParams [ lightIndex ] . x ;
2025-07-17 17:21:42 +08:00
# endif
2024-11-01 16:55:46 +08:00
# else
2025-07-17 17:21:42 +08:00
return half ( 1.0 ) ;
2024-11-01 16:55:46 +08:00
# endif
}
// Deprecated: Use SampleShadowmap that takes shadowParams instead of strength.
2025-07-17 17:21:42 +08:00
real SampleShadowmap ( float4 shadowCoord , TEXTURE2D_SHADOW_PARAM ( ShadowMap , sampler_ShadowMap ) ,
ShadowSamplingData samplingData , half shadowStrength , bool isPerspectiveProjection = true )
2024-11-01 16:55:46 +08:00
{
half4 shadowParams = half4 ( shadowStrength , 1.0 , 0.0 , 0.0 ) ;
2025-07-17 17:21:42 +08:00
return SampleShadowmap (
TEXTURE2D_SHADOW_ARGS ( ShadowMap , sampler_ShadowMap ) , shadowCoord , samplingData , shadowParams ,
isPerspectiveProjection ) ;
2024-11-01 16:55:46 +08:00
}
// Deprecated: Use AdditionalLightRealtimeShadow(int lightIndex, float3 positionWS, half3 lightDirection) in Shadows.hlsl instead, as it supports Point Light shadows
half AdditionalLightRealtimeShadow ( int lightIndex , float3 positionWS )
{
return AdditionalLightRealtimeShadow ( lightIndex , positionWS , half3 ( 1 , 0 , 0 ) ) ;
}
#endif