Michael Riegger пре 4 година
родитељ
комит
8b2c37d75f
3 измењених фајлова са 491 додато и 366 уклоњено
  1. 420 323
      Gem/Code/Source/ShadowExampleComponent.cpp
  2. 44 16
      Gem/Code/Source/ShadowExampleComponent.h
  3. 27 27
      Scripts/ShadowTest.bv.lua

+ 420 - 323
Gem/Code/Source/ShadowExampleComponent.cpp

@@ -29,6 +29,8 @@
 #include <SampleComponentConfig.h>
 
 #include <RHI/BasicRHIComponent.h>
+#include <Atom/RPI.Public/ColorManagement/TransformColor.h>
+
 
 namespace AtomSampleViewer
 {
@@ -39,12 +41,13 @@ namespace AtomSampleViewer
     };
 
     const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
-    AZ::Color ShadowExampleComponent::s_diskLightColors[] = {
+    AZ::Color ShadowExampleComponent::s_positionalLightColors[] = {
         // they will be initialized in the constructor.
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero()
     };
+
     const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = 
     {
         AZ::Render::ShadowmapSize::Size256,
@@ -77,9 +80,9 @@ namespace AtomSampleViewer
     ShadowExampleComponent::ShadowExampleComponent()
         : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml")
     {
-        s_diskLightColors[0] = AZ::Colors::Red;
-        s_diskLightColors[1] = AZ::Colors::Green;
-        s_diskLightColors[2] = AZ::Colors::Blue;
+        s_positionalLightColors[0] = AZ::Colors::Red;
+        s_positionalLightColors[1] = AZ::Colors::Green;
+        s_positionalLightColors[2] = AZ::Colors::Blue;
     }
 
     void ShadowExampleComponent::Reflect(AZ::ReflectContext* context)
@@ -123,9 +126,9 @@ namespace AtomSampleViewer
         m_floorMeshIsReady = false;
 
         m_directionalLightImageSizeIndex = 2; // image size is 1024.
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
-            m_diskLightImageSizeIndices[index] = 2; // image size is 1024.
+            m_positionalLightImageSizeIndices[index] = 2; // image size is 1024.
         }
         m_cascadeCount = 2;
         m_ratioLogarithmUniform = 0.5f;
@@ -135,6 +138,7 @@ namespace AtomSampleViewer
         RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         m_directionalLightFeatureProcessor = scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
         m_diskLightFeatureProcessor = scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
+        m_pointLightFeatureProcessor = scene->GetFeatureProcessor<Render::PointLightFeatureProcessorInterface>();
 
         CreateMeshes();
         CreateDirectionalLight();
@@ -154,12 +158,10 @@ namespace AtomSampleViewer
 
         GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
+        DestroyDiskLights();
+        DestroyPointLights();
 
         m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
-        for (DiskLightHandle& handle : m_diskLightHandles)
-        {
-            m_diskLightFeatureProcessor->ReleaseLight(handle);
-        }
 
         m_imguiSidebar.Deactivate();
     }
@@ -168,8 +170,7 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
         constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position.
-        constexpr float diskLightPeriodTime = 7.f; // 7 seconds for a rotation of disk light positions.
-        constexpr float directionalLightDist = 10.f;
+        constexpr float diskLightPeriodTime = 7.f; // 7 seconds for a rotation of light positions.
         constexpr float diskLightDist = 5.f;
 
         if (m_elapsedTime == 0.f)
@@ -185,67 +186,98 @@ namespace AtomSampleViewer
         {
             m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi);
         }
-        if (m_isDiskLightAutoRotate)
+        if (m_isPositionalLightAutoRotate)
         {
-            m_diskLightRotationAngle = fmodf(m_diskLightRotationAngle - deltaTime * Constants::TwoPi / diskLightPeriodTime + Constants::TwoPi, Constants::TwoPi);
+            m_positionalLightRotationAngle = fmodf(m_positionalLightRotationAngle - deltaTime * Constants::TwoPi / diskLightPeriodTime + Constants::TwoPi, Constants::TwoPi);
         }
 
-        // Directional Light Transform
+
+        UpdateDirectionalLight();
+
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
-            const auto lightLocation = Vector3(
-                directionalLightDist * sinf(m_directionalLightRotationAngle),
-                directionalLightDist * cosf(m_directionalLightRotationAngle),
-                m_directionalLightHeight);
-            const auto lightTransform = Transform::CreateLookAt(
-                lightLocation,
-                Vector3::CreateZero());
-            m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
-        }
+            const auto transform = GetTransformForLight(index);
 
-        // Disk Lights Transform
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
-        { 
-            const float angle = m_diskLightRotationAngle + index * Constants::TwoPi / 3;
-            const auto location = Vector3(
-                diskLightDist * sinf(angle),
-                diskLightDist * cosf(angle),
-                m_diskLightHeights[index]);
-            const auto transform = Transform::CreateLookAt(
-                location,
-                Vector3::CreateZero());
-            m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], location);
-            m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1));
-        }
+            if (m_diskLightHandles[index].IsValid())
+            {
+                m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], transform.GetTranslation());
+                m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1));
+            }
 
+            if (m_pointLightHandles[index].IsValid())
+            {
+                m_pointLightFeatureProcessor->SetPosition(m_pointLightHandles[index], transform.GetTranslation());
+            }
+        }
         Camera::CameraRequestBus::Event(
             GetCameraEntityId(),
             &Camera::CameraRequestBus::Events::SetFovRadians,
             m_cameraFovY);
 
