Преглед изворни кода

Merge branch 'main' into Atom/santorac/NewLayeringWorkflow

Chris Santora пре 4 година
родитељ
комит
0f466086ee
37 измењених фајлова са 762 додато и 392 уклоњено
  1. 1 1
      Gem/Code/Source/AreaLightExampleComponent.cpp
  2. 6 6
      Gem/Code/Source/Automation/ScriptableImGui.cpp
  3. 3 3
      Gem/Code/Source/Automation/ScriptableImGui.h
  4. 2 2
      Gem/Code/Source/CullingAndLodExampleComponent.cpp
  5. 14 0
      Gem/Code/Source/Platform/Android/SceneReloadSoakTestComponent_Traits_Platform.h
  6. 1 0
      Gem/Code/Source/Platform/Android/atomsampleviewer_android_files.cmake
  7. 13 0
      Gem/Code/Source/Platform/Linux/SceneReloadSoakTestComponent_Traits_Platform.h
  8. 1 0
      Gem/Code/Source/Platform/Linux/atomsampleviewer_linux_files.cmake
  9. 13 0
      Gem/Code/Source/Platform/Mac/SceneReloadSoakTestComponent_Traits_Platform.h
  10. 1 0
      Gem/Code/Source/Platform/Mac/atomsampleviewer_mac_files.cmake
  11. 13 0
      Gem/Code/Source/Platform/Windows/SceneReloadSoakTestComponent_Traits_Platform.h
  12. 1 0
      Gem/Code/Source/Platform/Windows/atomsampleviewer_windows_files.cmake
  13. 13 0
      Gem/Code/Source/Platform/iOS/SceneReloadSoakTestComponent_Traits_Platform.h
  14. 1 0
      Gem/Code/Source/Platform/iOS/atomsampleviewer_ios_files.cmake
  15. 9 5
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  16. 3 0
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h
  17. 1 1
      Gem/Code/Source/SampleComponentManager.cpp
  18. 3 1
      Gem/Code/Source/SceneReloadSoakTestComponent.cpp
  19. 423 324
      Gem/Code/Source/ShadowExampleComponent.cpp
  20. 44 16
      Gem/Code/Source/ShadowExampleComponent.h
  21. 2 2
      Gem/Code/Source/ShadowedBistroExampleComponent.cpp
  22. 1 1
      Gem/Code/Source/SkinnedMeshExampleComponent.cpp
  23. 3 0
      Scripts/ExpectedScreenshots/CopyQueue/screenshot_warp_CopyQueue.png
  24. 3 0
      Scripts/ExpectedScreenshots/DualSourceBlending/screenshot_warp_DualSourceBlending.png
  25. 3 0
      Scripts/ExpectedScreenshots/InputAssembly/screenshot_warp_InputAssembly.png
  26. 3 0
      Scripts/ExpectedScreenshots/MSAA/screenshot_warp_MSAA.png
  27. 3 0
      Scripts/ExpectedScreenshots/MultiRenderTarget/screenshot_warp_MultiRenderTarget.png
  28. 3 0
      Scripts/ExpectedScreenshots/MultiThread/screenshot_warp_MultiThread.png
  29. 3 0
      Scripts/ExpectedScreenshots/Stencil/screenshot_warp_Stencil.png
  30. 3 0
      Scripts/ExpectedScreenshots/Texture/screenshot_warp_Texture.png
  31. 3 0
      Scripts/ExpectedScreenshots/Texture3d/screenshot_warp_Texture3d.png
  32. 3 0
      Scripts/ExpectedScreenshots/TextureMap/screenshot_warp_TextureMap.png
  33. 27 27
      Scripts/ShadowTest.bv.lua
  34. 48 0
      Scripts/_AutomatedReviewWARPTestSuite_.bv.lua
  35. 4 2
      Shaders/StaticMesh.azsl
  36. 70 0
      Standalone/PythonTests/Automated/test_AtomSampleViewer_warp_suite.py
  37. 14 1
      Standalone/PythonTests/CMakeLists.txt

+ 1 - 1
Gem/Code/Source/AreaLightExampleComponent.cpp

@@ -595,7 +595,7 @@ namespace AtomSampleViewer
             ScriptableImGui::EndCombo();
             ScriptableImGui::EndCombo();
         }
         }
 
 
-        if (ScriptableImGui::SliderFloat("Lumens", &m_config.m_intensity, 0.0f, 1000.0f, "%.3f", 2.0f))
+        if (ScriptableImGui::SliderFloat("Lumens", &m_config.m_intensity, 0.0f, 1000.0f, "%.3f", ImGuiSliderFlags_Logarithmic))
         {
         {
             lightsNeedUpdate = true;
             lightsNeedUpdate = true;
         }
         }

+ 6 - 6
Gem/Code/Source/Automation/ScriptableImGui.cpp

