/* * 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 #include #include #include #include #include #include #include #include #include #include namespace AZ::Render { void ProjectedShadowFeatureProcessor::Reflect(ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext ->Class() ->Version(0); } } void ProjectedShadowFeatureProcessor::Activate() { const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get(); GpuBufferHandler::Descriptor desc; desc.m_bufferName = "ProjectedShadowBuffer"; desc.m_bufferSrgName = "m_projectedShadows"; desc.m_elementCountSrgName = ""; desc.m_elementSize = sizeof(ShadowData); desc.m_srgLayout = viewSrgLayout; m_shadowBufferHandler = GpuBufferHandler(desc); desc.m_bufferName = "ProjectedFilterParamsBuffer"; desc.m_bufferSrgName = "m_projectedFilterParams"; desc.m_elementCountSrgName = ""; desc.m_elementSize = sizeof(EsmShadowmapsPass::FilterParameter); desc.m_srgLayout = viewSrgLayout; m_filterParamBufferHandler = GpuBufferHandler(desc); m_shadowmapAtlasSizeIndex = viewSrgLayout->FindShaderInputConstantIndex(Name("m_shadowmapAtlasSize")); m_invShadowmapAtlasSizeIndex = viewSrgLayout->FindShaderInputConstantIndex(Name("m_invShadowmapAtlasSize")); CachePasses(); EnableSceneNotification(); } void ProjectedShadowFeatureProcessor::Deactivate() { DisableSceneNotification(); m_shadowData.Clear(); m_shadowBufferHandler.Release(); m_filterParamBufferHandler.Release(); m_shadowProperties.Clear(); m_projectedShadowmapsPasses.clear(); m_esmShadowmapsPasses.clear(); for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(false); } } ProjectedShadowFeatureProcessor::ShadowId ProjectedShadowFeatureProcessor::AcquireShadow() { // Reserve a new slot in m_shadowData size_t index = m_shadowData.Reserve(); if (index >= std::numeric_limits::max()) { m_shadowData.Release(index); return ShadowId::Null; } ShadowId id = ShadowId(aznumeric_cast(index)); InitializeShadow(id); return id; } void ProjectedShadowFeatureProcessor::ReleaseShadow(ShadowId id) { if (id.IsValid()) { m_shadowProperties.RemoveIndex(m_shadowData.GetElement(id.GetIndex())); m_shadowData.Release(id.GetIndex()); } m_filterParameterNeedsUpdate = true; m_shadowmapPassNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetShadowTransform(ShadowId id, Transform transform) { ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_desc.m_transform = transform; UpdateShadowView(shadowProperty); } void ProjectedShadowFeatureProcessor::SetNearFarPlanes(ShadowId id, float nearPlaneDistance, float farPlaneDistance) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFrontBackPlanes()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_desc.m_nearPlaneDistance = GetMax(nearPlaneDistance, 0.0001f); shadowProperty.m_desc.m_farPlaneDistance = GetMax(farPlaneDistance, nearPlaneDistance + 0.0001f); UpdateShadowView(shadowProperty); } void ProjectedShadowFeatureProcessor::SetAspectRatio(ShadowId id, float aspectRatio) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetAspectRatio()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_desc.m_aspectRatio = aspectRatio; UpdateShadowView(shadowProperty); } void ProjectedShadowFeatureProcessor::SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFieldOfViewY()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_desc.m_fieldOfViewYRadians = fieldOfViewYRadians; UpdateShadowView(shadowProperty); } void ProjectedShadowFeatureProcessor::SetShadowBias(ShadowId id, float bias) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBias()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_bias = bias; } void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution()."); AZ_Assert(size != ShadowmapSize::None, "Shadowmap size cannot be set to None, remove the shadow instead."); FilterParameter& esmData = m_shadowData.GetElement(id.GetIndex()); esmData.m_shadowmapSize = aznumeric_cast(size); m_deviceBufferNeedsUpdate = true; m_shadowmapPassNeedsUpdate = true; m_filterParameterNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetPcfMethod(ShadowId id, PcfMethod method) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetPcfMethod()."); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_pcfMethod = method; m_deviceBufferNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetEsmExponent(ShadowId id, float exponent) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetEsmExponent()."); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_esmExponent = exponent; m_deviceBufferNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowFilterMethod()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_shadowFilterMethod = aznumeric_cast(method); UpdateShadowView(shadowProperty); m_shadowmapPassNeedsUpdate = true; m_filterParameterNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBoundaryWidthAngle()."); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_boundaryScale = boundaryWidthRadians / 2.0f; m_shadowmapPassNeedsUpdate = true; m_filterParameterNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetPredictionSampleCount(ShadowId id, uint16_t count) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetPredictionSampleCount()."); AZ_Warning("ProjectedShadowFeatureProcessor", count <= Shadow::MaxPcfSamplingCount, "Sampling count exceed the limit."); count = GetMin(count, Shadow::MaxPcfSamplingCount); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_predictionSampleCount = count; m_deviceBufferNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetFilteringSampleCount(ShadowId id, uint16_t count) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFilteringSampleCount()."); AZ_Warning("ProjectedShadowFeatureProcessor", count <= Shadow::MaxPcfSamplingCount, "Sampling count exceed the limit."); count = GetMin(count, Shadow::MaxPcfSamplingCount); ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); shadowData.m_filteringSampleCount = count; m_deviceBufferNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowProperties()."); ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); shadowProperty.m_desc = descriptor; UpdateShadowView(shadowProperty); m_shadowmapPassNeedsUpdate = true; m_filterParameterNeedsUpdate = true; } auto ProjectedShadowFeatureProcessor::GetShadowProperties(ShadowId id) -> const ProjectedShadowDescriptor& { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::GetShadowProperties()."); return GetShadowPropertyFromShadowId(id).m_desc; } void ProjectedShadowFeatureProcessor::UpdateShadowView(ShadowProperty& shadowProperty) { const ProjectedShadowDescriptor& desc = shadowProperty.m_desc; float nearDist = desc.m_nearPlaneDistance; float farDist = desc.m_farPlaneDistance; // Adjust the near plane if it's too close to ensure accuracy. constexpr float NearFarRatio = 1000.0f; const float minDist = desc.m_farPlaneDistance / NearFarRatio; nearDist = GetMax(minDist, nearDist); Matrix4x4 viewToClipMatrix; MakePerspectiveFovMatrixRH( viewToClipMatrix, GetMax(desc.m_fieldOfViewYRadians, MinimumFieldOfView), desc.m_aspectRatio, nearDist, farDist); RPI::ViewPtr view = shadowProperty.m_shadowmapView; view->SetViewToClipMatrix(viewToClipMatrix); view->SetCameraTransform(Matrix3x4::CreateFromTransform(desc.m_transform)); ShadowData& shadowData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); // Adjust the manually set bias to a more appropriate range for the shader. Scale the bias by the // near plane so that the bias appears consistent as other light properties change. shadowData.m_bias = nearDist * shadowProperty.m_bias * 0.01f; FilterParameter& esmData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); // Set parameters to calculate linear depth if ESM is used. esmData.m_n_f_n = nearDist / (farDist - nearDist); esmData.m_n_f = nearDist - farDist; esmData.m_f = farDist; esmData.m_isEnabled = FilterMethodIsEsm(shadowData); m_filterParameterNeedsUpdate = m_filterParameterNeedsUpdate || esmData.m_isEnabled; for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(esmData.m_isEnabled); } // Set depth bias matrix. const Matrix4x4& worldToLightClipMatrix = view->GetWorldToClipMatrix(); const Matrix4x4 depthBiasMatrix = Shadow::GetClipToShadowmapTextureMatrix() * worldToLightClipMatrix; shadowData.m_depthBiasMatrix = depthBiasMatrix; shadowData.m_unprojectConstants[0] = view->GetViewToClipMatrix().GetRow(2).GetElement(2); shadowData.m_unprojectConstants[1] = view->GetViewToClipMatrix().GetRow(2).GetElement(3); m_deviceBufferNeedsUpdate = true; } void ProjectedShadowFeatureProcessor::InitializeShadow(ShadowId shadowId) { m_deviceBufferNeedsUpdate = true; m_shadowmapPassNeedsUpdate = true; // Reserve a slot in m_shadowProperties, and store that index in m_shadowData's second vector uint16_t shadowPropertyIndex = m_shadowProperties.GetFreeSlotIndex(); m_shadowData.GetElement(shadowId.GetIndex()) = shadowPropertyIndex; ShadowProperty& shadowProperty = m_shadowProperties.GetData(shadowPropertyIndex); shadowProperty.m_shadowId = shadowId; AZ::Name viewName(AZStd::string::format("ProjectedShadowView (shadowId:%d)", shadowId.GetIndex())); shadowProperty.m_shadowmapView = RPI::View::CreateView(viewName, RPI::View::UsageShadow); UpdateShadowView(shadowProperty); } void ProjectedShadowFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* /*renderPipeline*/) { CachePasses(); } void ProjectedShadowFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr /*renderPipeline*/) { CachePasses(); } void ProjectedShadowFeatureProcessor::OnRenderPipelineRemoved( RPI::RenderPipeline* /*renderPipeline*/) { CachePasses(); } void ProjectedShadowFeatureProcessor::CachePasses() { const AZStd::vector validPipelineIds = CacheProjectedShadowmapsPass(); CacheEsmShadowmapsPass(validPipelineIds); m_shadowmapPassNeedsUpdate = true; } AZStd::vector ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() { const AZStd::vector& renderPipelines = GetParentScene()->GetRenderPipelines(); const auto* passSystem = RPI::PassSystemInterface::Get();; const AZStd::vector& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate")); AZStd::vector validPipelineIds; m_projectedShadowmapsPasses.clear(); for (RPI::Pass* pass : passes) { ProjectedShadowmapsPass* shadowPass = static_cast(pass); for (const RPI::RenderPipelinePtr& pipeline : renderPipelines) { if (pipeline.get() == shadowPass->GetRenderPipeline()) { m_projectedShadowmapsPasses.emplace_back(shadowPass); validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId()); } } } return validPipelineIds; } void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) { static const Name LightTypeName = Name("projected"); const auto* passSystem = RPI::PassSystemInterface::Get(); const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); m_esmShadowmapsPasses.clear(); for (RPI::Pass* pass : passes) { EsmShadowmapsPass* esmPass = static_cast(pass); if (esmPass->GetRenderPipeline() && AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() && esmPass->GetLightTypeName() == LightTypeName) { m_esmShadowmapsPasses.emplace_back(esmPass); } } } void ProjectedShadowFeatureProcessor::UpdateFilterParameters() { if (m_filterParameterNeedsUpdate) { UpdateStandardDeviations(); UpdateFilterOffsetsCounts(); SetFilterParameterToPass(); m_filterParameterNeedsUpdate = false; } } void ProjectedShadowFeatureProcessor::UpdateStandardDeviations() { if (m_esmShadowmapsPasses.empty()) { AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass."); return; } AZStd::vector standardDeviations(m_shadowProperties.GetDataCount()); for (uint32_t i = 0; i < m_shadowProperties.GetDataCount(); ++i) { ShadowProperty& shadowProperty = m_shadowProperties.GetDataVector().at(i); const ShadowData& shadow = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); if (!FilterMethodIsEsm(shadow)) { continue; } const FilterParameter& filter = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); const float boundaryWidthAngle = shadow.m_boundaryScale * 2.0f; constexpr float SmallAngle = 0.01f; const float fieldOfView = GetMax(shadowProperty.m_desc.m_fieldOfViewYRadians, MinimumFieldOfView); const float ratioToEntireWidth = boundaryWidthAngle / fieldOfView; const float widthInPixels = ratioToEntireWidth * filter.m_shadowmapSize; standardDeviations.at(i) = widthInPixels / (2.0f * GaussianMathFilter::ReliableSectionFactor); } if (standardDeviations.empty()) { for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(false); } return; } for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(true); esmPass->SetFilterParameters(standardDeviations); } } void ProjectedShadowFeatureProcessor::UpdateFilterOffsetsCounts() { if (m_esmShadowmapsPasses.empty()) { AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass."); return; } // Get array of filter counts for the camera view. const AZStd::array_view filterCounts = m_esmShadowmapsPasses.front()->GetFilterCounts(); // Create array of filter offsets. AZStd::vector filterOffsets; filterOffsets.reserve(filterCounts.size()); uint32_t filterOffset = 0; for (const uint32_t count : filterCounts) { filterOffsets.push_back(filterOffset); filterOffset += count; } auto& shadowProperties = m_shadowProperties.GetDataVector(); for (uint32_t i = 0; i < shadowProperties.size(); ++i) { ShadowProperty& shadowProperty = shadowProperties.at(i); const ShadowId shadowId = shadowProperty.m_shadowId; ShadowData& shadowData = m_shadowData.GetElement(shadowId.GetIndex()); FilterParameter& filterData = m_shadowData.GetElement(shadowId.GetIndex()); if (FilterMethodIsEsm(shadowData)) { filterData.m_parameterOffset = filterOffsets[i]; filterData.m_parameterCount = filterCounts[i]; } else { // If filter is not required, reset offsets and counts of filter in ESM data. filterData.m_parameterOffset = 0; filterData.m_parameterCount = 0; } } } void ProjectedShadowFeatureProcessor::SetFilterParameterToPass() { static uint32_t nameIndex = 0; if (m_projectedShadowmapsPasses.empty() || m_esmShadowmapsPasses.empty()) { AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass."); return; } // Create index table buffer. // [GFX TODO ATOM-14851] Should not be creating a new buffer here, just map the data or orphan with new data. const AZStd::string indexTableBufferName = AZStd::string::format("IndexTableBuffer(Projected) %d", nameIndex++); const ShadowmapAtlas& atlas = m_projectedShadowmapsPasses.front()->GetShadowmapAtlas(); const Data::Instance indexTableBuffer = atlas.CreateShadowmapIndexTableBuffer(indexTableBufferName); m_filterParamBufferHandler.UpdateBuffer(m_shadowData.GetRawData(), static_cast(m_shadowData.GetSize())); // Set index table buffer and ESM parameter buffer to ESM pass. for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetShadowmapIndexTableBuffer(indexTableBuffer); esmPass->SetFilterParameterBuffer(m_filterParamBufferHandler.GetBuffer()); } } void ProjectedShadowFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& /*packet*/) { AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Simulate"); if (m_shadowmapPassNeedsUpdate) { // Rebuild the shadow map sizes AZStd::vector shadowmapSizes; shadowmapSizes.reserve(m_shadowProperties.GetDataCount()); auto& shadowProperties = m_shadowProperties.GetDataVector(); for (uint32_t i = 0; i < shadowProperties.size(); ++i) { ShadowProperty& shadowProperty = shadowProperties.at(i); uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex(); FilterParameter& filterData = m_shadowData.GetElement(shadowIndex); shadowmapSizes.push_back(); ProjectedShadowmapsPass::ShadowmapSizeWithIndices& sizeWithIndices = shadowmapSizes.back(); sizeWithIndices.m_size = static_cast(filterData.m_shadowmapSize); sizeWithIndices.m_shadowIndexInSrg = shadowIndex; } for (ProjectedShadowmapsPass* shadowPass : m_projectedShadowmapsPasses) { shadowPass->UpdateShadowmapSizes(shadowmapSizes); } for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->QueueForBuildAndInitialization(); } if (!m_projectedShadowmapsPasses.empty()) { const ProjectedShadowmapsPass* shadowPass = m_projectedShadowmapsPasses.front(); for (const auto& shadowProperty : shadowProperties) { const int16_t shadowIndexInSrg = shadowProperty.m_shadowId.GetIndex(); ShadowData& shadowData = m_shadowData.GetElement(shadowIndexInSrg); FilterParameter& filterData = m_shadowData.GetElement(shadowIndexInSrg); const ShadowmapAtlas::Origin origin = shadowPass->GetOriginInAtlas(shadowIndexInSrg); shadowData.m_shadowmapArraySlice = origin.m_arraySlice; filterData.m_shadowmapOriginInSlice = origin.m_originInSlice; m_deviceBufferNeedsUpdate = true; } } m_shadowmapPassNeedsUpdate = false; } // This has to be called after UpdateShadowmapSizes(). UpdateFilterParameters(); if (m_deviceBufferNeedsUpdate) { m_shadowBufferHandler.UpdateBuffer(m_shadowData.GetRawData(), static_cast(m_shadowData.GetSize())); m_deviceBufferNeedsUpdate = false; } } void ProjectedShadowFeatureProcessor::PrepareViews(const PrepareViewsPacket&, AZStd::vector>& outViews) { if (!m_projectedShadowmapsPasses.empty()) { ProjectedShadowmapsPass* pass = m_projectedShadowmapsPasses.front(); RPI::RenderPipeline* renderPipeline = pass->GetRenderPipeline(); if (renderPipeline) { auto& shadowProperties = m_shadowProperties.GetDataVector(); for (uint32_t i = 0; i < shadowProperties.size(); ++i) { ShadowProperty& shadowProperty = shadowProperties.at(i); uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex(); const FilterParameter& filterData = m_shadowData.GetElement(shadowIndex); if (filterData.m_shadowmapSize == aznumeric_cast(ShadowmapSize::None)) { continue; } const RPI::PipelineViewTag& viewTag = pass->GetPipelineViewTagOfChild(i); const RHI::DrawListMask drawListMask = renderPipeline->GetDrawListMask(viewTag); if (shadowProperty.m_shadowmapView->GetDrawListMask() != drawListMask) { shadowProperty.m_shadowmapView->Reset(); shadowProperty.m_shadowmapView->SetDrawListMask(drawListMask); } outViews.emplace_back(AZStd::make_pair(viewTag, shadowProperty.m_shadowmapView)); } } } } void ProjectedShadowFeatureProcessor::Render(const ProjectedShadowFeatureProcessor::RenderPacket& packet) { AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Render"); if (!m_projectedShadowmapsPasses.empty()) { const ProjectedShadowmapsPass* pass = m_projectedShadowmapsPasses.front(); for (const RPI::ViewPtr& view : packet.m_views) { if (view->GetUsageFlags() & RPI::View::UsageFlags::UsageCamera) { RPI::ShaderResourceGroup* srg = view->GetShaderResourceGroup().get(); srg->SetConstant(m_shadowmapAtlasSizeIndex, static_cast(pass->GetShadowmapAtlasSize())); const float invShadowmapSize = 1.0f / static_cast(pass->GetShadowmapAtlasSize()); srg->SetConstant(m_invShadowmapAtlasSizeIndex, invShadowmapSize); m_shadowBufferHandler.UpdateSrg(srg); m_filterParamBufferHandler.UpdateSrg(srg); } } } } bool ProjectedShadowFeatureProcessor::FilterMethodIsEsm(const ShadowData& shadowData) const { return aznumeric_cast(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::Esm || aznumeric_cast(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::EsmPcf; } auto ProjectedShadowFeatureProcessor::GetShadowPropertyFromShadowId(ShadowId id) -> ShadowProperty& { AZ_Assert(id.IsValid(), "Error: Invalid ShadowId"); uint16_t shadowPropertyId = m_shadowData.GetElement(id.GetIndex()); return m_shadowProperties.GetData(shadowPropertyId); } }