+
+        DrawSidebar();
+    }
+
+    AZ::Transform ShadowExampleComponent::GetTransformForLight(const uint32_t index) const
+    {
+        constexpr float diskLightDist = 5.f;
+        using namespace AZ;
+        const float angle = m_positionalLightRotationAngle + index * Constants::TwoPi / 3;
+        const auto location = Vector3(diskLightDist * sinf(angle), diskLightDist * cosf(angle), m_positionalLightHeights[index]);
+        const auto transform = Transform::CreateLookAt(location, Vector3::CreateZero());
+        return transform;
+    }
+
+    float ShadowExampleComponent::GetAttenuationForLight(const uint32_t index) const
+    {
+        return sqrtf(m_lightIntensities[index] / CutoffIntensity);
+    }
+
+    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> ShadowExampleComponent::GetRgbIntensityForLight(
+        const uint32_t index) const
+    {
+        AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightRgbIntensity(
+            s_positionalLightColors[index] * m_lightIntensities[index]);
+       
+        return lightRgbIntensity;
+    }
+
+    AZStd::pair<float, float> ShadowExampleComponent::GetConeAnglesForLight(const uint32_t index) const
+    {
+        const float innerConeAngle = AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio;
+        const float outerConeAngle = AZ::DegToRad(m_outerConeAngles[index]);
+
+        return AZStd::make_pair(innerConeAngle, outerConeAngle);
+    }
+
+
+    void ShadowExampleComponent::UpdateDirectionalLight()
+    {
+        using namespace AZ;
+        constexpr float directionalLightDist = 10.f;
+
+        // Directional Light Transform
+        {
+            const auto lightLocation = Vector3(
+                directionalLightDist * sinf(m_directionalLightRotationAngle), directionalLightDist * cosf(m_directionalLightRotationAngle),
+                m_directionalLightHeight);
+            const auto lightTransform = Transform::CreateLookAt(lightLocation, Vector3::CreateZero());
+            m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
+        }
+
         // Camera Configuration
         {
             Camera::Configuration config;
-            Camera::CameraRequestBus::EventResult(
-                config,
-                GetCameraEntityId(),
-                &Camera::CameraRequestBus::Events::GetCameraConfiguration);
-            m_directionalLightFeatureProcessor->SetCameraConfiguration(
-                m_directionalLightHandle,
-                config);
+            Camera::CameraRequestBus::EventResult(config, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
+            m_directionalLightFeatureProcessor->SetCameraConfiguration(m_directionalLightHandle, config);
         }
 
         // Camera Transform
         {
             Transform transform = Transform::CreateIdentity();
-            TransformBus::EventResult(
-                transform,
-                GetCameraEntityId(),
-                &TransformBus::Events::GetWorldTM);
-            m_directionalLightFeatureProcessor->SetCameraTransform(
-                m_directionalLightHandle, transform);
+            TransformBus::EventResult(transform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
+            m_directionalLightFeatureProcessor->SetCameraTransform(m_directionalLightHandle, transform);
         }
-
-        DrawSidebar();
     }
 
     void ShadowExampleComponent::SaveCameraConfiguration()
@@ -401,34 +433,33 @@ namespace AtomSampleViewer
 
     void ShadowExampleComponent::CreateDiskLights()
     {
+        DestroyDiskLights();
+
         using namespace AZ;
         Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
 
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
             const DiskLightHandle handle = featureProcessor->AcquireLight();
-
-            AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(s_diskLightColors[index] * m_diskLightIntensities[index]);
-            featureProcessor->SetRgbIntensity(handle, lightColor);
-            featureProcessor->SetAttenuationRadius(
-                handle,
-                sqrtf(m_diskLightIntensities[index] / CutoffIntensity));
-            featureProcessor->SetConeAngles(
-                handle,
-                DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio,
-                DegToRad(m_outerConeAngles[index]));
-            featureProcessor->SetShadowsEnabled(handle, m_diskLightShadowEnabled[index]);
-            if (m_diskLightShadowEnabled[index])
-            {
-                featureProcessor->SetShadowmapMaxResolution(handle, s_shadowmapImageSizes[m_diskLightImageSizeIndices[index]]);
-                featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndicesDisk[index]]);
-                featureProcessor->SetSofteningBoundaryWidthAngle(handle, AZ::DegToRad(m_boundaryWidthsDisk[index]));
-                featureProcessor->SetPredictionSampleCount(handle, m_predictionSampleCountsDisk[index]);
-                featureProcessor->SetFilteringSampleCount(handle, m_filteringSampleCountsDisk[index]);
-                featureProcessor->SetPcfMethod(handle, m_pcfMethod[index]);
-            }
+            AZ_Assert(m_diskLightHandles[index].IsNull(), "Unreleased light");
             m_diskLightHandles[index] = handle;
         }
+        ApplyDiskLightSettings();
+    }
+    void ShadowExampleComponent::CreatePointLights()
+    {
+        DestroyPointLights();
+
+        using namespace AZ;
+        Render::PointLightFeatureProcessorInterface* const featureProcessor = m_pointLightFeatureProcessor;
+
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            const PointLightHandle handle = featureProcessor->AcquireLight();        
+            AZ_Assert(m_pointLightHandles[index].IsNull(), "Unreleased light");
+            m_pointLightHandles[index] = handle;
+        }
+        ApplyPointLightSettings();
     }
 
     void ShadowExampleComponent::DrawSidebar()
@@ -439,317 +470,302 @@ namespace AtomSampleViewer
         {
             ImGui::Spacing();
 
-            ImGui::Text("Directional Light");
-            ImGui::Indent();
+            DrawSidebarDirectionalLight();
+
+            ImGui::Separator();
+
+            if (DrawSidebarPositionalLights())
             {
-                ImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", 2.f);
+                ApplyDiskLightSettings();
+                ApplyPointLightSettings();
+            }
 
-                ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate);
-                ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360);
+            ImGui::Separator();
 
-                if (ScriptableImGui::SliderFloat("Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", 2.f))
-                {
-                    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
-                    m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
-                }
+            DrawSidebarCamera();
 
-                ImGui::Separator();
+            ImGui::Separator();
 
-                ImGui::Text("Shadowmap Size");
-                if (ScriptableImGui::Combo(
-                    "Size##Directional",
-                    &m_directionalLightImageSizeIndex,
-                    s_shadowmapImageSizeLabels,
+            if (ScriptableImGui::Button("Material Details..."))
+            {
+                m_imguiMaterialDetails.SetMaterial(m_materialInstance);
+                m_imguiMaterialDetails.OpenDialog();
+            }
+
+            m_imguiSidebar.End();
+        }
+        m_imguiMaterialDetails.Tick();
+    }
+
+    void ShadowExampleComponent::DrawSidebarDirectionalLight()
+    {
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Directional Light", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            ImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", 2.f);
+
+            ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate);
+            ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360);
+
+            if (ScriptableImGui::SliderFloat("Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", 2.f))
+            {
+                AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(
+                    DirectionalLightColor * m_directionalLightIntensity);
+                m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
+            }
+
+            ImGui::Text("Shadowmap Size");
+            if (ScriptableImGui::Combo(
+                    "Size##Directional", &m_directionalLightImageSizeIndex, s_shadowmapImageSizeLabels,
                     AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels)))
-                {
-                    m_directionalLightFeatureProcessor->SetShadowmapSize(
-                        m_directionalLightHandle,
-                        s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
-                }
+            {
+                m_directionalLightFeatureProcessor->SetShadowmapSize(
+                    m_directionalLightHandle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
+            }
+
+            ImGui::Text("Number of cascades");
+            bool cascadesChanged = false;
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
+            if (cascadesChanged)
+            {
+                m_directionalLightFeatureProcessor->SetCascadeCount(m_directionalLightHandle, m_cascadeCount);
+            }
 
-                ImGui::Text("Number of cascades");
-                bool cascadesChanged = false;
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
-                if (cascadesChanged)
+            ImGui::Spacing();
+            bool cascadeDepthIsChanged = ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic);
+            if (m_shadowmapFrustumSplitIsAutomatic)
+            {
+                ImGui::Text("Cascade partition scheme");
+                ImGui::Text("  (uniform <--> logarithm)");
+                cascadeDepthIsChanged =
+                    cascadeDepthIsChanged || ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f");
+                if (cascadeDepthIsChanged)
                 {
-                    m_directionalLightFeatureProcessor->SetCascadeCount(
-                        m_directionalLightHandle,
-                        m_cascadeCount);
+                    m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
+                        m_directionalLightHandle, m_ratioLogarithmUniform);
                 }
-
-                ImGui::Spacing();
-                bool cascadeDepthIsChanged = 
-                    ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic);
-                if (m_shadowmapFrustumSplitIsAutomatic)
+            }
+            else
+            {
+                for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
                 {
-                    ImGui::Text("Cascade partition scheme");
-                    ImGui::Text("  (uniform <--> logarithm)");
-                    cascadeDepthIsChanged = cascadeDepthIsChanged ||
-                        ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f");
-                    if (cascadeDepthIsChanged)
-                    {
-                        m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
-                            m_directionalLightHandle,
-                            m_ratioLogarithmUniform);
-                    }
+                    const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex);
+                    cascadeDepthIsChanged =
+                        cascadeDepthIsChanged || ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f);
                 }
-                else
+                if (cascadeDepthIsChanged)
                 {
                     for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
                     {
-                        const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex);
-                        cascadeDepthIsChanged = cascadeDepthIsChanged ||
-                            ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f);
-                    }
-                    if (cascadeDepthIsChanged)
-                    {
-                        for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
-                        {
-                            m_directionalLightFeatureProcessor->SetCascadeFarDepth(
-                                m_directionalLightHandle,
-                                aznumeric_cast<uint16_t>(cascadeIndex),
-                                m_cascadeFarDepth[cascadeIndex]);
-                        }
+                        m_directionalLightFeatureProcessor->SetCascadeFarDepth(
+                            m_directionalLightHandle, aznumeric_cast<uint16_t>(cascadeIndex), m_cascadeFarDepth[cascadeIndex]);
                     }
                 }
+            }
 
-                ImGui::Spacing();
+            ImGui::Spacing();
 
