Merge pull request #395 from aws-lumberyard-dev/Atom/mriegger/PLS

Atom/mriegger/pls
This commit is contained in:
mrieggeramzn
2021-05-10 17:13:39 -07:00
committed by GitHub
12 changed files with 352 additions and 29 deletions
@@ -13,6 +13,49 @@
#pragma once
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
#include <Atom/Features/Shadow/ProjectedShadow.azsli>
// The order should match m_pointShadowTransforms in PointLightFeatureProcessor.h/.cpp
static const float3 PointLightShadowCubemapDirections[6] = {float3(-1,0,0), float3(1,0,0), float3(0,-1,0), float3(0,1,0), float3(0,0,-1), float3(0,0,1)};
int GetPointLightShadowCubemapFace(const float3 targetPos, const float3 lightPos)
{
const float3 toPoint = targetPos - lightPos;
const float maxElement = max(abs(toPoint.z), max(abs(toPoint.x), abs(toPoint.y)));
if (toPoint.x == -maxElement)
{
return 0;
}
else if (toPoint.x == maxElement)
{
return 1;
}
else if (toPoint.y == -maxElement)
{
return 2;
}
else if (toPoint.y == maxElement)
{
return 3;
}
else if (toPoint.z == -maxElement)
{
return 4;
}
else
{
return 5;
}
}
// PointLight::m_shadowIndices actually consists of uint16_t x 6 on the CPU, but visible as a uint32_t x 3 on the GPU.
// This function returns the proper uint16_t value given an input face in the range 0-5
int UnpackPointLightShadowIndex(const ViewSrg::PointLight light, const int face)
{
const int index = face >> 1;
const int shiftAmount = (face & 1) * 16;
return (light.m_shadowIndices[index] >> shiftAmount) & 0xFFFF;
}
void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
{
@@ -31,11 +74,38 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
d2 = max(0.001 * 0.001, d2); // clamp the light to at least 1mm away to avoid extreme values.
float3 lightIntensity = (light.m_rgbIntensityCandelas / d2) * radiusAttenuation;
// shadow
float litRatio = 1.0;
// How much is back face shadowed, it's set to the reverse of litRatio to share the same default value with thickness, which should be 0 if no shadow map available
float backShadowRatio = 0.0;
if (o_enableShadows)
{
const int shadowCubemapFace = GetPointLightShadowCubemapFace(surface.position, light.m_position);
const int shadowIndex = UnpackPointLightShadowIndex(light, shadowCubemapFace);
litRatio *= ProjectedShadow::GetVisibility(
shadowIndex,
light.m_position,
surface.position,
PointLightShadowCubemapDirections[shadowCubemapFace],
surface.normal);
// Use backShadowRatio to carry thickness from shadow map for thick mode
backShadowRatio = 1.0 - litRatio;
if (o_transmission_mode == TransmissionMode::ThickObject)
{
backShadowRatio = ProjectedShadow::GetThickness(
shadowIndex,
surface.position);
}
}
// Diffuse contribution
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight));
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
// Tranmission contribution
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), 0.0);
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), backShadowRatio);
// Adjust the light direcion for specular based on bulb size
@@ -61,6 +61,8 @@ ShaderResourceGroup RayTracingSceneSrg : SRG_RayTracingScene
float m_invAttenuationRadiusSquared;
float3 m_rgbIntensity;
float m_bulbRadius;
uint3 m_shadowIndices;
uint m_padding;
};
StructuredBuffer<PointLight> m_pointLights;
@@ -73,6 +73,8 @@ partial ShaderResourceGroup ViewSrg
float m_invAttenuationRadiusSquared; // For a radius at which this light no longer has an effect, 1 / radius^2.
float3 m_rgbIntensityCandelas;
float m_bulbRadius;
uint3 m_shadowIndices;
uint m_padding;
};
StructuredBuffer<PointLight> m_pointLights;
@@ -59,6 +59,8 @@ ShaderResourceGroup PassSrg : SRG_PerPass
float m_invAttenuationRadiusSquared; // For a radius at which this light no longer has an effect, 1 / radius^2.
float3 m_rgbIntensityCandelas;
float m_bulbRadius;
uint3 m_shadowIndices;
uint m_padding;
};
struct DiskLight
@@ -1,19 +1,20 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/RPI.Public/FeatureProcessor.h>
#include <Atom/Feature/CoreLights/PhotometricValue.h>
#include <Atom/Feature/CoreLights/ShadowConstants.h>
#include <Atom/RPI.Public/FeatureProcessor.h>
namespace AZ
{
@@ -22,9 +23,25 @@ namespace AZ
namespace Render
{
struct PointLightData
{
AZStd::array<float, 3> m_position = {{0.0f, 0.0f, 0.0f}};
// Inverse of the distance at which this light no longer has an effect, squared. Also used for falloff calculations.
float m_invAttenuationRadiusSquared = 0.0f;
AZStd::array<float, 3> m_rgbIntensity = {{0.0f, 0.0f, 0.0f}};
// Radius of spherical light in meters.
float m_bulbRadius = 0.0f;
static const int NumShadowFaces = 6;
AZStd::array<uint16_t, NumShadowFaces> m_shadowIndices = {{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}};
uint32_t m_padding;
};
//! PointLightFeatureProcessorInterface provides an interface to acquire, release, and update a point light.
class PointLightFeatureProcessorInterface
: public RPI::FeatureProcessor
class PointLightFeatureProcessorInterface : public RPI::FeatureProcessor
{
public:
AZ_RTTI(AZ::Render::PointLightFeatureProcessorInterface, "{D3E0B016-F3C6-4C7A-A29E-0B3A4FA87806}", AZ::RPI::FeatureProcessor);
@@ -33,7 +50,8 @@ namespace AZ
using LightHandle = RHI::Handle<uint16_t, class PointLight>;
static constexpr PhotometricUnit PhotometricUnitType = PhotometricUnit::Candela;
//! Creates a new point light which can be referenced by the returned LightHandle. Must be released via ReleaseLight() when no longer needed.
//! Creates a new point light which can be referenced by the returned LightHandle. Must be released via ReleaseLight() when no
//! longer needed.
virtual LightHandle AcquireLight() = 0;
//! Releases a LightHandle which removes the point light.
virtual bool ReleaseLight(LightHandle& handle) = 0;
@@ -48,6 +66,24 @@ namespace AZ
virtual void SetAttenuationRadius(LightHandle handle, float attenuationRadius) = 0;
//! Sets the bulb radius for the provided LightHandle. Values greater than zero effectively make it a spherical light.
virtual void SetBulbRadius(LightHandle handle, float bulbRadius) = 0;
//! Sets if shadows are enabled
virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
//! Sets the shadowmap size (width and height) of the light.
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
//! Specifies filter method of shadows.
virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0;
//! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on
//! the boundary. 0 disables softening.
virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0;
//! Sets sample count to predict boundary of shadow (up to 16). It will be clamped to be less than or equal to the filtering
//! sample count.
virtual void SetPredictionSampleCount(LightHandle handle, uint16_t count) = 0;
//! Sets sample count for filtering of shadow boundary (up to 64)
virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0;
//! Sets the shadowmap Pcf (percentage closer filtering) method.
virtual void SetPcfMethod(LightHandle handle, PcfMethod method) = 0;
//! Sets all of the the point data for the provided LightHandle.
virtual void SetPointData(LightHandle handle, const PointLightData& data) = 0;
};
} // namespace Render
} // namespace AZ
@@ -380,7 +380,7 @@ namespace AZ
const float invRadiusSquared = diskLight.m_invAttenuationRadiusSquared;
if (invRadiusSquared <= 0.f)
{
AZ_Assert(false, "Attenuation radius have to be set before use the light.");
AZ_Assert(false, "Attenuation radius must be set before using the light.");
return;
}
const float attenuationRadius = sqrtf(1.f / invRadiusSquared);
@@ -59,7 +59,7 @@ namespace AZ
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
void SetPredictionSampleCount(LightHandle handle, uint16_t count) override;
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
void SetPcfMethod(LightHandle handle, PcfMethod method);
void SetPcfMethod(LightHandle handle, PcfMethod method) override;
void SetDiskData(LightHandle handle, const DiskLightData& data) override;
@@ -84,7 +84,7 @@ namespace AZ
template <typename Functor, typename ParamType>
void SetShadowSetting(LightHandle handle, Functor&&, ParamType&& param);
ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor;
ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor = nullptr;
IndexedDataVector<DiskLightData> m_diskLightData;
GpuBufferHandler m_lightBufferHandler;
@@ -44,6 +44,13 @@ namespace AZ
PointLightFeatureProcessor::PointLightFeatureProcessor()
: PointLightFeatureProcessorInterface()
{
// Note must match PointShadowDirections in PointLight.azsli
m_pointShadowTransforms[0] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), -AZ::Vector3::CreateAxisX());
m_pointShadowTransforms[1] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), AZ::Vector3::CreateAxisX());
m_pointShadowTransforms[2] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), -AZ::Vector3::CreateAxisY());
m_pointShadowTransforms[3] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), AZ::Vector3::CreateAxisY());
m_pointShadowTransforms[4] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), -AZ::Vector3::CreateAxisZ());
m_pointShadowTransforms[5] = AZ::Transform::CreateLookAt(AZ::Vector3::CreateZero(), AZ::Vector3::CreateAxisZ());
}
void PointLightFeatureProcessor::Activate()
@@ -54,6 +61,7 @@ namespace AZ
desc.m_elementCountSrgName = "m_pointLightCount";
desc.m_elementSize = sizeof(PointLightData);
desc.m_srgLayout = RPI::RPISystemInterface::Get()->GetViewSrgAsset()->GetLayout();
m_shadowFeatureProcessor = GetParentScene()->GetFeatureProcessor<ProjectedShadowFeatureProcessor>();
m_lightBufferHandler = GpuBufferHandler(desc);
}
@@ -83,6 +91,15 @@ namespace AZ
{
if (handle.IsValid())
{
for (int i = 0; i < PointLightData::NumShadowFaces; ++i)
{
ShadowId shadowId = ShadowId(m_pointLightData.GetData(handle.GetIndex()).m_shadowIndices[i]);
if (shadowId.IsValid())
{
m_shadowFeatureProcessor->ReleaseShadow(shadowId);
}
}
m_pointLightData.RemoveIndex(handle.GetIndex());
m_deviceBufferNeedsUpdate = true;
handle.Reset();
@@ -148,6 +165,7 @@ namespace AZ
lightPosition.StoreToFloat3(position.data());
m_deviceBufferNeedsUpdate = true;
UpdateShadow(handle);
}
void PointLightFeatureProcessor::SetAttenuationRadius(LightHandle handle, float attenuationRadius)
@@ -177,5 +195,122 @@ namespace AZ
return m_lightBufferHandler.GetElementCount();
}
void PointLightFeatureProcessor::SetShadowsEnabled(LightHandle handle, bool enabled)
{
auto& light = m_pointLightData.GetData(handle.GetIndex());
for (int i = 0; i < PointLightData::NumShadowFaces; ++i)
{
ShadowId shadowId = ShadowId(light.m_shadowIndices[i]);
if (shadowId.IsValid() && !enabled)
{
// Disable shadows
m_shadowFeatureProcessor->ReleaseShadow(shadowId);
shadowId.Reset();
light.m_shadowIndices[i] = shadowId.GetIndex();
m_deviceBufferNeedsUpdate = true;
}
else if (shadowId.IsNull() && enabled)
{
// Enable shadows
light.m_shadowIndices[i] = m_shadowFeatureProcessor->AcquireShadow().GetIndex();
UpdateShadow(handle);
m_deviceBufferNeedsUpdate = true;
}
}
}
void PointLightFeatureProcessor::SetPointData(LightHandle handle, const PointLightData& data)
{
AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to PointLightFeatureProcessor::SetPointData().");
m_pointLightData.GetData(handle.GetIndex()) = data;
m_deviceBufferNeedsUpdate = true;
UpdateShadow(handle);
}
void PointLightFeatureProcessor::UpdateShadow(LightHandle handle)
{
constexpr float SqrtHalf = 0.707106781187f; // sqrt(0.5);
const auto& pointLight = m_pointLightData.GetData(handle.GetIndex());
for (int i = 0; i < PointLightData::NumShadowFaces; ++i)
{
ShadowId shadowId = ShadowId(pointLight.m_shadowIndices[i]);
if (shadowId.IsNull())
{
// Early out if shadows are disabled.
return;
}
ProjectedShadowFeatureProcessorInterface::ProjectedShadowDescriptor desc = m_shadowFeatureProcessor->GetShadowProperties(shadowId);
// Make it slightly larger than 90 degrees to avoid artifacts on the boundary between 2 cubemap faces
desc.m_fieldOfViewYRadians = DegToRad(91.0f);
desc.m_transform = m_pointShadowTransforms[i];
desc.m_transform.SetTranslation(pointLight.m_position[0], pointLight.m_position[1], pointLight.m_position[2]);
desc.m_aspectRatio = 1.0f;
desc.m_nearPlaneDistance = SqrtHalf * pointLight.m_bulbRadius;
const float invRadiusSquared = pointLight.m_invAttenuationRadiusSquared;
if (invRadiusSquared <= 0.f)
{
AZ_Assert(false, "Attenuation radius must be set before using the light.");
return;
}
const float attenuationRadius = sqrtf(1.f / invRadiusSquared);
desc.m_farPlaneDistance = attenuationRadius + pointLight.m_bulbRadius;
m_shadowFeatureProcessor->SetShadowProperties(shadowId, desc);
}
}
template<typename Functor, typename ParamType>
void PointLightFeatureProcessor::SetShadowSetting(LightHandle handle, Functor&& functor, ParamType&& param)
{
AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to PointLightFeatureProcessor::SetShadowSetting().");
auto& light = m_pointLightData.GetData(handle.GetIndex());
for (int lightIndex = 0; lightIndex < PointLightData::NumShadowFaces; ++lightIndex)
{
ShadowId shadowId = ShadowId(light.m_shadowIndices[lightIndex]);
AZ_Assert(shadowId.IsValid(), "Attempting to set a shadow property when shadows are not enabled.");
if (shadowId.IsValid())
{
AZStd::invoke(AZStd::forward<Functor>(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward<ParamType>(param));
}
}
}
void PointLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution, shadowmapSize);
}
void PointLightFeatureProcessor::SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method);
}
void PointLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians);
}
void PointLightFeatureProcessor::SetPredictionSampleCount(LightHandle handle, uint16_t count)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetPredictionSampleCount, count);
}
void PointLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetFilteringSampleCount, count);
}
void PointLightFeatureProcessor::SetPcfMethod(LightHandle handle, PcfMethod method)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetPcfMethod, method);
}
} // namespace Render
} // namespace AZ
@@ -16,6 +16,7 @@
#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/GpuBufferHandler.h>
#include <CoreLights/IndexedDataVector.h>
#include <Shadows/ProjectedShadowFeatureProcessor.h>
namespace AZ
{
@@ -24,15 +25,6 @@ namespace AZ
namespace Render
{
struct PointLightData
{
AZStd::array<float, 3> m_position = { { 0.0f, 0.0f, 0.0f } };
float m_invAttenuationRadiusSquared = 0.0f; // Inverse of the distance at which this light no longer has an effect, squared. Also used for falloff calculations.
AZStd::array<float, 3> m_rgbIntensity = { { 0.0f, 0.0f, 0.0f } };
float m_bulbRadius = 0.0f; // Radius of spherical light in meters.
};
class PointLightFeatureProcessor final
: public PointLightFeatureProcessorInterface
{
@@ -58,18 +50,34 @@ namespace AZ
void SetPosition(LightHandle handle, const AZ::Vector3& lightPosition) override;
void SetAttenuationRadius(LightHandle handle, float attenuationRadius) override;
void SetBulbRadius(LightHandle handle, float bulbRadius) override;
void SetShadowsEnabled(LightHandle handle, bool enabled) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
void SetPredictionSampleCount(LightHandle handle, uint16_t count) override;
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
void SetPcfMethod(LightHandle handle, PcfMethod method) override;
void SetPointData(LightHandle handle, const PointLightData& data) override;
const Data::Instance<RPI::Buffer> GetLightBuffer() const;
uint32_t GetLightCount()const;
private:
PointLightFeatureProcessor(const PointLightFeatureProcessor&) = delete;
using ShadowId = ProjectedShadowFeatureProcessor::ShadowId;
static constexpr const char* FeatureProcessorName = "PointLightFeatureProcessor";
void UpdateShadow(LightHandle handle);
// Convenience function for forwarding requests to the ProjectedShadowFeatureProcessor
template<typename Functor, typename ParamType>
void SetShadowSetting(LightHandle handle, Functor&&, ParamType&& param);
ProjectedShadowFeatureProcessor* m_shadowFeatureProcessor = nullptr;
IndexedDataVector<PointLightData> m_pointLightData;
GpuBufferHandler m_lightBufferHandler;
bool m_deviceBufferNeedsUpdate = false;
AZStd::array<AZ::Transform, PointLightData::NumShadowFaces> m_pointShadowTransforms;
};
} // namespace Render
} // namespace AZ
@@ -112,7 +112,7 @@ namespace AZ
bool AreaLightComponentConfig::SupportsShadows() const
{
return m_shapeType == AZ_CRC_CE("DiskShape");
return m_lightType == LightType::SpotDisk || m_lightType == LightType::Sphere;
}
bool AreaLightComponentConfig::ShadowsDisabled() const
@@ -63,5 +63,64 @@ namespace AZ
debugDisplay.DrawWireSphere(transform.GetTranslation(), CalculateAttenuationRadius(AreaLightComponentConfig::CutoffIntensity));
}
}
void SphereLightDelegate::SetEnableShadow(bool enabled)
{
Base::SetEnableShadow(enabled);
if (GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled);
}
}
void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size);
}
}
void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method);
}
}
void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
}
void SphereLightDelegate::SetPredictionSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count);
}
}
void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count);
}
}
void SphereLightDelegate::SetPcfMethod(PcfMethod method)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method);
}
}
} // namespace Render
} // namespace AZ
@@ -24,6 +24,8 @@ namespace AZ
class SphereLightDelegate final
: public LightDelegateBase<PointLightFeatureProcessorInterface>
{
using Base = LightDelegateBase<PointLightFeatureProcessorInterface>;
public:
SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible);
@@ -32,6 +34,13 @@ namespace AZ
void DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const override;
float GetSurfaceArea() const override;
float GetEffectiveSolidAngle() const override { return PhotometricValue::OmnidirectionalSteradians; }
void SetEnableShadow(bool enabled) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
void SetPredictionSampleCount(uint32_t count) override;
void SetFilteringSampleCount(uint32_t count) override;
void SetPcfMethod(PcfMethod method) override;
private: