You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
644 lines
27 KiB
C++
644 lines
27 KiB
C++
/*
|
|
* 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 <Shadows/ProjectedShadowFeatureProcessor.h>
|
|
|
|
#include <AzCore/Debug/EventTrace.h>
|
|
#include <AzCore/Math/MatrixUtils.h>
|
|
#include <Math/GaussianMathFilter.h>
|
|
#include <Atom/RHI/CpuProfiler.h>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
#include <Atom/RPI.Public/RPISystemInterface.h>
|
|
#include <Atom/RPI.Public/Scene.h>
|
|
#include <Atom/RPI.Public/View.h>
|
|
#include <Atom/RPI.Public/Pass/PassSystem.h>
|
|
#include <Atom/RPI.Public/Pass/PassFilter.h>
|
|
#include <CoreLights/Shadow.h>
|
|
|
|
namespace AZ::Render
|
|
{
|
|
void ProjectedShadowFeatureProcessor::Reflect(ReflectContext* context)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
|
{
|
|
serializeContext
|
|
->Class<ProjectedShadowFeatureProcessor, FeatureProcessor>()
|
|
->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<ShadowId::IndexType>::max())
|
|
{
|
|
m_shadowData.Release(index);
|
|
return ShadowId::Null;
|
|
}
|
|
|
|
ShadowId id = ShadowId(aznumeric_cast<ShadowId::IndexType>(index));
|
|
InitializeShadow(id);
|
|
|
|
return id;
|
|
}
|
|
|
|
void ProjectedShadowFeatureProcessor::ReleaseShadow(ShadowId id)
|
|
{
|
|
if (id.IsValid())
|
|
{
|
|
m_shadowProperties.RemoveIndex(m_shadowData.GetElement<ShadowPropertyIdIndex>(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<FilterParamIndex>(id.GetIndex());
|
|
esmData.m_shadowmapSize = aznumeric_cast<uint32_t>(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<ShadowDataIndex>(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<ShadowDataIndex>(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<ShadowDataIndex>(id.GetIndex());
|
|
shadowData.m_shadowFilterMethod = aznumeric_cast<uint16_t>(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<ShadowDataIndex>(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<ShadowDataIndex>(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<ShadowDataIndex>(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<ShadowDataIndex>(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<FilterParamIndex>(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<ShadowPropertyIdIndex>(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<RPI::RenderPipelineId> validPipelineIds = CacheProjectedShadowmapsPass();
|
|
CacheEsmShadowmapsPass(validPipelineIds);
|
|
m_shadowmapPassNeedsUpdate = true;
|
|
}
|
|
|
|
AZStd::vector<RPI::RenderPipelineId> ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass()
|
|
{
|
|
const AZStd::vector<RPI::RenderPipelinePtr>& renderPipelines = GetParentScene()->GetRenderPipelines();
|
|
const auto* passSystem = RPI::PassSystemInterface::Get();;
|
|
const AZStd::vector<RPI::Pass*>& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate"));
|
|
|
|
AZStd::vector<RPI::RenderPipelineId> validPipelineIds;
|
|
m_projectedShadowmapsPasses.clear();
|
|
for (RPI::Pass* pass : passes)
|
|
{
|
|
ProjectedShadowmapsPass* shadowPass = static_cast<ProjectedShadowmapsPass*>(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<RPI::RenderPipelineId>& validPipelineIds)
|
|
{
|
|
static const Name LightTypeName = Name("projected");
|
|
|
|
const auto* passSystem = RPI::PassSystemInterface::Get();
|
|
const AZStd::vector<RPI::Pass*> passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate"));
|
|
|
|
m_esmShadowmapsPasses.clear();
|
|
for (RPI::Pass* pass : passes)
|
|
{
|
|
EsmShadowmapsPass* esmPass = static_cast<EsmShadowmapsPass*>(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<float> 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<ShadowDataIndex>(shadowProperty.m_shadowId.GetIndex());
|
|
if (!FilterMethodIsEsm(shadow))
|
|
{
|
|
continue;
|
|
}
|
|
const FilterParameter& filter = m_shadowData.GetElement<FilterParamIndex>(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<uint32_t> filterCounts = m_esmShadowmapsPasses.front()->GetFilterCounts();
|
|
|
|
// Create array of filter offsets.
|
|
AZStd::vector<uint32_t> 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<ShadowDataIndex>(shadowId.GetIndex());
|
|
FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(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<RPI::Buffer> indexTableBuffer = atlas.CreateShadowmapIndexTableBuffer(indexTableBufferName);
|
|
|
|
m_filterParamBufferHandler.UpdateBuffer(m_shadowData.GetRawData<FilterParamIndex>(), static_cast<uint32_t>(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<ProjectedShadowmapsPass::ShadowmapSizeWithIndices> 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<FilterParamIndex>(shadowIndex);
|
|
|
|
shadowmapSizes.push_back();
|
|
ProjectedShadowmapsPass::ShadowmapSizeWithIndices& sizeWithIndices = shadowmapSizes.back();
|
|
sizeWithIndices.m_size = static_cast<ShadowmapSize>(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<ShadowDataIndex>(shadowIndexInSrg);
|
|
FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(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<ShadowDataIndex>(), static_cast<uint32_t>(m_shadowData.GetSize()));
|
|
m_deviceBufferNeedsUpdate = false;
|
|
}
|
|
}
|
|
|
|
void ProjectedShadowFeatureProcessor::PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>& 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<FilterParamIndex>(shadowIndex);
|
|
if (filterData.m_shadowmapSize == aznumeric_cast<uint32_t>(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<float>(pass->GetShadowmapAtlasSize()));
|
|
const float invShadowmapSize = 1.0f / static_cast<float>(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<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::Esm ||
|
|
aznumeric_cast<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::EsmPcf;
|
|
}
|
|
|
|
auto ProjectedShadowFeatureProcessor::GetShadowPropertyFromShadowId(ShadowId id) -> ShadowProperty&
|
|
{
|
|
AZ_Assert(id.IsValid(), "Error: Invalid ShadowId");
|
|
uint16_t shadowPropertyId = m_shadowData.GetElement<ShadowPropertyIdIndex>(id.GetIndex());
|
|
return m_shadowProperties.GetData(shadowPropertyId);
|
|
}
|
|
|
|
}
|