-                ImGui::Text("Filtering");
-                if (ScriptableImGui::Combo(
-                    "Filter Method##Directional",
-                    &m_shadowFilterMethodIndexDirectional,
-                    s_shadowFilterMethodLabels,
+            ImGui::Text("Filtering");
+            if (ScriptableImGui::Combo(
+                    "Filter Method##Directional", &m_shadowFilterMethodIndexDirectional, s_shadowFilterMethodLabels,
                     AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
+            {
+                m_directionalLightFeatureProcessor->SetShadowFilterMethod(
+                    m_directionalLightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
+            }
+            if (m_shadowFilterMethodIndexDirectional != aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::None))
+            {
+                ImGui::Text("Boundary Width in meter");
+                if (ScriptableImGui::SliderFloat("Width##Directional", &m_boundaryWidthDirectional, 0.f, 0.1f, "%.3f"))
                 {
-                    m_directionalLightFeatureProcessor->SetShadowFilterMethod(
-                        m_directionalLightHandle,
-                        s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
-                }
-                if (m_shadowFilterMethodIndexDirectional != aznumeric_cast<int>(ShadowFilterMethod::None))
-                {
-                    ImGui::Text("Boundary Width in meter");
-                    if (ScriptableImGui::SliderFloat("Width##Directional", &m_boundaryWidthDirectional, 0.f, 0.1f, "%.3f"))
-                    {
-                        m_directionalLightFeatureProcessor->SetShadowBoundaryWidth(
-                            m_directionalLightHandle,
-                            m_boundaryWidthDirectional);
-                    }
-                }
-
-                if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
-                    m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
-                {
-                    ImGui::Spacing();
-                    ImGui::Text("Filtering (PCF specific)");
-                    if (ScriptableImGui::SliderInt("Prediction # ##Directional", &m_predictionSampleCountDirectional, 4, 16))
-                    {
-                        m_directionalLightFeatureProcessor->SetPredictionSampleCount(
-                            m_directionalLightHandle,
-                            m_predictionSampleCountDirectional);
-                    }
-                    if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
-                    {
-                        m_directionalLightFeatureProcessor->SetFilteringSampleCount(
-                            m_directionalLightHandle,
-                            m_filteringSampleCountDirectional);
-                    }
+                    m_directionalLightFeatureProcessor->SetShadowBoundaryWidth(m_directionalLightHandle, m_boundaryWidthDirectional);
                 }
+            }
 
+            if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::Pcf) ||
+                m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::EsmPcf))
+            {
                 ImGui::Spacing();
-
-                bool debugFlagsChanged = false;
-                debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged;
-                debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged;
-
-                if (debugFlagsChanged)
+                ImGui::Text("Filtering (PCF specific)");
+                if (ScriptableImGui::SliderInt("Prediction # ##Directional", &m_predictionSampleCountDirectional, 4, 16))
                 {
-                    SetupDebugFlags();
+                    m_directionalLightFeatureProcessor->SetPredictionSampleCount(
+                        m_directionalLightHandle, m_predictionSampleCountDirectional);
                 }
-                
-                if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
+                if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
                 {
-                    m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(
-                        m_directionalLightHandle,
-                        m_isCascadeCorrectionEnabled);
+                    m_directionalLightFeatureProcessor->SetFilteringSampleCount(
+                        m_directionalLightHandle, m_filteringSampleCountDirectional);
                 }
             }
-            ImGui::Unindent();
 
+            ImGui::Spacing();
 
-            ImGui::Separator();
+            bool debugFlagsChanged = false;
+            debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged;
+            debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged;
 
-            ImGui::Text("Disk Lights");
-            ImGui::Indent();
+            if (debugFlagsChanged)
             {
-                ScriptableImGui::Checkbox("Auto Rotation##Disk", &m_isDiskLightAutoRotate);
-                ScriptableImGui::SliderAngle("Base Direction##Disk", &m_diskLightRotationAngle, 0, 360);
+                SetupDebugFlags();
+            }
 
-                ImGui::Spacing();
+            if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
+            {
+                m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(m_directionalLightHandle, m_isCascadeCorrectionEnabled);
+            }
+        }
+        ImGui::Unindent();
+    }
 
-                ImGui::Text("Control Target");
-                ScriptableImGui::RadioButton("Red", &m_controlTargetDiskLightIndex, 0);
-                ImGui::SameLine();
-                ScriptableImGui::RadioButton("Green", &m_controlTargetDiskLightIndex, 1);
-                ImGui::SameLine();
-                ScriptableImGui::RadioButton("Blue", &m_controlTargetDiskLightIndex, 2);
+    bool ShadowExampleComponent::DrawSidebarPositionalLights()
+    {
+        using namespace AZ::Render;
+        bool settingsChanged = false;
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Positional Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            ImGui::Text("Light Type");
+            if (ScriptableImGui::RadioButton("Disk", &m_positionalLightTypeActive, 0))
+            {
+                DestroyPointLights();
+                CreateDiskLights();
+                settingsChanged = true;
+            }
+            if (ScriptableImGui::RadioButton("Point", &m_positionalLightTypeActive, 1))
+            {
+                DestroyDiskLights();
+                CreatePointLights();
+                settingsChanged = true;
+            }
 
-                const int index = m_controlTargetDiskLightIndex;
-                const DiskLightHandle lightId = m_diskLightHandles[index];
+            ScriptableImGui::Checkbox("Auto Rotation##Positional", &m_isPositionalLightAutoRotate);
+            ScriptableImGui::SliderAngle("Base Direction##Positional", &m_positionalLightRotationAngle, 0, 360);
 
-                ScriptableImGui::SliderFloat("Height##Disk",
-                    &m_diskLightHeights[index], 1.f, 30.f, "%.1f", 2.f);
+            ImGui::Spacing();
 
-                if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f))
-                {
-                    m_diskLightFeatureProcessor->SetConeAngles(
-                        lightId,
-                        AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio,
-                        AZ::DegToRad(m_outerConeAngles[index]));
-                }
+            ImGui::Text("Control Target");
+            ScriptableImGui::RadioButton("Red", &m_controlTargetPositionalLightIndex, 0);
+            ImGui::SameLine();
+            ScriptableImGui::RadioButton("Green", &m_controlTargetPositionalLightIndex, 1);
+            ImGui::SameLine();
+            ScriptableImGui::RadioButton("Blue", &m_controlTargetPositionalLightIndex, 2);
 
-                if (ScriptableImGui::SliderFloat("Intensity##Disk", &m_diskLightIntensities[index], 0.f, 20000.f, "%.1f", 2.f))
-                {
-                    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(s_diskLightColors[index] * m_diskLightIntensities[index]);
-                    m_diskLightFeatureProcessor->SetRgbIntensity(lightId, lightColor);
-                    m_diskLightFeatureProcessor->SetAttenuationRadius(
-                        lightId,
-                        sqrtf(m_diskLightIntensities[index] / CutoffIntensity));
-                }
+            const int index = m_controlTargetPositionalLightIndex;
 
