#ifndef CHARACTER_SHADOW_DEPTH_INPUT_INCLUDED #define CHARACTER_SHADOW_DEPTH_INPUT_INCLUDED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl" #include "./CharacterShadowTransforms.hlsl" #if _USE_2D_FACE_SHADOW #include "../Common/PotaToonGlobalInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl" #endif // We use custom samplers for 2021 SAMPLER(sampler_PotaToonPointClamp); SAMPLER(sampler_PotaToonLinearClamp); #define MAX_CHAR_SHADOWMAPS 1 uint LocalLightIndexToShadowmapIndex(uint lightindex) { if (_UseBrightestLight > 0 && lightindex == _BrightestLightIndex) return 0; return MAX_CHAR_SHADOWMAPS; } #define ADDITIONAL_CHARSHADOW_CHECK(i, lightIndex) { \ i = LocalLightIndexToShadowmapIndex(lightIndex); \ if (i >= MAX_CHAR_SHADOWMAPS) \ return 0; } float3 TransformWorldToCharShadowCoord(float3 worldPos) { float4 clipPos = CharShadowWorldToHClipWithoutBias(worldPos); #if UNITY_REVERSED_Z clipPos.z = min(clipPos.z, UNITY_NEAR_CLIP_VALUE); #else clipPos.z = max(clipPos.z, UNITY_NEAR_CLIP_VALUE); #endif float3 ndc = clipPos.xyz / clipPos.w; float2 ssUV = ndc.xy * 0.5 + 0.5; #if UNITY_UV_STARTS_AT_TOP ssUV.y = 1.0 - ssUV.y; #endif return float3(ssUV, ndc.z); } void ScaleUVForCascadeCharShadow(inout float2 uv) { uv *= _CharShadowUVScale; uv = (1.0 - _CharShadowUVScale) * 0.5 + uv; } half SampleCharShadowTexture(TEXTURE2D_PARAM(tex, samplerTex), float2 uv, half isFace = 0) { float2 value = SAMPLE_TEXTURE2D_LOD(tex, samplerTex, uv, 0).rg; return lerp(value.r, value.g, isFace); } half SampleScreenSpaceCharacterShadowTexture(float2 ssUV, half isFace = 0) { half2 value = SAMPLE_TEXTURE2D_LOD(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, ssUV, 0).rg; return lerp(value.r, value.g, isFace); } half2 SpiralBlurScreenSpaceCharacterShadow(float2 uv, float distance, float distanceSteps, float radialSteps, float radialOffset, float kernelPower) { float2 newUV = uv; int i = 0; float stepSize = distance / (int)distanceSteps; float curDistance = 0; float2 curOffset = 0; float subOffset = 0; float accumdist = 0; // half2 value = SAMPLE_TEXTURE2D_LOD(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, uv, 0).rg; // if (distanceSteps < 1) // return value; half2 value = 0; while (i < (int)distanceSteps) { curDistance += stepSize; for (int j = 0; j < (int)radialSteps; j++) { subOffset += 1; curOffset.x = cos(TWO_PI * (subOffset / radialSteps)); curOffset.y = sin(TWO_PI * (subOffset / radialSteps)); newUV = uv + curOffset * curDistance.xx; float distpow = PositivePow(curDistance, kernelPower); value += SAMPLE_TEXTURE2D_LOD(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, newUV, 0).rg * distpow; accumdist += distpow; } subOffset += radialOffset; i++; } value /= accumdist; return value; } half SampleScreenSpaceCharacterShadowTextureFiltered(float2 ssUV, half isFace = 0) { half2 attenuation = SAMPLE_TEXTURE2D_LOD(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, ssUV, 0).rg; half4 gatherR = GATHER_RED_TEXTURE2D(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, ssUV); half4 gatherG = GATHER_GREEN_TEXTURE2D(_ScreenSpaceCharShadowmapTexture, sampler_PotaToonPointClamp, ssUV); float avg = lerp(gatherR.x + gatherR.y + gatherR.z + gatherR.w, gatherG.x + gatherG.y + gatherG.z + gatherG.w, isFace) * 0.25; if (avg < 0.001 || avg > 0.999) return lerp(attenuation.r, attenuation.g, isFace); attenuation = SpiralBlurScreenSpaceCharacterShadow(ssUV, max(_ScreenSize.z, _ScreenSize.w) * 2, 2, 2, 0.62, 0.1); return lerp(attenuation.r, attenuation.g, isFace); } #if _USE_2D_FACE_SHADOW half Sample2DFaceShadow(float2 ssUV, float3 worldPos, half isFace) { float sceneDepth = SampleSceneDepth(ssUV); #if UNITY_REVERSED_Z half depthMultiplier = 1.0 - sceneDepth; #else half depthMultiplier = sceneDepth; #endif half width = _2DFaceShadowWidth * (0.25 + saturate(dot(_FaceForward.xyz, GetWorldSpaceViewDir(worldPos))) * 0.75) * depthMultiplier; float4 positionCS = TransformWorldToHClip(worldPos + _BrightestLightDirection.xyz * width * 0.04); positionCS.xyz /= positionCS.w; float2 sampleDepthUV = positionCS.xy * 0.5 + 0.5; #if UNITY_UV_STARTS_AT_TOP sampleDepthUV.y = 1.0 - sampleDepthUV.y; #endif if (any(sampleDepthUV) < 0 || any(sampleDepthUV) > 1) return 0; half charMask = SAMPLE_TEXTURE2D_X(_PotaToonCharMask, sampler_PotaToonLinearClamp, sampleDepthUV).g; const half minMask = 0.75; #if UNITY_REVERSED_Z float sampleDepth = charMask > minMask ? SampleSceneDepth(sampleDepthUV) : 0; if (sampleDepth - sceneDepth > 0.000001) return smoothstep(minMask, 1, charMask); #else float sampleDepth = charMask > minMask ? SampleSceneDepth(sampleDepthUV) : 1.0; if (sceneDepth - sampleDepth > 0.000001) return smoothstep(minMask, 1, charMask); #endif return 0; } #endif half SampleCharacterShadowmapFiltered(float2 uv, float z, SamplerState s) { // UV must be the scaled value with ScaleUVForCascadeCharShadow() z += 0.00001; float ow = _CharShadowmapSize.x * _CharShadowCascadeParams.y; float attenuation = SampleCharShadowTexture(TEXTURE2D_ARGS(_CharShadowMap, s), uv) + SampleCharShadowTexture(TEXTURE2D_ARGS(_CharShadowMap, s), uv + float2(ow, ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_CharShadowMap, s), uv + float2(ow, -ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_CharShadowMap, s), uv + float2(-ow, ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_CharShadowMap, s), uv + float2(-ow, -ow)); attenuation *= 0.2f; return attenuation - (z + _CharShadowBias.x); } half SampleTransparentShadowmapFiltered(float2 uv, float z, SamplerState s) { // UV must be the scaled value with ScaleUVForCascadeCharShadow() z += 0.00001; float ow = _CharTransparentShadowmapSize.x * _CharShadowCascadeParams.y; float attenuation = SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentShadowMap, s), uv) + SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentShadowMap, s), uv + float2(ow, ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentShadowMap, s), uv + float2(ow, -ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentShadowMap, s), uv + float2(-ow, ow)) + SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentShadowMap, s), uv + float2(-ow, -ow)); attenuation *= 0.2f; return attenuation - (z + _CharShadowBias.x); } half TransparentAttenuation(float2 uv, half opacity) { // UV must be the scaled value with ScaleUVForCascadeCharShadow() // Saturate since texture could have value more than 1 return saturate(SampleCharShadowTexture(TEXTURE2D_ARGS(_TransparentAlphaSum, sampler_PotaToonLinearClamp), uv) - opacity); // Total alpha sum - current pixel's alpha } // For transparent shadow, we assume that 'isFace' is always false. (The face should not be transparent by design) half GetTransparentShadow(float2 uv, float z, half opacity) { // Ignore if transparent shadow is disabled. if (_CharTransparentShadowmapSize.x > 0.99) return 0; // UV must be the scaled value with ScaleUVForCascadeCharShadow() half hidden = SampleTransparentShadowmapFiltered(uv, z, sampler_PotaToonLinearClamp) > 0 ? 1 : 0; half atten = TransparentAttenuation(uv, opacity); return hidden * atten; } half CharacterAndTransparentShadowmap(float2 ssUV, float3 worldPos, half opacity, half isFace) { // TODO: Scale uv first for cascade char shadow map // ScaleUVForCascadeCharShadow(uv); float3 coord = TransformWorldToCharShadowCoord(worldPos); #if _USE_2D_FACE_SHADOW return max(Sample2DFaceShadow(ssUV, worldPos, isFace), GetTransparentShadow(coord.xy, coord.z, opacity)); #else #if _ALPHATEST_ON // We assume that 'isFace' is always false. (The face should not be transparent by design) half charShadowAtten = SampleCharacterShadowmapFiltered(coord.xy, coord.z, sampler_PotaToonLinearClamp); return max(smoothstep(0, 0.0002, charShadowAtten), GetTransparentShadow(coord.xy, coord.z, opacity)); #else return max(SampleScreenSpaceCharacterShadowTextureFiltered(ssUV, isFace), GetTransparentShadow(coord.xy, coord.z, opacity)); #endif #endif } half SampleCharacterAndTransparentShadow(float2 ssUV, float3 worldPos, half opacity, half isFace) { if (_IsBrightestLightMain == 0) return 0; half fallback = isFace ? 0 : SAMPLE_TEXTURE2D_LOD(_CharContactShadowTexture, sampler_PotaToonPointClamp, ssUV, 0).r; return lerp(CharacterAndTransparentShadowmap(ssUV, worldPos, opacity, isFace), fallback, GetCharShadowFade(TransformWorldToView(worldPos).z)); } half SampleAdditionalCharacterAndTransparentShadow(float2 ssUV, float3 worldPos, half opacity, half isFace, uint lightIndex = 0) { #ifndef USE_FORWARD_PLUS return 0; #endif uint i; ADDITIONAL_CHARSHADOW_CHECK(i, lightIndex) if (_IsBrightestLightMain == 1) return 0; half fallback = isFace ? 0 : SAMPLE_TEXTURE2D_LOD(_CharContactShadowTexture, sampler_PotaToonPointClamp, ssUV, 0).r; return lerp(CharacterAndTransparentShadowmap(ssUV, worldPos, opacity, isFace), fallback, GetCharShadowFade(TransformWorldToView(worldPos).z)); } #endif