@@ -338,11 +338,11 @@ namespace AtomSampleViewer
         return ActionHelper<float>(label, imguiAction, reportScriptableAction, handleScriptedAction);
         return ActionHelper<float>(label, imguiAction, reportScriptableAction, handleScriptedAction);
     }
     }
 
 
-    bool ScriptableImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
+    bool ScriptableImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
     {
     {
         auto imguiAction = [&]()
         auto imguiAction = [&]()
         {
         {
-            return ImGui::SliderFloat(label, v, v_min, v_max, format, power);
+            return ImGui::SliderFloat(label, v, v_min, v_max, format, flags);
         };
         };
 
 
         auto reportScriptableAction = [&](const AZStd::string& pathToImGuiItem)
         auto reportScriptableAction = [&](const AZStd::string& pathToImGuiItem)
@@ -359,11 +359,11 @@ namespace AtomSampleViewer
         return ActionHelper<float>(label, imguiAction, reportScriptableAction, handleScriptedAction);
         return ActionHelper<float>(label, imguiAction, reportScriptableAction, handleScriptedAction);
     }
     }
 
 
-    bool ScriptableImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
+    bool ScriptableImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
     {
     {
         auto imguiAction = [&]()
         auto imguiAction = [&]()
         {
         {
-            return ImGui::SliderFloat2(label, v, v_min, v_max, format, power);
+            return ImGui::SliderFloat2(label, v, v_min, v_max, format, flags);
         };
         };
 
 
         auto reportScriptableAction = [&](const AZStd::string& pathToImGuiItem)
         auto reportScriptableAction = [&](const AZStd::string& pathToImGuiItem)
@@ -400,11 +400,11 @@ namespace AtomSampleViewer
         return ActionHelper<AZ::Vector3>(label, imguiAction, reportScriptableAction, handleScriptedAction);
         return ActionHelper<AZ::Vector3>(label, imguiAction, reportScriptableAction, handleScriptedAction);
     }
     }
 
 
-    bool ScriptableImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
+    bool ScriptableImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
     {
     {
         auto imguiAction = [&]()
         auto imguiAction = [&]()
         {
         {
-            return ImGui::SliderFloat3(label, v, v_min, v_max, format, power);
+            return ImGui::SliderFloat3(label, v, v_min, v_max, format, flags);
         };
         };
 
 
         return ThreeComponentHelper(label, v, imguiAction);
         return ThreeComponentHelper(label, v, imguiAction);

+ 3 - 3
Gem/Code/Source/Automation/ScriptableImGui.h

@@ -72,9 +72,9 @@ namespace AtomSampleViewer
         static bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);
         static bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);
 
 
         static bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d");
         static bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d");
-        static bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
-        static bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
-        static bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
+        static bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+        static bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+        static bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
         static bool SliderAngle(const char* label, float* v, float v_min = -360.0f, float v_max = 360.0f, const char* format = "%.0f deg");
         static bool SliderAngle(const char* label, float* v, float v_min = -360.0f, float v_max = 360.0f, const char* format = "%.0f deg");
 
 
         static bool ColorEdit3(const char* label, float v[3], ImGuiColorEditFlags flags);
         static bool ColorEdit3(const char* label, float v[3], ImGuiColorEditFlags flags);

+ 2 - 2
Gem/Code/Source/CullingAndLodExampleComponent.cpp

@@ -338,7 +338,7 @@ namespace AtomSampleViewer
             const auto lightTrans = Transform::CreateRotationZ(m_directionalLightYaw) * Transform::CreateRotationX(m_directionalLightPitch);
             const auto lightTrans = Transform::CreateRotationZ(m_directionalLightYaw) * Transform::CreateRotationX(m_directionalLightPitch);
             m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTrans.GetBasis(1));
             m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTrans.GetBasis(1));
 
 