-                bool shadowmapSizeChanged =
-                    ScriptableImGui::Checkbox("Enable Shadow", &m_diskLightShadowEnabled[index]);
-
-                ImGui::Text("Shadowmap Size");
-                shadowmapSizeChanged = shadowmapSizeChanged ||
-                    ScriptableImGui::Combo(
-                        "Size##Disk",
-                        &m_diskLightImageSizeIndices[index],
-                        s_shadowmapImageSizeLabels,
-                        AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels));
-                if (shadowmapSizeChanged)
-                {
-                    m_diskLightFeatureProcessor->SetShadowsEnabled(lightId, m_diskLightShadowEnabled[index]);
-                    if (m_diskLightShadowEnabled[index])
-                    {
-                        m_diskLightFeatureProcessor->SetShadowmapMaxResolution(lightId, s_shadowmapImageSizes[m_diskLightImageSizeIndices[index]]);
-                    }
+            ScriptableImGui::SliderFloat("Height##Positional", &m_positionalLightHeights[index], 1.f, 30.f, "%.1f", 2.f);
 
-                    // Reset shadow parameters when shadow is disabled.
-                    if (!m_diskLightShadowEnabled[index])
-                    {
-                        m_shadowFilterMethodIndicesDisk[index] = 0;
-                        m_boundaryWidthsDisk[index] = 0.f;
-                        m_predictionSampleCountsDisk[index] = 0;
-                        m_filteringSampleCountsDisk[index] = 0;
-                    }
-                }
+            if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f))
+            {
+                settingsChanged = true;
+            }
 
-                ImGui::Spacing();
+            if (ScriptableImGui::SliderFloat("Intensity##Positional", &m_lightIntensities[index], 0.f, 20000.f, "%.1f", 2.f))
+            {
+                settingsChanged = true;
+            }
 
-                ImGui::Text("Filtering");
-                if (ScriptableImGui::Combo(
-                    "Filter Method##Disk",
-                    &m_shadowFilterMethodIndicesDisk[index],
-                    s_shadowFilterMethodLabels,
-                    AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
-                {
-                    m_diskLightFeatureProcessor->SetShadowFilterMethod(
-                        lightId,
-                        s_shadowFilterMethods[m_shadowFilterMethodIndicesDisk[index]]);
-                }
+            bool shadowmapSizeChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_positionalLightShadowEnabled[index]);
 
-                if (m_shadowFilterMethodIndicesDisk[index] != aznumeric_cast<int>(ShadowFilterMethod::None))
+            ImGui::Text("Shadowmap Size");
+            shadowmapSizeChanged = shadowmapSizeChanged ||
+                ScriptableImGui::Combo("Size##Positional", &m_positionalLightImageSizeIndices[index], s_shadowmapImageSizeLabels,
+                                       AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels));
+            if (shadowmapSizeChanged)
+            {
+                // Reset shadow parameters when shadow is disabled.
+                if (!m_positionalLightShadowEnabled[index])
                 {
-                    ImGui::Text("Boundary Width in degrees");
-                    if (ScriptableImGui::SliderFloat("Width##Disk", &m_boundaryWidthsDisk[index], 0.f, 1.0f, "%.3f"))
-                    {
-                        m_diskLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightId, AZ::DegToRad(m_boundaryWidthsDisk[index]));
-                    }
+                    m_shadowFilterMethodIndicesPositional[index] = 0;
+                    m_boundaryWidthsPositional[index] = 0.f;
+                    m_predictionSampleCountsPositional[index] = 0;
+                    m_filteringSampleCountsPositional[index] = 0;
                 }
+                settingsChanged = true;
+            }
 
