#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) #if defined(_MAIN_LIGHT_SHADOWS) || defined(_MAIN_LIGHT_SHADOWS_CASCADE) || defined(_MAIN_LIGHT_SHADOWS_SCREEN) #define MAIN_LIGHT_CALCULATE_SHADOWS #if defined(_MAIN_LIGHT_SHADOWS) || (defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)) #define REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR #endif #endif #if defined(_ADDITIONAL_LIGHT_SHADOWS) #define ADDITIONAL_LIGHT_CALCULATE_SHADOWS #endif #endif #if defined(UNITY_DOTS_INSTANCING_ENABLED) && !defined(USE_LEGACY_LIGHTMAPS) // ^ GPU-driven rendering is enabled, and we haven't opted-out from lightmap // texture arrays. This minimizes batch breakages, but texture arrays aren't // supported in a performant way on all GPUs. #define SHADOWMASK_NAME unity_ShadowMasks #define SHADOWMASK_SAMPLER_NAME samplerunity_ShadowMasks #define SHADOWMASK_SAMPLE_EXTRA_ARGS , unity_LightmapIndex.x #else // ^ Lightmaps are not bound as texture arrays, but as individual textures. The // batch is broken every time lightmaps are changed, but this is well-supported // on all GPUs. #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) #define SAMPLE_SHADOWMASK(uv) unity_ProbesOcclusion; #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 TEXTURE2D_X(_ScreenSpaceShadowmapTexture); 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) #endif // 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) #if defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) #if !USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA // 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 #endif #endif #ifndef SHADER_API_GLES3 CBUFFER_END #endif #if defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA StructuredBuffer _AdditionalShadowParams_SSBO; // Per-light data - TODO: test if splitting _AdditionalShadowParams_SSBO[lightIndex].w into a separate StructuredBuffer buffer is faster StructuredBuffer _AdditionalLightsWorldToShadow_SSBO; // Per-shadow-slice-data - A shadow casting light can have 6 shadow slices (if it's a point light) #endif #endif // x: depth bias, // y: normal bias, // z: light type (Spot = 0, Directional = 1, Point = 2, Area/Rectangle = 3, Disc = 4, Pyramid = 5, Box = 6, Tube = 7) // w: unused float4 _ShadowBias; half IsSpotLight() { return round(_ShadowBias.z) == 0.0 ? 1 : 0; } half IsDirectionalLight() { return round(_ShadowBias.z) == 1.0 ? 1 : 0; } half IsPointLight() { return round(_ShadowBias.z) == 2.0 ? 1 : 0; } #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 = half4(_MainLightShadowOffset0); shadowSamplingData.shadowOffset1 = half4(_MainLightShadowOffset1); // shadowmapSize is used in SampleShadowmapFiltered otherwise shadowSamplingData.shadowmapSize = _MainLightShadowmapSize; shadowSamplingData.softShadowQuality = half(_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 half4(_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) { half4 results; #if defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA results = _AdditionalShadowParams_SSBO[lightIndex]; #else results = _AdditionalShadowParams[lightIndex]; results.w = lightIndex < 0 ? -1 : results.w; #endif #else // Same defaults as set in AdditionalLightsShadowCasterPass.cs return half4(0, 0, 0, -1); #endif return results; } 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); #if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED) half attenuation = SAMPLE_TEXTURE2D_ARRAY(_ScreenSpaceShadowmapTexture, sampler_PointClamp, shadowCoord.xy, unity_StereoEyeIndex).x; #else half attenuation = half(SAMPLE_TEXTURE2D(_ScreenSpaceShadowmapTexture, sampler_PointClamp, shadowCoord.xy).x); #endif return attenuation; } real SampleShadowmapFilteredLowQuality(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData) { // 4-tap hardware comparison real4 attenuation4; 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))); return dot(attenuation4, real(0.25)); } real SampleShadowmapFilteredMediumQuality(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData) { real fetchesWeights[9]; real2 fetchesUV[9]; SampleShadow_ComputeSamples_Tent_5x5(samplingData.shadowmapSize, shadowCoord.xy, fetchesWeights, fetchesUV); 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)); } real SampleShadowmapFilteredHighQuality(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData) { real fetchesWeights[16]; real2 fetchesUV[16]; SampleShadow_ComputeSamples_Tent_7x7(samplingData.shadowmapSize, shadowCoord.xy, fetchesWeights, fetchesUV); 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)); } real SampleShadowmapFiltered(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData) { real attenuation = real(1.0); if (samplingData.softShadowQuality == SOFT_SHADOW_QUALITY_LOW) { attenuation = SampleShadowmapFilteredLowQuality(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData); } else if(samplingData.softShadowQuality == SOFT_SHADOW_QUALITY_MEDIUM) { attenuation = SampleShadowmapFilteredMediumQuality(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData); } else // SOFT_SHADOW_QUALITY_HIGH { attenuation = SampleShadowmapFilteredHighQuality(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData); } return attenuation; } real SampleShadowmap(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float4 shadowCoord, ShadowSamplingData samplingData, half4 shadowParams, bool isPerspectiveProjection = true) { // 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 attenuation = real(SAMPLE_TEXTURE2D_SHADOW(ShadowMap, sampler_ShadowMap, shadowCoord.xyz)); #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; float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3)); 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) { #ifdef _MAIN_LIGHT_SHADOWS_CASCADE half cascadeIndex = ComputeCascadeIndex(positionWS); #else half cascadeIndex = half(0.0); #endif float4 shadowCoord = mul(_MainLightWorldToShadow[cascadeIndex], float4(positionWS, 1.0)); return float4(shadowCoord.xyz, 0); } half MainLightRealtimeShadow(float4 shadowCoord) { #if !defined(MAIN_LIGHT_CALCULATE_SHADOWS) return half(1.0); #elif defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT) return SampleScreenSpaceShadowmap(shadowCoord); #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; } #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA float4 shadowCoord = mul(_AdditionalLightsWorldToShadow_SSBO[shadowSliceIndex], float4(positionWS, 1.0)); #else float4 shadowCoord = mul(_AdditionalLightsWorldToShadow[shadowSliceIndex], float4(positionWS, 1.0)); #endif return SampleShadowmap(TEXTURE2D_ARGS(_AdditionalLightsShadowmapTexture, sampler_LinearClampCompare), shadowCoord, shadowSamplingData, shadowParams, true); #else return half(1.0); #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 return half(1.0); #endif } half MixRealtimeAndBakedShadows(half realtimeShadow, half bakedShadow, half shadowFade) { #if defined(LIGHTMAP_SHADOW_MIXING) return min(lerp(realtimeShadow, 1, shadowFade), bakedShadow); #else return lerp(realtimeShadow, bakedShadow, shadowFade); #endif } 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; } 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 return MixRealtimeAndBakedShadows(realtimeShadow, bakedShadow, shadowFade); } half AdditionalLightShadow(int lightIndex, float3 positionWS, half3 lightDirection, half4 shadowMask, half4 occlusionProbeChannels) { half realtimeShadow = AdditionalLightRealtimeShadow(lightIndex, positionWS, lightDirection); #ifdef CALCULATE_BAKED_SHADOWS half bakedShadow = BakedShadow(shadowMask, occlusionProbeChannels); #else half bakedShadow = half(1.0); #endif #ifdef ADDITIONAL_LIGHT_CALCULATE_SHADOWS half shadowFade = GetAdditionalLightShadowFade(positionWS); #else half shadowFade = half(1.0); #endif return MixRealtimeAndBakedShadows(realtimeShadow, bakedShadow, shadowFade); } float4 GetShadowCoord(VertexPositionInputs vertexInput) { #if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT) return ComputeScreenPos(vertexInput.positionCS); #else return TransformWorldToShadowCoord(vertexInput.positionWS); #endif } 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; } float4 ApplyShadowClamping(float4 positionCS) { #if UNITY_REVERSED_Z float clamped = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); #else float clamped = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); #endif // The current implementation of vertex clamping in Universal RP is the same as in Unity Built-In RP. // We follow the same convention in Universal RP where it's only enabled for Directional Lights // (see: Shadows.cpp::RenderShadowMaps() #L2161-L2162) // (see: Shadows.cpp::RenderShadowMaps() #L2086-L2102) // (see: Shadows.cpp::PrepareStateForShadowMap() #L1685-L1686) positionCS.z = lerp(positionCS.z, clamped, IsDirectionalLight()); return positionCS; } /////////////////////////////////////////////////////////////////////////////// // 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 half(_MainLightShadowData.x); } // Deprecated: Use GetAdditionalLightShadowParams instead. half GetAdditionalLightShadowStrenth(int lightIndex) { #if defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA return half(_AdditionalShadowParams_SSBO[lightIndex].x); #else return half(_AdditionalShadowParams[lightIndex].x); #endif #else return half(1.0); #endif } // Deprecated: Use SampleShadowmap that takes shadowParams instead of strength. real SampleShadowmap(float4 shadowCoord, TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), ShadowSamplingData samplingData, half shadowStrength, bool isPerspectiveProjection = true) { half4 shadowParams = half4(shadowStrength, 1.0, 0.0, 0.0); return SampleShadowmap(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData, shadowParams, isPerspectiveProjection); } // 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