-            if (ImGui::SliderFloat("Intensity##directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", 2.f))
+            if (ImGui::SliderFloat("Intensity##directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
             {
             {
                 m_directionalLightFeatureProcessor->SetRgbIntensity(
                 m_directionalLightFeatureProcessor->SetRgbIntensity(
                     m_directionalLightHandle,
                     m_directionalLightHandle,
@@ -432,7 +432,7 @@ namespace AtomSampleViewer
                 UpdateDiskLightCount(diskLightCount);
                 UpdateDiskLightCount(diskLightCount);
             }
             }
 
 
-            if (ImGui::SliderFloat("Intensity##disk", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", 4.f))
+            if (ImGui::SliderFloat("Intensity##disk", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
             {
             {
                 for (const DiskLight& light : m_diskLights)
                 for (const DiskLight& light : m_diskLights)
                 {
                 {

+ 14 - 0
Gem/Code/Source/Platform/Android/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,14 @@
+/*
+* 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.
+*
+*/
+
+// [ATOM-15367] On more limited devices the large lattice size would run into out of memory error, specifically "device lost" error
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       2

+ 1 - 0
Gem/Code/Source/Platform/Android/atomsampleviewer_android_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Android.cpp
     AtomSampleViewerOptions_Android.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Android.cpp
     SampleComponentManager_Android.cpp

+ 13 - 0
Gem/Code/Source/Platform/Linux/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Linux/atomsampleviewer_linux_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Linux.cpp
     AtomSampleViewerOptions_Linux.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Linux.cpp
     SampleComponentManager_Linux.cpp

+ 13 - 0
Gem/Code/Source/Platform/Mac/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Mac/atomsampleviewer_mac_files.cmake

@@ -13,6 +13,7 @@ set(FILES
     ../../../../Resources/MacLauncher/Info.plist
     ../../../../Resources/MacLauncher/Info.plist
     AtomSampleViewerOptions_Mac.cpp
     AtomSampleViewerOptions_Mac.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Mac.cpp
     SampleComponentManager_Mac.cpp

+ 13 - 0
Gem/Code/Source/Platform/Windows/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Windows/atomsampleviewer_windows_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Windows.cpp
     AtomSampleViewerOptions_Windows.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Windows.cpp
     SampleComponentManager_Windows.cpp

+ 13 - 0
Gem/Code/Source/Platform/iOS/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/iOS/atomsampleviewer_ios_files.cmake

@@ -13,6 +13,7 @@ set(FILES
     ../../../../Resources/IOSLauncher/Info.plist
     ../../../../Resources/IOSLauncher/Info.plist
     AtomSampleViewerOptions_iOS.cpp
     AtomSampleViewerOptions_iOS.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_iOS.cpp
     SampleComponentManager_iOS.cpp

+ 9 - 5
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -466,15 +466,19 @@ namespace AtomSampleViewer
         // Set the images
         // Set the images
         {
         {
             Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
             Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
-            AZ::RHI::ShaderInputImageIndex imageIndex = imageSrg->FindShaderInputImageIndex(AZ::Name(textureArrayId));
+            AZ::RHI::ShaderInputImageUnboundedArrayIndex imageIndex = imageSrg->FindShaderInputImageUnboundedArrayIndex(AZ::Name(textureArrayId));
 
 
-            for (uint32_t textuerIdx = 0u; textuerIdx < InternalBP::ImageCount; textuerIdx++)
+            AZStd::vector<const RHI::ImageView*> imageViews;
+            for (uint32_t textureIdx = 0u; textureIdx < InternalBP::ImageCount; textureIdx++)
             {
             {
-                AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textuerIdx], InternalBP::SampleName);
-                [[maybe_unused]] bool result = imageSrg->SetImage(imageIndex, image, textuerIdx);
-                AZ_Assert(result, "Failed to set image into shader resource group.");
+                AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textureIdx], InternalBP::SampleName);
+                m_images.push_back(image);
+                imageViews.push_back(image->GetImageView());
             }
             }
 
 
+            [[maybe_unused]] bool result = imageSrg->SetImageViewUnboundedArray(imageIndex, imageViews);
+            AZ_Assert(result, "Failed to set image view unbounded array into shader resource group.");
+
             // Compile the image SRG
             // Compile the image SRG
             imageSrg->Compile();
             imageSrg->Compile();
         }
         }

+ 3 - 0
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h

@@ -219,6 +219,9 @@ namespace AtomSampleViewer
         // Handle array holding the FloatBuffer handles for all objects
         // Handle array holding the FloatBuffer handles for all objects
         AZStd::vector<FloatBufferHandle> m_objectHandleArray;
         AZStd::vector<FloatBufferHandle> m_objectHandleArray;
 
 
+        // Image array holding all of the StreamImages
+        AZStd::vector<AZ::Data::Instance<AZ::RPI::StreamingImage>> m_images;
+
         // Light direction handle
         // Light direction handle
         FloatBufferHandle m_lightDirectionHandle;
         FloatBufferHandle m_lightDirectionHandle;
 
 

+ 1 - 1
Gem/Code/Source/SampleComponentManager.cpp

@@ -233,7 +233,7 @@ namespace AtomSampleViewer
 
 
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AlphaToCoverage", azrtti_typeid<AlphaToCoverageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AlphaToCoverage", azrtti_typeid<AlphaToCoverageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AsyncCompute", azrtti_typeid<AsyncComputeExampleComponent>() ) );
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AsyncCompute", azrtti_typeid<AsyncComputeExampleComponent>() ) );
-        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/BindlessPrototype", azrtti_typeid<BindlessPrototypeExampleComponent>() ));
+        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/BindlessPrototype", azrtti_typeid<BindlessPrototypeExampleComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_unboundedArrays; } ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/Compute", azrtti_typeid<ComputeExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/Compute", azrtti_typeid<ComputeExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/CopyQueue", azrtti_typeid<CopyQueueComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/CopyQueue", azrtti_typeid<CopyQueueComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/DualSourceBlending", azrtti_typeid<DualSourceBlendingComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_dualSourceBlending; } ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/DualSourceBlending", azrtti_typeid<DualSourceBlendingComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_dualSourceBlending; } ));

+ 3 - 1
Gem/Code/Source/SceneReloadSoakTestComponent.cpp

@@ -23,6 +23,8 @@
 
 
 #include <RHI/BasicRHIComponent.h>
 #include <RHI/BasicRHIComponent.h>
 
 
+#include <SceneReloadSoakTestComponent_Traits_Platform.h>
+
 namespace AtomSampleViewer
 namespace AtomSampleViewer
 {
 {
     using namespace AZ;
     using namespace AZ;
@@ -52,7 +54,7 @@ namespace AtomSampleViewer
         m_currentCount = 0;
         m_currentCount = 0;
         m_totalResetCount = 0;
         m_totalResetCount = 0;
 
 
-        SetLatticeDimensions(5, 5, 5);
+        SetLatticeDimensions(ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE, ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE, ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE);
         Base::Activate();
         Base::Activate();
 
 
         TickBus::Handler::BusConnect();
         TickBus::Handler::BusConnect();

+ 423 - 324
Gem/Code/Source/ShadowExampleComponent.cpp

@@ -29,6 +29,8 @@
 #include <SampleComponentConfig.h>
 #include <SampleComponentConfig.h>
 
 
 #include <RHI/BasicRHIComponent.h>
 #include <RHI/BasicRHIComponent.h>
+#include <Atom/RPI.Public/ColorManagement/TransformColor.h>
+
 
 
 namespace AtomSampleViewer
 namespace AtomSampleViewer
 {
 {
@@ -39,12 +41,13 @@ namespace AtomSampleViewer
     };
     };
 
 
     const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
     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.
         // they will be initialized in the constructor.
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero()
         AZ::Color::CreateZero()
     };
     };
+
     const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = 
     const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = 
     {
     {
         AZ::Render::ShadowmapSize::Size256,
         AZ::Render::ShadowmapSize::Size256,
@@ -77,9 +80,9 @@ namespace AtomSampleViewer
     ShadowExampleComponent::ShadowExampleComponent()
     ShadowExampleComponent::ShadowExampleComponent()
         : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml")
         : 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)
     void ShadowExampleComponent::Reflect(AZ::ReflectContext* context)
@@ -123,9 +126,9 @@ namespace AtomSampleViewer
         m_floorMeshIsReady = false;
         m_floorMeshIsReady = false;
 
 
         m_directionalLightImageSizeIndex = 2; // image size is 1024.
         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_cascadeCount = 2;
         m_ratioLogarithmUniform = 0.5f;
         m_ratioLogarithmUniform = 0.5f;
@@ -135,6 +138,7 @@ namespace AtomSampleViewer
         RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         m_directionalLightFeatureProcessor = scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
         m_directionalLightFeatureProcessor = scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
         m_diskLightFeatureProcessor = scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
         m_diskLightFeatureProcessor = scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
+        m_pointLightFeatureProcessor = scene->GetFeatureProcessor<Render::PointLightFeatureProcessorInterface>();
 
 
         CreateMeshes();
         CreateMeshes();
         CreateDirectionalLight();
         CreateDirectionalLight();
@@ -154,12 +158,10 @@ namespace AtomSampleViewer
 
 
         GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
+        DestroyDiskLights();
+        DestroyPointLights();
 
 
         m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
         m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
-        for (DiskLightHandle& handle : m_diskLightHandles)
-        {
-            m_diskLightFeatureProcessor->ReleaseLight(handle);
-        }
 
 
         m_imguiSidebar.Deactivate();
         m_imguiSidebar.Deactivate();
     }
     }
@@ -168,9 +170,7 @@ namespace AtomSampleViewer
     {
     {
         using namespace AZ;
         using namespace AZ;
         constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position.
         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 diskLightDist = 5.f;
+        constexpr float lightPeriodTime = 7.f; // 7 seconds for a rotation of light positions.
 
 
         if (m_elapsedTime == 0.f)
         if (m_elapsedTime == 0.f)
         {
         {
@@ -185,67 +185,98 @@ namespace AtomSampleViewer
         {
         {
             m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi);
             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 / lightPeriodTime + 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(
         Camera::CameraRequestBus::Event(
             GetCameraEntityId(),
             GetCameraEntityId(),
             &Camera::CameraRequestBus::Events::SetFovRadians,
             &Camera::CameraRequestBus::Events::SetFovRadians,
             m_cameraFovY);
             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
         {
         {
             Camera::Configuration config;
             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
         // Camera Transform
         {
         {
             Transform transform = Transform::CreateIdentity();
             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()
     void ShadowExampleComponent::SaveCameraConfiguration()
@@ -401,34 +432,33 @@ namespace AtomSampleViewer
 
 
     void ShadowExampleComponent::CreateDiskLights()
     void ShadowExampleComponent::CreateDiskLights()
     {
     {
+        DestroyDiskLights();
+
         using namespace AZ;
         using namespace AZ;
         Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
         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();
             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;
             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()
     void ShadowExampleComponent::DrawSidebar()
@@ -439,317 +469,305 @@ namespace AtomSampleViewer
         {
         {
             ImGui::Spacing();
             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))
+        {
+            ScriptableImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
+
+            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", ImGuiSliderFlags_Logarithmic))
+            {
+                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)))
                     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)
                     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)))
                     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();
                 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", ImGuiSliderFlags_Logarithmic);
 
 
-                    // 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", ImGuiSliderFlags_Logarithmic))
+            {
+                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::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()
     void ShadowExampleComponent::SetupDebugFlags()
@@ -767,4 +785,85 @@ namespace AtomSampleViewer
             static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
             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
 } // namespace AtomSampleViewer

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

@@ -18,6 +18,9 @@
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 
 
+#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
+
+
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/Component/TickBus.h>
 
 
 #include <Utils/ImGuiSidebar.h>
 #include <Utils/ImGuiSidebar.h>
@@ -51,6 +54,7 @@ namespace AtomSampleViewer
     private:
     private:
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
+        using PointLightHandle = AZ::Render::PointLightFeatureProcessorInterface::LightHandle;
         
         
         // AZ::TickBus::Handler overrides...
         // AZ::TickBus::Handler overrides...
         void OnTick(float deltaTime, AZ::ScriptTimePoint time);
         void OnTick(float deltaTime, AZ::ScriptTimePoint time);
@@ -68,18 +72,38 @@ namespace AtomSampleViewer
         void CreateMeshes();
         void CreateMeshes();
         void CreateDirectionalLight();
         void CreateDirectionalLight();
         void CreateDiskLights();
         void CreateDiskLights();
+        void CreatePointLights();
+        void DestroyDiskLights();
+        void DestroyPointLights();
+
         void SetInitialShadowParams();
         void SetInitialShadowParams();
         void SetupDebugFlags();
         void SetupDebugFlags();
 
 
         void DrawSidebar();
         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 ConeAngleInnerRatio = 0.9f;
         static constexpr float CutoffIntensity = 0.1f;
         static constexpr float CutoffIntensity = 0.1f;
         static constexpr float FarClipDistance = 20.f;
         static constexpr float FarClipDistance = 20.f;
 
 
         static const AZ::Color DirectionalLightColor;
         static const AZ::Color DirectionalLightColor;
-        static AZ::Color s_diskLightColors[DiskLightCount];
+        static AZ::Color s_positionalLightColors[PositionalLightCount];
         
         
         // Mesh Handles
         // Mesh Handles
         using MeshHandle = AZ::Render::MeshFeatureProcessorInterface::MeshHandle;
         using MeshHandle = AZ::Render::MeshFeatureProcessorInterface::MeshHandle;
@@ -89,8 +113,10 @@ namespace AtomSampleViewer
         // lights
         // lights
         AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
         AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
         AZ::Render::DiskLightFeatureProcessorInterface* m_diskLightFeatureProcessor = nullptr;
         AZ::Render::DiskLightFeatureProcessorInterface* m_diskLightFeatureProcessor = nullptr;
+        AZ::Render::PointLightFeatureProcessorInterface* m_pointLightFeatureProcessor = nullptr;
         DirectionalLightHandle m_directionalLightHandle;
         DirectionalLightHandle m_directionalLightHandle;
-        DiskLightHandle m_diskLightHandles[DiskLightCount];
+        DiskLightHandle m_diskLightHandles[PositionalLightCount];
+        PointLightHandle m_pointLightHandles[PositionalLightCount];
 
 
         // asset
         // asset
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_bunnyModelAsset;
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_bunnyModelAsset;
@@ -108,23 +134,24 @@ namespace AtomSampleViewer
         // GUI
         // GUI
         float m_elapsedTime = 0.f;
         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_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_isDirectionalLightAutoRotate = true;
-        bool m_isDiskLightAutoRotate = true;
+        bool m_isPositionalLightAutoRotate = true;
         float m_directionalLightHeight = 10.f;
         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_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;
         float m_cameraFovY = AZ::Constants::QuarterPi;
 
 
         // Shadowmap
         // Shadowmap
         static const AZ::Render::ShadowmapSize s_shadowmapImageSizes[];
         static const AZ::Render::ShadowmapSize s_shadowmapImageSizes[];
         static const char* s_shadowmapImageSizeLabels[];
         static const char* s_shadowmapImageSizeLabels[];
         int m_directionalLightImageSizeIndex = 2; // image size is 1024.
         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;
         int m_cascadeCount = 2;
         bool m_shadowmapFrustumSplitIsAutomatic = true;
         bool m_shadowmapFrustumSplitIsAutomatic = true;
         float m_ratioLogarithmUniform = 0.5f;
         float m_ratioLogarithmUniform = 0.5f;
@@ -138,20 +165,21 @@ namespace AtomSampleViewer
         bool m_isCascadeCorrectionEnabled = false;
         bool m_isCascadeCorrectionEnabled = false;
         bool m_isDebugColoringEnabled = false;
         bool m_isDebugColoringEnabled = false;
         bool m_isDebugBoundingBoxEnabled = 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
         // Edge-softening of shadows
         static const AZ::Render::ShadowFilterMethod s_shadowFilterMethods[];
         static const AZ::Render::ShadowFilterMethod s_shadowFilterMethods[];
         static const char* s_shadowFilterMethodLabels[];
         static const char* s_shadowFilterMethodLabels[];
         int m_shadowFilterMethodIndexDirectional = 0; // filter method is None.
         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_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_predictionSampleCountDirectional = 8;
-        int m_predictionSampleCountsDisk[DiskLightCount] = { 8, 8, 8 };
+        int m_predictionSampleCountsPositional[PositionalLightCount] = { 8, 8, 8 };
         int m_filteringSampleCountDirectional = 32;
         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};
             AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch};
 
 
         ImGuiSidebar m_imguiSidebar;
         ImGuiSidebar m_imguiSidebar;

+ 2 - 2
Gem/Code/Source/ShadowedBistroExampleComponent.cpp

@@ -410,7 +410,7 @@ namespace AtomSampleViewer
             ScriptableImGui::SliderAngle("Pitch", &m_directionalLightPitch, -90.0f, 0.f);
             ScriptableImGui::SliderAngle("Pitch", &m_directionalLightPitch, -90.0f, 0.f);
             ScriptableImGui::SliderAngle("Yaw", &m_directionalLightYaw, 0.f, 360.f);
             ScriptableImGui::SliderAngle("Yaw", &m_directionalLightYaw, 0.f, 360.f);
 
 
-            if (ScriptableImGui::SliderFloat("Intensity##directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", 2.f))
+            if (ScriptableImGui::SliderFloat("Intensity##directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
             {
             {
                 AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
                 AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
                 m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
                 m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
@@ -551,7 +551,7 @@ namespace AtomSampleViewer
                 UpdateDiskLightCount(diskLightCount);
                 UpdateDiskLightCount(diskLightCount);
             }
             }
 
 
-            if (ScriptableImGui::SliderFloat("Intensity##spot", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", 4.f))
+            if (ScriptableImGui::SliderFloat("Intensity##spot", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
             {
             {
                 for (const DiskLight& light : m_diskLights)
                 for (const DiskLight& light : m_diskLights)
                 {
                 {

+ 1 - 1
Gem/Code/Source/SkinnedMeshExampleComponent.cpp

@@ -115,7 +115,7 @@ namespace AtomSampleViewer
 
 
         // Imgui limits slider range to half the natural range of the type
         // Imgui limits slider range to half the natural range of the type
         float segmentCountFloat = static_cast<float>(config.m_segmentCount);
         float segmentCountFloat = static_cast<float>(config.m_segmentCount);
-        configWasModified |= ScriptableImGui::SliderFloat("Segments Per-Mesh", &segmentCountFloat, 2.0f, 2048.0f, "%.0f", 2.0f);
+        configWasModified |= ScriptableImGui::SliderFloat("Segments Per-Mesh", &segmentCountFloat, 2.0f, 2048.0f, "%.0f", ImGuiSliderFlags_Logarithmic);
         configWasModified |= ScriptableImGui::SliderInt("Vertices Per-Segment", &config.m_verticesPerSegment, 4, 2048);
         configWasModified |= ScriptableImGui::SliderInt("Vertices Per-Segment", &config.m_verticesPerSegment, 4, 2048);
         configWasModified |= ScriptableImGui::SliderInt("Bones Per-Mesh", &config.m_boneCount, 2, 256);
         configWasModified |= ScriptableImGui::SliderInt("Bones Per-Mesh", &config.m_boneCount, 2, 256);
         configWasModified |= ScriptableImGui::SliderInt("Influences Per-Vertex", &config.m_influencesPerVertex, 1, 4);
         configWasModified |= ScriptableImGui::SliderInt("Influences Per-Vertex", &config.m_influencesPerVertex, 1, 4);

+ 3 - 0
Scripts/ExpectedScreenshots/CopyQueue/screenshot_warp_CopyQueue.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c7ffbad8c205f9335b3605230adbd081ff4e0807a85bd1411a675ab51363bba4
+size 361583

+ 3 - 0
Scripts/ExpectedScreenshots/DualSourceBlending/screenshot_warp_DualSourceBlending.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3849c8b2a75525dbea09c79a27b889b09883cb5098911f6500dde43dbca1f4f6
+size 105474

+ 3 - 0
Scripts/ExpectedScreenshots/InputAssembly/screenshot_warp_InputAssembly.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b253499e35fea04d771415e575851d9dedf401f3626b6db4b2cb90f5bd6396be
+size 28731

+ 3 - 0
Scripts/ExpectedScreenshots/MSAA/screenshot_warp_MSAA.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3ecc97d79ef6aefe987686eef829ec498f74a94792cdeb1732c58886f50cb58
+size 131616

+ 3 - 0
Scripts/ExpectedScreenshots/MultiRenderTarget/screenshot_warp_MultiRenderTarget.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:40a765267ffdfa1be068cb983990af3011933e823005645025d4cbafb5e66e1a
+size 27204

+ 3 - 0
Scripts/ExpectedScreenshots/MultiThread/screenshot_warp_MultiThread.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c39bb140d8ff26a837fea25b4f9859f2ca130b87ccbfb832dd340381123f3199
+size 40320

+ 3 - 0
Scripts/ExpectedScreenshots/Stencil/screenshot_warp_Stencil.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3e935daee94252b93ed7880a9ccc476841e7d59157f1d0923c6c82fd658fd1e
+size 45752

+ 3 - 0
Scripts/ExpectedScreenshots/Texture/screenshot_warp_Texture.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c131dff46677ddeec5a42cf143e0092a6acd5bdfc65b7cb78171bf5157711f13
+size 690406

+ 3 - 0
Scripts/ExpectedScreenshots/Texture3d/screenshot_warp_Texture3d.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e63e4e24b6006c87019bfd872ac7c34a8ab302f31f6c0f672331d476c5073b8
+size 267281

+ 3 - 0
Scripts/ExpectedScreenshots/TextureMap/screenshot_warp_TextureMap.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:25238e2f240da7ca9dc9c3a749a7ab00220d739c0899319b47a90f66c5c96b0d
+size 28986

+ 27 - 27
Scripts/ShadowTest.bv.lua

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

+ 48 - 0
Scripts/_AutomatedReviewWARPTestSuite_.bv.lua

@@ -0,0 +1,48 @@
+----------------------------------------------------------------------------------------------------
+--
+-- 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.
+--
+--
+----------------------------------------------------------------------------------------------------
+
+-- Test suite "Main" tests for the automated review process using WARP software rasterizer.
+-- These tests do not require GPU nodes. They will run only on AtomSampleViewer repository.
+-- They are intended to run simply with the most basic RHI tests focused on deterministic outcomes
+-- screen compares expect to be run under WARP use either dxcpl to force WARP or command line
+-- argument: -forceAdapter="Microsoft Basic Render Driver"
+
+-- We need to lock the frame time to get deterministic timing of the screenshots for consistency between runs
+LockFrameTime(1/10)
+
+function SimpleScreenshotTest(sample, threshold)
+    local sampleSplit = {}
+    for str in string.gmatch(sample, "([^/]+)") do
+        table.insert(sampleSplit, str)
+    end
+    g_screenshotOutputFolder = ResolvePath('@user@/Scripts/Screenshots/' .. sampleSplit[#sampleSplit] .. '/')
+    Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
+    OpenSample(sample)
+    ResizeViewport(1600, 900)
+    SelectImageComparisonToleranceLevel(threshold)
+    IdleFrames(15)
+    CaptureScreenshot(g_screenshotOutputFolder .. '/screenshot_warp_' .. sampleSplit[#sampleSplit] .. '.png')
+    OpenSample(nil)
+end
+
+SimpleScreenshotTest('RHI/CopyQueue', 'Level A')
+SimpleScreenshotTest('RHI/DualSourceBlending', 'Level A')
+SimpleScreenshotTest('RHI/InputAssembly', 'Level E')
+SimpleScreenshotTest('RHI/MSAA', 'Level A')
+SimpleScreenshotTest('RHI/MultiRenderTarget', 'Level A')
+SimpleScreenshotTest('RHI/MultiThread', 'Level A')
+SimpleScreenshotTest('RHI/Stencil', 'Level A')
+SimpleScreenshotTest('RHI/Texture', 'Level A')
+SimpleScreenshotTest('RHI/Texture3d', 'Level A')
+SimpleScreenshotTest('RHI/TextureMap', 'Level D')

+ 4 - 2
Shaders/StaticMesh.azsl

@@ -133,7 +133,9 @@ PixelOutput MainPS(VertexOutput input)
             debugInfo);
             debugInfo);
     }
     }
     
     
-    output.m_diffuse = float4(diffuse.rgb, saturate(baseColor.a));
-    output.m_specular = float4(specular.rgb, saturate(baseColor.a));
+    float clampedAlpha = saturate(baseColor.a);
+    output.m_diffuse = float4(diffuse.rgb, clampedAlpha);
+    output.m_specular = float4(specular.rgb, clampedAlpha);
+
     return output;
     return output;
 }
 }

+ 70 - 0
Standalone/PythonTests/Automated/test_AtomSampleViewer_warp_suite.py

@@ -0,0 +1,70 @@
+"""
+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.
+"""
+import logging
+import os
+import subprocess
+
+import pytest
+
+import ly_test_tools.environment.process_utils as process_utils
+import ly_test_tools.launchers.platforms.base
+
+logger = logging.getLogger(__name__)
+
+
+class AtomSampleViewerException(Exception):
+    """Custom Exception class for AtomSampleViewer tests."""
+    pass
+
+
[email protected]('launcher_platform', ['windows'])
[email protected]("project", ["AtomSampleViewer"])
[email protected]('rhi', ['dx12'])
[email protected]("clean_atomsampleviewer_logs", "atomsampleviewer_log_monitor")
+class TestAutomationWarpSuite:
+
+    def test_AutomatedReviewWARPTestSuite(self, request, workspace, launcher_platform, rhi, project, atomsampleviewer_log_monitor):
+        # Script call setup.
+        test_script = '_AutomatedReviewWARPTestSuite_.bv.lua'
+        test_script_path = os.path.join(workspace.paths.engine_root(), project, 'Scripts', test_script)
+        if not os.path.exists(test_script_path):
+            raise AtomSampleViewerException(f'Test script does not exist in path: {test_script_path}')
+        cmd = os.path.join(workspace.paths.build_directory(),
+                           'AtomSampleViewerStandalone.exe '
+                           '-forceAdapter="Microsoft Basic Render Driver" '
+                           f'--project-path={workspace.paths.project()} '
+                           f'--rhi {rhi} '
+                           f'--runtestsuite scripts/{test_script} '
+                           '--exitontestend')
+
+        def teardown():
+            process_utils.kill_processes_named(['AssetProcessor', 'AtomSampleViewerStandalone'], ignore_extensions=True)
+        request.addfinalizer(teardown)
+
+        # Execute test.
+        process_utils.safe_check_call(cmd, stderr=subprocess.STDOUT, encoding='UTF-8', shell=True)
+        try:
+            unexpected_lines = ["Script: Screenshot check failed. Diff score",  # "Diff score" ensures legit failure.
+                                'Trace::Error']
+            atomsampleviewer_log_monitor.monitor_log_for_lines(
+                unexpected_lines=unexpected_lines, halt_on_unexpected=True, timeout=200)
+        except ly_test_tools.log.log_monitor.LogMonitorException as e:
+            expected_screenshots_path = os.path.join(
+                workspace.paths.engine_root(), "AtomSampleViewer", "Scripts", "ExpectedScreenshots")
+            test_screenshots_path = os.path.join(
+                workspace.paths.project(), "user", "Scripts", "Screenshots")
+            raise AtomSampleViewerException(
+                f"Got error: {e}\n"
+                f"Screenshot comparison check failed using Render Hardware Interface (RHI): '{rhi}'\n"
+                "Please review logs and screenshots at:\n"
+                f"Log file: {atomsampleviewer_log_monitor.file_to_monitor}\n"
+                f"Expected screenshots: {expected_screenshots_path}\n"
+                f"Test screenshots: {test_screenshots_path}\n")

+ 14 - 1
Standalone/PythonTests/CMakeLists.txt

@@ -20,7 +20,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
         TEST_REQUIRES gpu
         TEST_REQUIRES gpu
         TEST_SUITE main
         TEST_SUITE main
         TEST_SERIAL
         TEST_SERIAL
-        TIMEOUT 1200
+        TIMEOUT 300
         RUNTIME_DEPENDENCIES
         RUNTIME_DEPENDENCIES
             AssetProcessor
             AssetProcessor
             AssetProcessorBatch
             AssetProcessorBatch
@@ -42,4 +42,17 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.Assets
             AtomSampleViewer.Assets
     )
     )
+    ly_add_pytest(
+        NAME AtomSampleViewer::PythonWARPTests
+        PATH ${CMAKE_CURRENT_LIST_DIR}/Automated/test_AtomSampleViewer_warp_suite.py
+        TEST_SUITE main
+        TEST_SERIAL
+        TIMEOUT 300
+        RUNTIME_DEPENDENCIES
+            AssetProcessor
+            AssetProcessorBatch
+            AtomSampleViewerStandalone
+            AtomSampleViewer.GameLauncher
+            AtomSampleViewer.Assets
+    )
 endif()
 endif()