-                if (m_shadowFilterMethodIndicesDisk[index] == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
-                    m_shadowFilterMethodIndicesDisk[index] == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
-                {
-                    ImGui::Spacing();
-                    ImGui::Text("Filtering (PCF specific)");
-                    
-                    if (m_pcfMethod[index] == PcfMethod::BoundarySearch && ScriptableImGui::SliderInt("Prediction # ##Disk", &m_predictionSampleCountsDisk[index], 4, 16))
-                    {
-                        m_diskLightFeatureProcessor->SetPredictionSampleCount(lightId, m_predictionSampleCountsDisk[index]);
-                    }
-                    if (ScriptableImGui::SliderInt("Filtering # ##Disk", &m_filteringSampleCountsDisk[index], 4, 64))
-                    {
-                        m_diskLightFeatureProcessor->SetFilteringSampleCount(lightId, m_filteringSampleCountsDisk[index]);
-                    }
+            ImGui::Spacing();
 
-                    int pcfMethodAsInteger = aznumeric_cast<int>(m_pcfMethod[index]);
-                    if (ScriptableImGui::RadioButton(
-                            "Boundary Search filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::BoundarySearch)))
-                    {
-                        m_pcfMethod[index] = PcfMethod::BoundarySearch;
-                        m_diskLightFeatureProcessor->SetPcfMethod(lightId, m_pcfMethod[index]);
-                    }
-                    if (ScriptableImGui::RadioButton(
-                            "Bicubic filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::Bicubic)))
-                    {
-                        m_pcfMethod[index] = PcfMethod::Bicubic;
-                        m_diskLightFeatureProcessor->SetPcfMethod(lightId, m_pcfMethod[index]);
-                    }
+            ImGui::Text("Filtering");
+            if (ScriptableImGui::Combo(
+                    "Filter Method##Positional", &m_shadowFilterMethodIndicesPositional[index], s_shadowFilterMethodLabels,
+                    AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
+            {
+                settingsChanged = true;
+            }
 
+            if (m_shadowFilterMethodIndicesPositional[index] != aznumeric_cast<int>(ShadowFilterMethod::None))
+            {
+                ImGui::Text("Boundary Width in degrees");
+                if (ScriptableImGui::SliderFloat("Width##Positional", &m_boundaryWidthsPositional[index], 0.f, 1.0f, "%.3f"))
+                {
+                    settingsChanged = true;
                 }
             }
-            ImGui::Unindent();
 
-            ImGui::Separator();
-
-            ImGui::Text("Camera");
-            ImGui::Indent();
+            if (m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
+                m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
             {
-                using namespace AZ;
-
-                ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f);
                 ImGui::Spacing();
+                ImGui::Text("Filtering (PCF specific)");
+
+                if (m_pcfMethod[index] == PcfMethod::BoundarySearch &&
+                    ScriptableImGui::SliderInt("Prediction # ##Positional", &m_predictionSampleCountsPositional[index], 4, 16))
+                {
+                    settingsChanged = true;
+                }
+                if (ScriptableImGui::SliderInt("Filtering # ##Positional", &m_filteringSampleCountsPositional[index], 4, 64))
+                {
+                    settingsChanged = true;
+                }
 
-                Transform cameraTransform;
-                TransformBus::EventResult(
-                    cameraTransform,
-                    GetCameraEntityId(),
-                    &TransformBus::Events::GetWorldTM);
-                const Vector3 eularDegrees = cameraTransform.GetEulerDegrees();
-                float cameraPitch = eularDegrees.GetElement(0);
-                const float cameraYaw = eularDegrees.GetElement(1);
-                if (cameraPitch > 180.f)
+                int pcfMethodAsInteger = aznumeric_cast<int>(m_pcfMethod[index]);
+                if (ScriptableImGui::RadioButton(
+                        "Boundary Search filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::BoundarySearch)))
                 {
-                    cameraPitch -= 360.f;
+                    m_pcfMethod[index] = PcfMethod::BoundarySearch;
+                    settingsChanged = true;
+                }
+                if (ScriptableImGui::RadioButton("Bicubic filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::Bicubic)))
+                {
+                    m_pcfMethod[index] = PcfMethod::Bicubic;
+                    settingsChanged = true;
                 }
-                ImGui::Text("Pitch: %f", cameraPitch);
-                ImGui::Text("Yaw:   %f", cameraYaw);
             }
-            ImGui::Unindent();
+        }
+        ImGui::Unindent();
+        return settingsChanged;
+    }
 
-            ImGui::Separator();
+    void ShadowExampleComponent::DrawSidebarCamera()
+    {
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            using namespace AZ;
 
-            if (ScriptableImGui::Button("Material Details..."))
+            ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f);
+            ImGui::Spacing();
+
+            Transform cameraTransform;
+            TransformBus::EventResult(cameraTransform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
+            const Vector3 eularDegrees = cameraTransform.GetEulerDegrees();
+            float cameraPitch = eularDegrees.GetElement(0);
+            const float cameraYaw = eularDegrees.GetElement(1);
+            if (cameraPitch > 180.f)
             {
-                m_imguiMaterialDetails.SetMaterial(m_materialInstance);
-                m_imguiMaterialDetails.OpenDialog();
+                cameraPitch -= 360.f;
             }
-
-            m_imguiSidebar.End();
+            ImGui::Text("Pitch: %f", cameraPitch);
+            ImGui::Text("Yaw:   %f", cameraYaw);
         }
-
-        m_imguiMaterialDetails.Tick();
+        ImGui::Unindent();
     }
 
     void ShadowExampleComponent::SetupDebugFlags()
@@ -767,4 +783,85 @@ namespace AtomSampleViewer
             static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
     }
 
+    void ShadowExampleComponent::DestroyDiskLights()
+    {
+        for (DiskLightHandle& handle : m_diskLightHandles)
+        {
+            m_diskLightFeatureProcessor->ReleaseLight(handle);
+        }
+    }
+
+    void ShadowExampleComponent::DestroyPointLights()
+    {
+        for (PointLightHandle& handle : m_pointLightHandles)
+        {
+            m_pointLightFeatureProcessor->ReleaseLight(handle);
+        }
+    }
+
+    void ShadowExampleComponent::ApplyDiskLightSettings()
+    {
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            auto lightHandle = m_diskLightHandles[index];
+            if (lightHandle.IsValid())
+            {
+                auto [innerConeAngle, outerConeAngle] = GetConeAnglesForLight(index);
+                m_diskLightFeatureProcessor->SetConeAngles(lightHandle, innerConeAngle, outerConeAngle);
+                m_diskLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
+                m_diskLightFeatureProcessor->SetDirection(lightHandle, GetTransformForLight(index).GetBasis(1));
+                m_diskLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
+                m_diskLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
+
+                const bool shadowEnabled = m_positionalLightShadowEnabled[index];
+                m_diskLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
+                if (shadowEnabled)
+                {
+                    m_diskLightFeatureProcessor->SetShadowmapMaxResolution(
+                        lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
+
+                    m_diskLightFeatureProcessor->SetShadowFilterMethod(
+                        lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
+
+                    m_diskLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightHandle, AZ::DegToRad(m_boundaryWidthsPositional[index]));
+                    m_diskLightFeatureProcessor->SetFilteringSampleCount(lightHandle, m_filteringSampleCountsPositional[index]);
+                    m_diskLightFeatureProcessor->SetPredictionSampleCount(lightHandle, m_predictionSampleCountsPositional[index]);
+                    m_diskLightFeatureProcessor->SetPcfMethod(lightHandle, m_pcfMethod[index]);
+                }
+            }
+        }
+    }
+
+    void ShadowExampleComponent::ApplyPointLightSettings()
+    {
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            auto lightHandle = m_pointLightHandles[index];
+            if (lightHandle.IsValid())
+            {
+
+                m_pointLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
+                m_pointLightFeatureProcessor->SetBulbRadius(lightHandle, 0.0f);
+                m_pointLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
+                m_pointLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
+
+                const bool shadowEnabled = m_positionalLightShadowEnabled[index];
+                m_pointLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
+                if (shadowEnabled)
+                {
+                    m_pointLightFeatureProcessor->SetShadowmapMaxResolution(
+                        lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
+
+                    m_pointLightFeatureProcessor->SetShadowFilterMethod(
+                        lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
+
+                    m_pointLightFeatureProcessor->SetPcfMethod(lightHandle, m_pcfMethod[index]);
+                    m_pointLightFeatureProcessor->SetFilteringSampleCount(lightHandle, m_filteringSampleCountsPositional[index]);
+                    m_pointLightFeatureProcessor->SetPredictionSampleCount(lightHandle, m_predictionSampleCountsPositional[index]);
+                    m_pointLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightHandle, AZ::DegToRad(m_boundaryWidthsPositional[index]));
+                }
+            }
+        }
+    }
+
 } // namespace AtomSampleViewer

+ 44 - 16
Gem/Code/Source/ShadowExampleComponent.h

@@ -18,6 +18,9 @@
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 
+#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
+
+
 #include <AzCore/Component/TickBus.h>
 
 #include <Utils/ImGuiSidebar.h>
@@ -51,6 +54,7 @@ namespace AtomSampleViewer
     private:
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
+        using PointLightHandle = AZ::Render::PointLightFeatureProcessorInterface::LightHandle;
         
         // AZ::TickBus::Handler overrides...
         void OnTick(float deltaTime, AZ::ScriptTimePoint time);
@@ -68,18 +72,38 @@ namespace AtomSampleViewer
         void CreateMeshes();
         void CreateDirectionalLight();
         void CreateDiskLights();
+        void CreatePointLights();
+        void DestroyDiskLights();
+        void DestroyPointLights();
+
         void SetInitialShadowParams();
         void SetupDebugFlags();
 
         void DrawSidebar();
+        void DrawSidebarDirectionalLight();
+
+        // Return true if settings have changed
+        bool DrawSidebarPositionalLights();
+
+        void DrawSidebarCamera();
+
+        void ApplyDiskLightSettings();
+        void ApplyPointLightSettings();
+
+        void UpdateDirectionalLight();
+
+        AZ::Transform GetTransformForLight(const uint32_t index) const;
+        float GetAttenuationForLight(const uint32_t index) const;
+        AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> GetRgbIntensityForLight(const uint32_t index) const;
+        AZStd::pair<float, float> GetConeAnglesForLight(const uint32_t index) const;
 
-        static constexpr uint32_t DiskLightCount = 3;
+        static constexpr uint32_t PositionalLightCount = 3;
         static constexpr float ConeAngleInnerRatio = 0.9f;
         static constexpr float CutoffIntensity = 0.1f;
         static constexpr float FarClipDistance = 20.f;
 
         static const AZ::Color DirectionalLightColor;
-        static AZ::Color s_diskLightColors[DiskLightCount];
+        static AZ::Color s_positionalLightColors[PositionalLightCount];
         
         // Mesh Handles
         using MeshHandle = AZ::Render::MeshFeatureProcessorInterface::MeshHandle;
@@ -89,8 +113,10 @@ namespace AtomSampleViewer
         // lights
         AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
         AZ::Render::DiskLightFeatureProcessorInterface* m_diskLightFeatureProcessor = nullptr;
+        AZ::Render::PointLightFeatureProcessorInterface* m_pointLightFeatureProcessor = nullptr;
         DirectionalLightHandle m_directionalLightHandle;
-        DiskLightHandle m_diskLightHandles[DiskLightCount];
+        DiskLightHandle m_diskLightHandles[PositionalLightCount];
+        PointLightHandle m_pointLightHandles[PositionalLightCount];
 
         // asset
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_bunnyModelAsset;
@@ -108,23 +134,24 @@ namespace AtomSampleViewer
         // GUI
         float m_elapsedTime = 0.f;
 
-        int m_controlTargetDiskLightIndex = 0;
+        int m_positionalLightTypeActive = 0;
+        int m_controlTargetPositionalLightIndex = 0;
         float m_directionalLightRotationAngle = 0.f; // in radian
-        float m_diskLightRotationAngle = 0.f; // in radian
+        float m_positionalLightRotationAngle = 0.f; // in radian
         bool m_isDirectionalLightAutoRotate = true;
-        bool m_isDiskLightAutoRotate = true;
+        bool m_isPositionalLightAutoRotate = true;
         float m_directionalLightHeight = 10.f;
-        float m_diskLightHeights[DiskLightCount] = {5.f, 6.f, 7.f};
+        float m_positionalLightHeights[PositionalLightCount] = {5.f, 6.f, 7.f};
         float m_directionalLightIntensity = 5.f;
-        float m_diskLightIntensities[DiskLightCount] = {500.f, 900.f, 500.f};
-        float m_outerConeAngles[DiskLightCount] = {17.5f, 20.f, 22.5f};
+        float m_lightIntensities[PositionalLightCount] = {500.f, 900.f, 500.f};
+        float m_outerConeAngles[PositionalLightCount] = {17.5f, 20.f, 22.5f};
         float m_cameraFovY = AZ::Constants::QuarterPi;
 
         // Shadowmap
         static const AZ::Render::ShadowmapSize s_shadowmapImageSizes[];
         static const char* s_shadowmapImageSizeLabels[];
         int m_directionalLightImageSizeIndex = 2; // image size is 1024.
-        int m_diskLightImageSizeIndices[DiskLightCount] = {2, 2, 2}; // image size is 1024.
+        int m_positionalLightImageSizeIndices[PositionalLightCount] = {2, 2, 2}; // image size is 1024.
         int m_cascadeCount = 2;
         bool m_shadowmapFrustumSplitIsAutomatic = true;
         float m_ratioLogarithmUniform = 0.5f;
@@ -138,20 +165,21 @@ namespace AtomSampleViewer
         bool m_isCascadeCorrectionEnabled = false;
         bool m_isDebugColoringEnabled = false;
         bool m_isDebugBoundingBoxEnabled = false;
-        bool m_diskLightShadowEnabled[DiskLightCount] = {true, true, true};
+        bool m_positionalLightShadowEnabled[PositionalLightCount] = {true, true, true};
+        bool m_pointLightShadowEnabled[PositionalLightCount] = {true, true, true};
 
         // Edge-softening of shadows
         static const AZ::Render::ShadowFilterMethod s_shadowFilterMethods[];
         static const char* s_shadowFilterMethodLabels[];
         int m_shadowFilterMethodIndexDirectional = 0; // filter method is None.
-        int m_shadowFilterMethodIndicesDisk[DiskLightCount] = { 0, 0, 0 }; // filter method is None.
+        int m_shadowFilterMethodIndicesPositional[PositionalLightCount] = { 0, 0, 0 }; // filter method is None.
         float m_boundaryWidthDirectional = 0.03f; // 3cm
-        float m_boundaryWidthsDisk[DiskLightCount] = { 0.25f, 0.25f, 0.25f }; // 0.25 degrees
+        float m_boundaryWidthsPositional[PositionalLightCount] = { 0.25f, 0.25f, 0.25f }; // 0.25 degrees
         int m_predictionSampleCountDirectional = 8;
-        int m_predictionSampleCountsDisk[DiskLightCount] = { 8, 8, 8 };
+        int m_predictionSampleCountsPositional[PositionalLightCount] = { 8, 8, 8 };
         int m_filteringSampleCountDirectional = 32;
-        int m_filteringSampleCountsDisk[DiskLightCount] = { 32, 32, 32 };
-        AZ::Render::PcfMethod m_pcfMethod[DiskLightCount] = {
+        int m_filteringSampleCountsPositional[PositionalLightCount] = { 32, 32, 32 };
+        AZ::Render::PcfMethod m_pcfMethod[PositionalLightCount] = {
             AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch};
 
         ImGuiSidebar m_imguiSidebar;

+ 27 - 27
Scripts/ShadowTest.bv.lua

@@ -19,24 +19,24 @@ OpenSample('Features/Shadow')
 ResizeViewport(800, 600)
 SelectImageComparisonToleranceLevel("Level H")
 SetImguiValue('Auto Rotation##Directional', false)
-SetImguiValue('Auto Rotation##Disk', false)
+SetImguiValue('Auto Rotation##Positional', false)
 
 -- Initial
 SetImguiValue('Direction##Directional', 0.0)
-SetImguiValue('Base Direction##Disk', 0.0)
+SetImguiValue('Base Direction##Positional', 0.0)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/initial.ppm')
 
 -- Directional Light Initial
--- Diabling Disk lights
+-- Diabling Positional lights
 SetImguiValue('Red', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 IdleFrames(1)
 SetImguiValue('Green', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 IdleFrames(1)
 SetImguiValue('Blue', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 -- Set Camera
 ArcBallCameraController_SetHeading(DegToRad(90.0))
 ArcBallCameraController_SetPitch(DegToRad(-45.0))
@@ -98,60 +98,60 @@ SetImguiValue('Filter Method##Directional', 'ESM+PCF')
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/directional_esm_pcf.ppm')
 
--- Disk Light Initial
+-- Positional Light Initial
 -- Disabling directional light
 SetImguiValue('Intensity##Directional', 0.0)
 SetImguiValue('Red', true)
-SetImguiValue('Intensity##Disk', 500.0)
+SetImguiValue('Intensity##Positional', 500.0)
 IdleFrames(1)
 SetImguiValue('Green', true)
-SetImguiValue('Height##Disk', 4.0)
-SetImguiValue('Intensity##Disk', 400.0)
+SetImguiValue('Height##Positional', 4.0)
+SetImguiValue('Intensity##Positional', 400.0)
 IdleFrames(1)
 SetImguiValue('Blue', true)
-SetImguiValue('Intensity##Disk', 500.0)
+SetImguiValue('Intensity##Positional', 500.0)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_initial.ppm')
 
--- Disk Light Disabling Shadow for Red
+-- Positional Light Disabling Shadow for Red
 SetImguiValue('Red', true)
 SetImguiValue('Enable Shadow', false)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_no_red_shadow.ppm')
 
--- Disk Light Various Shadowmap Sizes
+-- Positional Light Various Shadowmap Sizes
 SetImguiValue('Red', true)
 SetImguiValue('Enable Shadow', true)
 IdleFrames(1)
-SetImguiValue('Size##Disk', '2048')
+SetImguiValue('Size##Positional', '2048')
 IdleFrames(1)
 SetImguiValue('Green', true)
-SetImguiValue('Size##Disk', '1024')
+SetImguiValue('Size##Positional', '1024')
 IdleFrames(1)
 SetImguiValue('Blue', true)
-SetImguiValue('Size##Disk', '512')
+SetImguiValue('Size##Positional', '512')
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_shadowmap_size.ppm')
 
--- Disk Light Various Filter Method
+-- Positional Light Various Filter Method
 SetImguiValue('Red', true)
-SetImguiValue('Filter Method##Disk', 'PCF')
+SetImguiValue('Filter Method##Positional', 'PCF')
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
-SetImguiValue('Prediction # ##Disk', 16)
-SetImguiValue('Filtering # ##Disk', 64)
+SetImguiValue('Width##Positional', 0.5)
+SetImguiValue('Prediction # ##Positional', 16)
+SetImguiValue('Filtering # ##Positional', 64)
 IdleFrames(1)
 SetImguiValue('Green', true)
-SetImguiValue('Filter Method##Disk', 'ESM')
+SetImguiValue('Filter Method##Positional', 'ESM')
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
+SetImguiValue('Width##Positional', 0.5)
 IdleFrames(1)
 SetImguiValue('Blue', true)
-SetImguiValue('Filter Method##Disk', 'ESM+PCF')
+SetImguiValue('Filter Method##Positional', 'ESM+PCF')
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
-SetImguiValue('Prediction # ##Disk', 16)
-SetImguiValue('Filtering # ##Disk', 64)
+SetImguiValue('Width##Positional', 0.5)
+SetImguiValue('Prediction # ##Positional', 16)
+SetImguiValue('Filtering # ##Positional', 64)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_filter_method.ppm')