151 lines
6.9 KiB
HLSL
151 lines
6.9 KiB
HLSL
#ifndef BADDOG_AREA_LIGHT_SHADOWS_INCLUDED
|
|
#define BADDOG_AREA_LIGHT_SHADOWS_INCLUDED
|
|
|
|
// URP shadow sampling utilities
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
|
|
#include "Packages/com.baddog.rendering.arealight/Shaders/Include/HDPCSS.hlsl"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Area Light Shadow Data
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Maximum number of area lights that can cast shadows
|
|
#define MAX_AREA_LIGHT_SHADOW_COUNT 8
|
|
|
|
// Area Light Shadow Atlas
|
|
TEXTURE2D_SHADOW(_AreaLightsShadowmapTexture);
|
|
|
|
// Area Light Shadow Atlas Size
|
|
// x: 1.0 / width (texel size)
|
|
// y: 1.0 / height (texel size)
|
|
// z: width (resolution)
|
|
// w: height (resolution)
|
|
float4 _AreaLightShadowmapSize;
|
|
|
|
// Area Light Shadow Parameters (per area light, indexed by areaLightIndex)
|
|
// x: shadow strength [0,1]
|
|
// y: soft shadow (1.0 for soft, 0.0 for hard) - reserved for future use
|
|
// z: light type identifier (for shader variants) - reserved for future use
|
|
// w: areaLightIndex (same as array index, or -1 if no shadow)
|
|
float4 _AreaLightShadowParams[MAX_AREA_LIGHT_SHADOW_COUNT];
|
|
|
|
// Area Light World-to-Shadow Matrices (per area light, indexed by areaLightIndex)
|
|
float4x4 _AreaLightsWorldToShadow[MAX_AREA_LIGHT_SHADOW_COUNT];
|
|
|
|
// PCSS Parameters for Area Light Shadows
|
|
// x: shadowSoftness (world space units)
|
|
// y: blockerSampleCount
|
|
// z: filterSampleCount
|
|
// w: rangeScale (for directional lights, 1.0 for area lights)
|
|
float4 _PCSSAdditionalLightParams;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// PCSS Support Function (if not provided by URP Shadows.hlsl)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
real SampleShadowmapFilteredPCSS(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData, float4 pcssParams, float3 positionWS, bool isPerspectiveProjection)
|
|
{
|
|
// Generate jitter for PCSS using screen pixel coordinates
|
|
float4 clipPos = TransformWorldToHClip(positionWS);
|
|
float2 screenPos = ComputeScreenPos(clipPos).xy * _ScreenParams.xy;
|
|
float sampleJitterAngle = InterleavedGradientNoise(screenPos, 0) * 2.0 * PI;
|
|
float2 sampleJitter = float2(sin(sampleJitterAngle), cos(sampleJitterAngle));
|
|
|
|
// Extract PCSS parameters
|
|
float shadowSoftness = pcssParams.x;
|
|
int blockerSampleCount = (int)pcssParams.y;
|
|
int filterSampleCount = (int)pcssParams.z;
|
|
float lightRangeScale = pcssParams.w;
|
|
float minFilterRadius = 2.0;
|
|
|
|
// shadowmapSize format: (texelWidth, texelHeight, width, height)
|
|
// We need to convert shadowSoftness (world units) to UV space
|
|
// shadowSoftness is in world units, we convert to texels then to UV
|
|
float lightArea = shadowSoftness * samplingData.shadowmapSize.x; // Convert to UV space using texel size
|
|
float maxLightArea = 0.04; // 4% of UV space
|
|
lightArea = min(lightArea, maxLightArea);
|
|
|
|
// Shadow map bounds (full texture for non-atlas)
|
|
float UMin = 0.0, UMax = 1.0, VMin = 0.0, VMax = 1.0;
|
|
|
|
// Perform PCSS blocker search
|
|
real averageBlockerDepth;
|
|
real numBlockers;
|
|
bool blockerFound = BlockerSearch(averageBlockerDepth, numBlockers, lightArea, shadowCoord.xyz, UMin, UMax, VMin, VMax, sampleJitter, ShadowMap, sampler_PointClamp, blockerSampleCount);
|
|
|
|
// Calculate filter size based on penumbra estimation
|
|
float filterSize = shadowSoftness * (isPerspectiveProjection ? PenumbraSizePunctual(shadowCoord.z, averageBlockerDepth) : PenumbraSizeDirectional(shadowCoord.z, averageBlockerDepth, lightRangeScale));
|
|
filterSize = blockerFound ? max(filterSize, minFilterRadius) : minFilterRadius;
|
|
|
|
// Convert filter size to UV space (filterSize is in world units, multiply by texel size)
|
|
filterSize *= samplingData.shadowmapSize.x;
|
|
|
|
// Perform PCSS filtering
|
|
return PCSS(shadowCoord.xyz, UMin, UMax, VMin, VMax, filterSize, sampleJitter, ShadowMap, sampler_ShadowMap, filterSampleCount);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Area Light Shadow Sampling
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Sample area light shadow for a given area light index
|
|
// areaLightIndex: Index of the area light (matches _AreaLightDataBuffer index)
|
|
// positionWS: World space position to sample shadow at
|
|
// Returns: Shadow attenuation [0,1], where 1 = fully lit, 0 = fully shadowed
|
|
half SampleAreaLightShadow(uint areaLightIndex, float3 positionWS)
|
|
{
|
|
float4 shadowParams = _AreaLightShadowParams[areaLightIndex];
|
|
|
|
// Check if this light has shadow (w >= 0)
|
|
// w contains the areaLightIndex if shadow is enabled, or -1 if disabled
|
|
if (shadowParams.w < 0.0)
|
|
return 1.0;
|
|
|
|
// Transform position to shadow space
|
|
float4 shadowCoord = mul(_AreaLightsWorldToShadow[areaLightIndex], float4(positionWS, 1.0));
|
|
|
|
// Build sampling data compatible with URP shadow sampling
|
|
ShadowSamplingData samplingData;
|
|
samplingData.shadowOffset0 = 0;
|
|
samplingData.shadowOffset1 = 0;
|
|
samplingData.shadowmapSize = _AreaLightShadowmapSize;
|
|
samplingData.softShadowQuality = half(shadowParams.y);
|
|
|
|
// Perspective divide and out-of-bounds handling
|
|
shadowCoord.xyz /= max(shadowCoord.w, 1e-5);
|
|
if (BEYOND_SHADOW_FAR(shadowCoord))
|
|
return 1.0;
|
|
|
|
real attenuation;
|
|
|
|
// Directly call URP shadow filter entry points by BG keywords (no macro remap).
|
|
#if defined(_BG_AREALIGHT_SHADOWS_PCF2X2)
|
|
attenuation = SampleShadowmapFilteredLowQuality(TEXTURE2D_SHADOW_ARGS(_AreaLightsShadowmapTexture, sampler_LinearClampCompare), shadowCoord, samplingData);
|
|
#elif defined(_BG_AREALIGHT_SHADOWS_TENT5X5)
|
|
attenuation = SampleShadowmapFilteredMediumQuality(TEXTURE2D_SHADOW_ARGS(_AreaLightsShadowmapTexture, sampler_LinearClampCompare), shadowCoord, samplingData);
|
|
#elif defined(_BG_AREALIGHT_SHADOWS_TENT7X7)
|
|
attenuation = SampleShadowmapFilteredHighQuality(TEXTURE2D_SHADOW_ARGS(_AreaLightsShadowmapTexture, sampler_LinearClampCompare), shadowCoord, samplingData);
|
|
#elif defined(_BG_AREALIGHT_SHADOWS_PCSS)
|
|
// PCSS for area lights: reuse the PCSS path implemented in Shadows.hlsl,
|
|
// controlled by _PCSSAdditionalLightParams (x: softness, y: blockerSamples, z: filterSamples, w: rangeScale)
|
|
attenuation = SampleShadowmapFilteredPCSS(
|
|
TEXTURE2D_SHADOW_ARGS(_AreaLightsShadowmapTexture, sampler_LinearClampCompare),
|
|
float4(shadowCoord.xyz, 1.0),
|
|
samplingData,
|
|
_PCSSAdditionalLightParams,
|
|
positionWS,
|
|
/*isPerspectiveProjection*/ true);
|
|
#else
|
|
attenuation = real(SAMPLE_TEXTURE2D_SHADOW(_AreaLightsShadowmapTexture, sampler_LinearClampCompare, shadowCoord.xyz));
|
|
#endif
|
|
|
|
// Apply shadow strength
|
|
attenuation = LerpWhiteTo(attenuation, shadowParams.x);
|
|
|
|
return half(attenuation);
|
|
}
|
|
|
|
#endif // BADDOG_AREA_LIGHT_SHADOWS_INCLUDED
|
|
|
|
|