/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include ShaderResourceGroup ShadowSrg : SRG_PerObject { row_major float4x4 m_worldMatrix; row_major float4x4 m_lightViewProjectionMatrix; float4 m_lightPosition; float4 m_ambientColor; float4 m_diffuseColor; Texture2D m_depthMapTexture; SamplerComparisonState m_shadowSampler { MagFilter = Linear; MinFilter = Linear; MipFilter = Point; ComparisonFunc = Less; MaxAnisotropy = 16; ReductionType = Comparison; }; } struct VSInput { float3 m_position : POSITION; float3 m_normal: NORMAL; }; struct VSOutput { float4 m_position : SV_Position; float3 m_normal: NORMAL; float4 m_lightViewPosition : TEXCOORD1; float3 m_worldPosition : TEXCOORD2; }; VSOutput MainVS(VSInput vsInput) { VSOutput OUT; OUT.m_worldPosition = mul(ShadowSrg::m_worldMatrix, float4(vsInput.m_position, 1.0)).xyz; OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPosition, 1.0)); OUT.m_lightViewPosition = mul(ShadowSrg::m_lightViewProjectionMatrix, float4(OUT.m_worldPosition, 1.0)); float4 normalsToWorld = mul(ShadowSrg::m_worldMatrix, float4(vsInput.m_normal, 0.0)); OUT.m_normal = normalsToWorld.xyz; OUT.m_normal = normalize(OUT.m_normal); return OUT; } struct PSOutput { float4 m_color : SV_Target0; }; //-------------------------------------------------------------------------------------------------- // Computes shadows using a 5x5 kernel, but optimized to 9-taps using bilinear fetches. // // https://vec3.ca/bicubic-filtering-in-fewer-taps/ //-------------------------------------------------------------------------------------------------- float ShadowResolve_5x5PCF(const Texture2D shadowMap, const float3 shadowPos, const float shadowMapSize, const SamplerComparisonState samplerState) { const float invShadowMapSize = 1.0 / shadowMapSize; // Compute the shadow map UV and fractionals. float2 shadowScaled = shadowPos.xy * shadowMapSize + float2(0.5, 0.5); float2 shadowUv = floor(shadowScaled); float2 st = shadowScaled - shadowUv; shadowUv = (shadowUv - float2(0.5, 0.5)) * invShadowMapSize; // Compute the offsets and weights for the 9 bilinear taps. float2 uvw0 = 4.0 - 3.0 * st; float2 uvw1 = 7.0; float2 uvw2 = 1.0 + 3.0 * st; float2 vUV0 = ((3.0 - 2.0 * st) / uvw0) - 2.0; float2 vUV1 = (3.0 + st) / uvw1; float2 vUV2 = (st / uvw2) + 2.0; // Accumulate the shadow results and return the resolve value. float shadowResult; shadowResult = uvw0.x * uvw0.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV0.x, vUV0.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw1.x * uvw0.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV1.x, vUV0.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw2.x * uvw0.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV2.x, vUV0.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw0.x * uvw1.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV0.x, vUV1.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw1.x * uvw1.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV1.x, vUV1.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw2.x * uvw1.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV2.x, vUV1.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw0.x * uvw2.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV0.x, vUV2.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw1.x * uvw2.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV1.x, vUV2.y) * invShadowMapSize, shadowPos.z); shadowResult += uvw2.x * uvw2.y * shadowMap.SampleCmpLevelZero(samplerState, shadowUv + float2(vUV2.x, vUV2.y) * invShadowMapSize, shadowPos.z); shadowResult /= 144.0; return shadowResult * shadowResult; } float GetShadowMapDimensions() { float shadowMapWidth, shadowMapHeight; ShadowSrg::m_depthMapTexture.GetDimensions(shadowMapWidth, shadowMapHeight); return shadowMapWidth; } PSOutput MainPS(VSOutput vsOutput) { PSOutput OUT; float bias = 0.001f; float shadowMask; float lightIntensity; float3 projectTexCoord; float3 lightDirection; OUT.m_color = ShadowSrg::m_ambientColor; lightDirection = ShadowSrg::m_lightPosition.xyz - vsOutput.m_worldPosition; lightDirection = normalize(lightDirection); projectTexCoord.xyz = vsOutput.m_lightViewPosition.xyz / vsOutput.m_lightViewPosition.w; projectTexCoord.xy = (projectTexCoord.xy / float2(2.0f, -2.0f)) + 0.5f; projectTexCoord.z -= bias; shadowMask = ShadowResolve_5x5PCF(ShadowSrg::m_depthMapTexture, projectTexCoord, GetShadowMapDimensions(), ShadowSrg::m_shadowSampler); lightIntensity = shadowMask * saturate(dot(normalize(vsOutput.m_normal), lightDirection)); OUT.m_color += (ShadowSrg::m_diffuseColor * lightIntensity); OUT.m_color = saturate(OUT.m_color); return OUT; }