// Each #kernel tells which function to compile; you can have many kernels #pragma kernel DeferredDirectionalShadow DEFERRED_DIRECTIONAL=DeferredDirectionalShadow #pragma kernel DeferredDirectionalShadow_Contact DEFERRED_DIRECTIONAL=DeferredDirectionalShadow_Contact ENABLE_CONTACT_SHADOWS #pragma kernel DeferredContactShadow #ifdef SHADER_API_PSSL # pragma argument( scheduler=minpressure ) // instruct the shader compiler to prefer minimizing vgpr usage #endif #include "CoreRP/ShaderLibrary/Common.hlsl" #include "../ShaderVariables.hlsl" #include "../Material/NormalBuffer.hlsl" #include "Lighting.hlsl" #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch //#pragma enable_d3d11_debug_symbols RWTexture2D _DeferredShadowTextureUAV; CBUFFER_START(DeferredShadowParameters) uint _DirectionalShadowIndex; float4 _DirectionalLightDirection; float4 _PunctualLightPosition; float4 _ContactShadowParamsParameters; float4 _ContactShadowParamsParameters2; int _SampleCount; CBUFFER_END #define _ContactShadowLength _ContactShadowParamsParameters.x #define _ContactShadowDistanceScaleFactor _ContactShadowParamsParameters.y #define _ContactShadowFadeEnd _ContactShadowParamsParameters.z #define _ContactShadowFadeOneOverRange _ContactShadowParamsParameters.w #define _ContactShadowOpacity _ContactShadowParamsParameters2.x #define DEFERRED_SHADOW_TILE_SIZE 16 // Return 1.0 if occluded 0.0 if not float4 ScreenSpaceShadowRayCast(float3 positionWS, float3 rayDirection, float rayLength) { uint3 hashInput = uint3(abs(GetAbsolutePositionWS(positionWS)) * 1000); // Dither pattern is shifted by 0.5 because we want to jitter the ray starting position backward and forward (so we need values between -0.5 and 0.5) float ditherBias = 0.5; float dither = GenerateHashedRandomFloat(hashInput) - ditherBias; float3 rayStartWS = positionWS; float3 rayEndWS = rayStartWS + rayDirection * rayLength; float4 rayStartCS = TransformWorldToHClip(rayStartWS); float4 rayEndCS = TransformWorldToHClip(rayEndWS); // Here we compute a ray perpendicular to view space. This is the ray we use to compute the threshold for rejecting samples. // This is done this way so that the threshold is less dependent of ray slope. float4 rayOrthoViewSpace = rayStartCS + mul(GetViewToHClipMatrix(), float4(0, 0, rayLength, 0)); rayOrthoViewSpace = rayOrthoViewSpace / rayOrthoViewSpace.w; rayStartCS.xyz = rayStartCS.xyz / rayStartCS.w; rayEndCS.xyz = rayEndCS.xyz / rayEndCS.w; // Pixel to light ray in clip space. float3 rayCS = rayEndCS.xyz - rayStartCS.xyz; // Depth at the start of the ray float startDepth = rayStartCS.z; // Depth range of the ray float rayDepth = rayCS.z; // Starting UV of the sampling loop float2 startUV = rayStartCS.xy * 0.5f + 0.5f; startUV.y = 1.0 - startUV.y; // Pixel to light ray in float2 rayUV = rayCS.xy * 0.5f; rayUV.y = -rayUV.y; float step = 1.0 / _SampleCount; float compareThreshold = abs(rayOrthoViewSpace.z - rayStartCS.z) * step; float occluded = 0.0f; for (int i = 0; i < _SampleCount; i++) { // Step for this sample float sampleStep = ((i + 1) * step + step * dither); // UVs for the current sample float2 sampleUV = (startUV + rayUV * sampleStep) * _ScreenToTargetScale.xy; // Ray depth for this sample float raySampleDepth = startDepth + rayDepth * sampleStep; // Depth buffer depth for this sample float sampleDepth = SAMPLE_TEXTURE2D_LOD(_CameraDepthTexture, s_point_clamp_sampler, sampleUV, 0.0).x; bool Hit = false; float depthDiff = sampleDepth - raySampleDepth; Hit = depthDiff < compareThreshold && depthDiff > 0.0;// 1e-4; if (Hit) occluded = 1.0f; } // Off screen masking // We remove the occlusion if the ray is occluded and only if direction steps out of the screen float2 vignette = max(6.0 * abs(rayStartCS.xy + rayCS.xy * occluded * 0.5) - 5.0, 0.0); occluded *= saturate( 1.0 - dot(vignette, vignette) ); return occluded; } float ComputeContactShadow(PositionInputs posInput, float3 direction) { float contactShadow = 1.0; if (_ContactShadowLength > 0.0f) { //Here LightDirection is not the light direction but the light position float4 result = ScreenSpaceShadowRayCast(posInput.positionWS, direction, _ContactShadowLength * max(0.5, posInput.linearDepth * _ContactShadowDistanceScaleFactor)); contactShadow = 1.0 - result.x * saturate((_ContactShadowFadeEnd - posInput.linearDepth) * _ContactShadowFadeOneOverRange); } return lerp(1.0, contactShadow, _ContactShadowOpacity); } [numthreads(DEFERRED_SHADOW_TILE_SIZE, DEFERRED_SHADOW_TILE_SIZE, 1)] void DEFERRED_DIRECTIONAL(uint2 groupThreadId : SV_GroupThreadID, uint2 groupId : SV_GroupID) { uint2 pixelCoord = groupId * DEFERRED_SHADOW_TILE_SIZE + groupThreadId; uint2 tileCoord = groupId; float contactShadow = 1.0; float depth = LOAD_TEXTURE2D(_CameraDepthTexture, pixelCoord.xy).x; if (depth == UNITY_RAW_FAR_CLIP_VALUE) return; PositionInputs posInput = GetPositionInput(pixelCoord.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V, tileCoord); //Direction got from either the directional light direction or the difference of punctual light position and the pixel position float3 direction = normalize(_DirectionalLightDirection.xyz * _DirectionalLightDirection.w + (_PunctualLightPosition.xyz - posInput.positionWS) * _PunctualLightPosition.w); NormalData normalData; DecodeFromNormalBuffer(posInput.positionSS, normalData); float3 normalWS = normalData.normalWS; ShadowContext shadowContext = InitShadowContext(); float shadow = GetDirectionalShadowAttenuation(shadowContext, posInput.positionWS, normalWS, _DirectionalShadowIndex, _DirectionalLightDirection.xyz); #ifdef ENABLE_CONTACT_SHADOWS contactShadow = ComputeContactShadow(posInput, direction); #endif _DeferredShadowTextureUAV[pixelCoord] = float4(shadow, contactShadow, 1.0, 1.0); // Note: RT is RG16 format } [numthreads(DEFERRED_SHADOW_TILE_SIZE, DEFERRED_SHADOW_TILE_SIZE, 1)] void DeferredContactShadow(uint2 groupThreadId : SV_GroupThreadID, uint2 groupId : SV_GroupID) { uint2 pixelCoord = groupId * DEFERRED_SHADOW_TILE_SIZE + groupThreadId; uint2 tileCoord = groupId; float contactShadow = 1.0; float depth = LOAD_TEXTURE2D(_CameraDepthTexture, pixelCoord.xy).x; if (depth == UNITY_RAW_FAR_CLIP_VALUE) return; PositionInputs posInput = GetPositionInput(pixelCoord.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V, tileCoord); float3 direction = normalize(_PunctualLightPosition.xyz - posInput.positionWS); contactShadow = ComputeContactShadow(posInput, direction); _DeferredShadowTextureUAV[pixelCoord] = float4(1.0, contactShadow, 1.0, 1.0); // Note: RT is RG16 format }