Explorar el Código

Separate lighting Evaluation / Light assignment Loop from the Material (#18739)

Signed-off-by: Karl Haubenwallner <[email protected]>
Signed-off-by: Johannes <[email protected]>
Co-authored-by: Johannes <[email protected]>
Karl Haubenwallner hace 1 semana
padre
commit
63a0c864e9
Se han modificado 100 ficheros con 2265 adiciones y 1398 borrados
  1. 1 0
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/DepthPass.azsli
  2. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/DepthPass_CustomZ.azsli
  3. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/TransparentPassVertexAndPixel.azsli
  4. 1 3
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_BaseLighting.azsli
  5. 2 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_StandardLighting.azsli
  6. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_StandardLighting_CustomZ.azsli
  7. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/LowEndForwardPassVertexAndPixel.azsli
  8. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/Transparent_StandardLighting.azsli
  9. 1 2
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPassVertexAndPixel.azsli
  10. 2 5
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_BaseLighting.azsli
  11. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_EnhancedLighting.azsli
  12. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_EnhancedLighting_CustomZ.azsli
  13. 2 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_SkinLighting.azsli
  14. 2 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_StandardLighting.azsli
  15. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_StandardLighting_CustomZ.azsli
  16. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/Transparent_EnhancedLighting.azsli
  17. 2 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/Transparent_StandardLighting.azsli
  18. 1 3
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_BaseLighting.azsli
  19. 1 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting.azsli
  20. 1 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting_CustomZ.azsli
  21. 2 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/MobileForwardPassVertexAndPixel.azsli
  22. 1 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/Transparent_StandardLighting.azsli
  23. 1 3
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_BaseLighting.azsli
  24. 1 6
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_StandardLighting.azsli
  25. 1 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_StandardLighting_CustomZ.azsli
  26. 2 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/MultiViewForwardPassVertexAndPixel.azsli
  27. 1 7
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/Transparent_StandardLighting.azsli
  28. 6 1
      Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.azsli
  29. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization_Defines.azsli
  30. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/BasePBR.azsli
  31. 1 3
      Gems/Atom/Feature/Common/Assets/Materials/Types/BasePBR_Defines.azsli
  32. 4 2
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.azsli
  33. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Defines.azsli
  34. 2 1
      Gems/Atom/Feature/Common/Assets/Materials/Types/Eye.azsli
  35. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/Eye_Defines.azsli
  36. 4 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsli
  37. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Defines.azsli
  38. 4 1
      Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.azsli
  39. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Defines.azsli
  40. 2 1
      Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.azsli
  41. 2 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Defines.azsli
  42. 233 0
      Gems/Atom/Feature/Common/Assets/Materials/materialpipeline_shader_structure.md
  43. 6 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli
  44. 2 37
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli
  45. 0 6
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli
  46. 17 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli
  47. 104 176
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/CapsuleLight.azsli
  48. 28 82
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli
  49. 113 176
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli
  50. 2 23
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/IblForward.azsli
  51. 89 2
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli
  52. 63 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightUtilTemplate.azsli
  53. 10 48
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Lights.azsli
  54. 85 193
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli
  55. 58 59
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli
  56. 91 125
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
  57. 39 55
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/SimplePointLight.azsli
  58. 50 84
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/SimpleSpotLight.azsli
  59. 5 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli
  60. 68 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassCapsuleLights.azsli
  61. 48 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDecals.azsli
  62. 67 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDirectLighting.azsli
  63. 100 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDirectionalLights.azsli
  64. 100 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDiskLights.azsli
  65. 45 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli
  66. 46 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassIbl.azsli
  67. 156 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPointLights.azsli
  68. 61 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPolygonLights.azsli
  69. 70 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassQuadLights.azsli
  70. 60 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimplePointLights.azsli
  71. 76 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimpleSpotLights.azsli
  72. 40 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/Readme.md
  73. 0 17
      Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli
  74. 3 31
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli
  75. 40 34
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli
  76. 3 0
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceData.azsli
  77. 20 12
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceEval.azsli
  78. 2 0
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceData.azsli
  79. 18 10
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceEval.azsli
  80. 13 7
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_SurfaceEval.azsli
  81. 10 9
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_LightingEval.azsli
  82. 3 1
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceData.azsli
  83. 15 9
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceEval.azsli
  84. 14 8
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_SurfaceEval.azsli
  85. 11 9
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli
  86. 2 0
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceData.azsli
  87. 21 13
      Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceEval.azsli
  88. 2 0
      Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.azsl
  89. 13 2
      Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake
  90. 1 1
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/PersistentIndexAllocator.h
  91. 0 9
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h
  92. 12 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h
  93. 8 8
      Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp
  94. 4 0
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp
  95. 2 2
      Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp
  96. 4 0
      Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/BasePBR/MaterialGraphName_Common.azsli
  97. 13 7
      Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/BasePBR/MaterialGraphName_SurfaceEval.azsli
  98. 5 1
      Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/StandardPBR/MaterialGraphName_Common.azsli
  99. 13 7
      Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/StandardPBR/MaterialGraphName_SurfaceEval.azsli
  100. 4 0
      Gems/AtomContent/TestData/Assets/TestData/Materials/BasePbrTestCases/001_DefaultWhite.material

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/DepthPass.azsli

@@ -35,6 +35,7 @@ VsOutput VertexShader(VsInput IN, uint instanceId : SV_InstanceID)
     VsSystemValues SV;
     SV.m_instanceId = instanceId;
     VsOutput OUT = EvaluateVertexGeometry(IN, SV, GetMaterialParameters());
+    OUT.m_instanceId = instanceId;
     return OUT;
 }
 

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/DepthPass_CustomZ.azsli

@@ -52,7 +52,7 @@ PsOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
     // Pixel clipping can be done here
-    EvaluateSurface(IN, geoData, GetMaterialParameters());
+    EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     PsOutput OUT;
     OUT.m_depth = IN.position.z;

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Common/TransparentPassVertexAndPixel.azsli

@@ -41,7 +41,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
-    Surface surface = EvaluateSurface(IN, geoData, GetMaterialParameters());
+    Surface surface = EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     LightingData lightingData = EvaluateLighting(surface, IN.position, views);
 

+ 1 - 3
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_BaseLighting.azsli

@@ -44,9 +44,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "LowEndForwardPassVertexAndPixel.azsli"
 

+ 2 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_StandardLighting.azsli

@@ -44,13 +44,8 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "LowEndForwardPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/ForwardPass_StandardLighting_CustomZ.azsli

@@ -44,12 +44,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "LowEndForwardPassVertexAndPixel.azsli"
 

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/LowEndForwardPassVertexAndPixel.azsli

@@ -40,7 +40,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
-    Surface surface = EvaluateSurface(IN, geoData, GetMaterialParameters());
+    Surface surface = EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     LightingData lightingData = EvaluateLighting(surface, IN.position, views);
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/LowEndPipeline/Transparent_StandardLighting.azsli

@@ -43,11 +43,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "../Common/TransparentPassVertexAndPixel.azsli"

+ 1 - 2
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPassVertexAndPixel.azsli

@@ -56,7 +56,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
-    Surface surface = EvaluateSurface(IN, geoData, GetMaterialParameters());
+    Surface surface = EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     LightingData lightingData = EvaluateLighting(surface, IN.position, views);
 
@@ -96,7 +96,6 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 #endif
 
     OUT.m_normal.a = EncodeUnorm2BitFlags(applyIblInFullscreenPasses, o_specularF0_enableMultiScatterCompensation);
-
     // --- Emissive ---
     // Sending to specular output for historical reasons until sending to diffuse can be validated
     OUT.m_specularColor.rgb += lightingData.emissiveLighting; 

+ 2 - 5
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_BaseLighting.azsli

@@ -38,11 +38,8 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_EnhancedLighting.azsli

@@ -39,12 +39,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/EnhancedPBR/EnhancedPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_EnhancedLighting_CustomZ.azsli

@@ -39,12 +39,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/EnhancedPBR/EnhancedPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_SkinLighting.azsli

@@ -46,13 +46,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/Skin/Skin_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/Skin/Skin_LightingEval.azsli"
-
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_StandardLighting.azsli

@@ -39,13 +39,8 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_StandardLighting_CustomZ.azsli

@@ -39,12 +39,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "ForwardPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/Transparent_EnhancedLighting.azsli

@@ -39,12 +39,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/EnhancedPBR/EnhancedPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "../Common/TransparentPassVertexAndPixel.azsli"
 

+ 2 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/Transparent_StandardLighting.azsli

@@ -39,11 +39,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+// Lighting eval functions from the forward-pass that use functions defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "../Common/TransparentPassVertexAndPixel.azsli"

+ 1 - 3
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_BaseLighting.azsli

@@ -61,8 +61,6 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MobileForwardPassVertexAndPixel.azsli"

+ 1 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting.azsli

@@ -61,13 +61,7 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MobileForwardPassVertexAndPixel.azsli"
 

+ 1 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting_CustomZ.azsli

@@ -60,13 +60,7 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MobileForwardPassVertexAndPixel.azsli"
 

+ 2 - 1
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/MobileForwardPassVertexAndPixel.azsli

@@ -18,6 +18,7 @@ VsOutput VertexShader(VsInput IN, uint instanceId : SV_InstanceID)
     VsSystemValues SV;
     SV.m_instanceId = instanceId;
     VsOutput OUT = EvaluateVertexGeometry(IN, SV, GetMaterialParameters());
+    OUT.m_instanceId = instanceId;
     return OUT;
 }
 
@@ -38,7 +39,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
-    Surface surface = EvaluateSurface(IN, geoData, GetMaterialParameters());
+    Surface surface = EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     LightingData lightingData = EvaluateLighting(surface, IN.position, views);
 

+ 1 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/Transparent_StandardLighting.azsli

@@ -58,12 +58,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "../Common/TransparentPassVertexAndPixel.azsli"
 

+ 1 - 3
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_BaseLighting.azsli

@@ -55,8 +55,6 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MultiViewForwardPassVertexAndPixel.azsli"

+ 1 - 6
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_StandardLighting.azsli

@@ -55,12 +55,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MultiViewForwardPassVertexAndPixel.azsli"
 

+ 1 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/ForwardPass_StandardLighting_CustomZ.azsli

@@ -56,13 +56,7 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "MultiViewForwardPassVertexAndPixel.azsli"
 

+ 2 - 1
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/MultiViewForwardPassVertexAndPixel.azsli

@@ -18,6 +18,7 @@ VsOutput VertexShader(VsInput IN, uint instanceId : SV_InstanceID)
     VsSystemValues SV;
     SV.m_instanceId = instanceId;
     VsOutput OUT = EvaluateVertexGeometry(IN, SV, GetMaterialParameters());
+    OUT.m_instanceId = instanceId;
     return OUT;
 }
 
@@ -39,7 +40,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
 
     PixelGeometryData geoData = EvaluatePixelGeometry(IN, SV, isFrontFace, GetMaterialParameters());
 
-    Surface surface = EvaluateSurface(IN, geoData, GetMaterialParameters());
+    Surface surface = EvaluateSurface(IN, SV, geoData, GetMaterialParameters());
 
     LightingData lightingData = EvaluateLighting(surface, IN.position, views);
 

+ 1 - 7
Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MultiView/Transparent_StandardLighting.azsli

@@ -53,13 +53,7 @@
 #include MATERIAL_TYPE_AZSLI_FILE_PATH
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
-
-// TODO(MaterialPipeline): I don't like how this file #includes something from BasePBR. I'd rather it include a file
-// called StandardPBR_LightingData.azsli which just #includes the BasePBR file. Otherwise this looks suspicious like
-// a mistake of some kind.
-#include "../../../Shaders/Materials/BasePBR/BasePBR_LightingData.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli"
-#include "../../../Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli"
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
 
 #include "../Common/TransparentPassVertexAndPixel.azsli"
 

+ 6 - 1
Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.azsli

@@ -67,9 +67,10 @@
         return geoData;
     }
 
-    Surface EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+    Surface EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
     {
         Surface surface;
+        surface.lightingChannels = GetLightingChannelMask(SV);
         surface.position = geoData.positionWS;
         surface.vertexNormal = geoData.vertexNormal;
         surface.normal = geoData.vertexNormal;
@@ -85,6 +86,10 @@
         return surface;
     }
 
+    // use the lighting model (lightingData, BRDF - function and ApplyLights) from the BasePBR
+
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli>
+
 #else
 
     #include <Atom/Feature/Common/Assets/Shaders/Materials/DepthPass_VertexEval.azsli>

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization_Defines.azsli

@@ -51,6 +51,8 @@
     };
 
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli>
 
 #else
 

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/BasePBR.azsli

@@ -25,6 +25,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceEval.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli>
 
 #else
 

+ 1 - 3
Gems/Atom/Feature/Common/Assets/Materials/Types/BasePBR_Defines.azsli

@@ -35,9 +35,7 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceData.azsli>
-
-    // LightingData
-    // LightingBRDF
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
 #else 
     #include <Atom/Feature/Common/Assets/Shaders/Materials/DepthPass_VertexData.azsli>
 #endif

+ 4 - 2
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.azsli

@@ -49,7 +49,9 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceEval.azsli>
-    
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_LightingBrdf.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli>
+
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
 
     COMMON_OPTIONS_PARALLAX()
@@ -84,7 +86,7 @@
     
     #include "Atom/Feature/Common/Assets/Shaders/Materials/MaterialFunctions/StandardGetAlphaAndClip.azsli"
 
-    void EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+    void EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
     {
         GetAlphaAndClip(params, geoData.uvs);
     }

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Defines.azsli

@@ -34,6 +34,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_PixelGeometryData.azsli>

+ 2 - 1
Gems/Atom/Feature/Common/Assets/Materials/Types/Eye.azsli

@@ -22,7 +22,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_SurfaceEval.azsli>
-    
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_LightingEval.azsli>
+
 #else
     #include <Atom/Feature/Common/Assets/Shaders/Materials/DepthPass_VertexEval.azsli>
 #endif

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/Eye_Defines.azsli

@@ -34,6 +34,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli>
 #else    
     #include <Atom/Feature/Common/Assets/Shaders/Materials/DepthPass_VertexData.azsli>
 #endif

+ 4 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsli

@@ -10,7 +10,9 @@
 
 #if MATERIALPIPELINE_SHADER_HAS_PIXEL_STAGE
 
+#if PIPELINE_HAS_OBJECT_SRG
     #include <Atom/Features/WrinkleMask.azsli>
+#endif
 
     COMMON_OPTIONS_BASE_COLOR()
     COMMON_OPTIONS_ROUGHNESS()
@@ -28,6 +30,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceEval.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_LightingBrdf.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_LightingEval.azsli>
 
 #else
 

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Defines.azsli

@@ -35,6 +35,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+
 #else
 
     #include <Atom/Feature/Common/Assets/Shaders/Materials/DepthPass_VertexData.azsli>

+ 4 - 1
Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.azsli

@@ -69,6 +69,9 @@ option enum class OpacityMode {Opaque, Cutout, Blended, TintedTransparent} o_opa
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_SurfaceEval.azsli>
 
+
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli>
+
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
 
     bool ShouldHandleParallax()
@@ -86,7 +89,7 @@ option enum class OpacityMode {Opaque, Cutout, Blended, TintedTransparent} o_opa
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_PixelGeometryEval.azsli>
 
-    void EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+    void EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
     {
         // do nothing, this is where alpha clip can be done if it's supported
     }

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Defines.azsli

@@ -33,6 +33,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli>
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_PixelGeometryData.azsli>

+ 2 - 1
Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.azsli

@@ -44,6 +44,7 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_PixelGeometryEval.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceEval.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli>
 
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
 
@@ -76,7 +77,7 @@
 
     #include "Atom/Feature/Common/Assets/Shaders/Materials/MaterialFunctions/StandardGetAlphaAndClip.azsli"
 
-    void EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+    void EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
     {
         GetAlphaAndClip(params, geoData.uvs);
     }

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Defines.azsli

@@ -36,6 +36,8 @@
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_PixelGeometryData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+    #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli>
 #elif  MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
     #include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_VertexData.azsli>
     #include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_PixelGeometryData.azsli>

+ 233 - 0
Gems/Atom/Feature/Common/Assets/Materials/materialpipeline_shader_structure.md

@@ -0,0 +1,233 @@
+### Glossary
+
+- **Render Pipeline**: Sequence of Render-Passes, declared in e.g. `MainRenderPipeline.azasset` and `MainPipeline.pass`, or `LowEndRenderPipeline.azasset` and `LowEndPipeline.pass`
+- **Materialtype**: A list of material properties, material functors and rasterpass shaders needed to render a mesh with that material, e.g. `AutoBrick.materialtype`, or `basepbr_generated.materialtype`  
+- **Abstract Materialtype**: A list of material properties and material-functors and several shader functions, but no actual shaders. Also specifies a lighting model needed for the material pipeline. E.g. `BasePBR.materialtype`
+- **Material**: An instance of a MaterialType with specific property values, assigned to a mesh and rendered.
+- **Material Instance**: This term is sometimes used in the Editor, but it is functionally the same as a Material, i.e. a set of property values for a material-type.
+- **Material Property**: A generic property value for a material, either directly used by the shaders (e.g. `baseColor`, `baseColorMap`, and `baseColorFactor`), as shader-option (e.g. `enableShadows` or `enableIBL`), or as rasterizer setting (e.g. `doubleSided`) 
+- **Material Shader Parameters**: Specific set of property values (e.g. a texture, or a color) used by shaders during rendering. 
+- **Material Functors**: Generic Lua scripts or C++ functions that are executed when material properties are modified, e.g. `UseTexture` sets a shader-option and a texture-map - index when a texture is selected.
+- **Material Pipeline**: A list of shader templates that are compatible with a Render Pipeline and that use the material-shadercode from the abstract materialtype. A material-pipeline generates a non-abstract materialtype and shaders, so that an abstract material can be rendered with a specific render pipeline, e.g (`MainPipeline.materialpipeline` or `LowEndPipeline.materialpipelin`).
+- **Material Pipeline Script**: A lua script that filters the shader templates of the material pipeline based on the lighting model of the abstract materialtype, e.g. `MainPipelineScript.lua` or `LowEndPipelineScript.lua`
+- **Material Pipeline Functors**: Similar to material functors, but specified in the material pipeline: Generic lua scripts that are executed when a material property is modified: e.g. `ShaderEnable.lua` enables or disables transparency - shaders based on the `isTransparent` - property of the material. 
+- **Material Canvas**: A graphical node-based editor used to create custom shader code for material types. The graph is converted into shader code for abstract material-types, which in turn use materialpipelines to create shaders for specific render pipelines.
+
+### MaterialPipeline basics
+
+At the most basic level a (non-abstract) material in O3DE provides a set of shaders that are executed by the matching raster passes for each mesh using that material. This means that the material shaders and the render pipeline have to be closely linked: In order to render a mesh in a Forward+ pipeline, the material has to provide at least a depth pre-pass shader and an opaque shader, and all shaders have to use the same render attachments as the corresponding raster passes. Furthermore, the opaque shader for the Forward+ pipeline expects a view-based tiled light assignment, and a cascaded shadowmap for the directional lights, which consequently also forces the render pipeline to provide these things.
+
+In O3DE, the render pipeline is very configurable. It is reasonably easy to set up a deferred render pipeline or a fully path-traced render pipeline. But since the material shaders are closely linked to the render pipeline, they cannot be reused across different configurations. For example, in a deferred render pipeline, the opaque pass is executed as a fullscreen shader per material type---as opposed to a raster pass, which is executed per mesh. As a different example, the default opaque shader expects a cascaded shadowmap for the directional light, but that shadowmap is only valid in the view frustum of the camera. This becomes problematic for features like ray traced reflections, where a mesh outside of the frustum still needs a valid shadow value. 
+
+While a material type could theoretically provide shaders for both, Forward+ and deferred render pipelines, doing so would require adapting shaders each time a pipeline is modified or a new one is introduced. This means that every change to the render pipeline—such as adding or removing a render target in the opaque pass of the MainPipeline—necessitates updates to every opaque shader, for every material, in every project, including user-generated materials—or existing projects would break.
+
+To solve this issue, O3DE uses material pipelines (e.g., `MainPipeline.materialpipeline`) and abstract material types to separate the material shaders from the render pipeline to some extend. The material pipeline is strictly related to the render pipeline, and defines a list of template shaders that are compatible with the render passes of the render pipeline. Each of these shaders call specific material functions at specific locations, and the abstract material has to provide these functions. This allows O3DE to generate the actual material shaders needed to render a mesh with any render pipeline.
+
+_Note:_ Material pipelines do not explicitly specifiy the material functions or the shader structure, and the formal requirements to both the shaders and the abstract materials are fairly minimal: The abstract material type specifies two `.azsli` files in the `materialShaderDefines` and `materialShaderCode` fields. These are provided to the shader template via `MATERIAL_TYPE_DEFINES_AZSLI_FILE_PATH` and `MATERIAL_TYPE_AZSLI_FILE_PATH`, respectively. For additional background information: The define file referenced in `MATERIAL_PARAMETERS_AZSLI_FILE_PATH` contains a `struct MaterialParameters`, which is generated from the material shader parameters.
+
+That being said, the abstract material types provided by the engine (namely `BasePBR`, `StandardPBR`, etc.) and the shader templates of the material pipelines (namely `MainPipeline`, `LowEndPipeline`, `MobilePipeline`, etc.) do follow a fairly strict interface, which is described below. In order to integrate custom material types into existing render pipelines, or to create a new render pipeline that uses the existing materials, it is suggested to stick to something very close to this.
+
+**Example:**
+- `MainPipeline.materialpipeline` defines the shader template `ForwardPass_BaseLighting` and the `pipelineScript` `MainPipelineScript.lua`
+- `BasePBR.materialtype` defines `lightingModel`: `Base`, `materialShaderDefines`: `BasePBR_Defines.azsli` and `materialShaderCode`: `BasePBR.azsli`.
+- This means that the `MainPipelineScript.lua` script selects the `ForwardPass_BaseLighting` shader template, and instantiates an actual shader by combining the `ForwardPass_BaseLighting.azsli` with `BasePBR_Defines.azsli` and `BasePBR.azsli`. 
+    - That instantiated shader can be found in `<project>/Cache/Intermediate Assets/materials/types/basepbr_mainpipeline_forwardpass_baselighting.azsl`, together with `basepbr_generated.materialtype`.
+
+### Material Interface Basics
+
+- `VsOutput EvaluateVertexGeometry(VsInput IN, VsSystemValues SV, const MaterialParameters params);`
+  - Generally called in the vertex shader, and is supposed to provide the vertices in normalized device coordinates.
+  - The structs `VsInput` and `VsOutput` are defined by the shader template, but can be controlled with various defines, e.g., `MATERIAL_USES_VERTEX_POSITION` or `MATERIAL_USES_VERTEX_NORMAL`.
+  - The struct `MaterialParameters` is defined by the material.
+  - The struct `VsSystemValues` is defined by the shader template, and should be treated as opaque by the material.
+- `PixelGeometryData EvaluatePixelGeometry(VsOutput IN, VsSystemValues SV, bool isFrontFace, const MaterialParameters params);`
+  - Generally called in the pixel shader, forwards values from the `VsOutput` struct, and constructs the per-pixel tangent frame. 
+  - Can be used to procedurally generate geometry, or to prepare input for a more generic `EvaluateSurface`.
+  - The struct `PixelGeometryData` is defined by the material, and opaque to the shader template.
+- `Surface EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData);`
+  - Generally called in the pixel shader. It mostly samples textures.
+  - The struct `Surface` is defined by the material. It needs to provide a few fixed members, e.g., `position`.
+- `LightingData EvaluateLighting(Surface surface, IN.position, ViewSrg::m_worldPosition.xyz);`
+  - Generally called in the pixel shader, and is supposed to calculate the final shading.
+  - The struct `LightingData` is defined by the material. It needs to provide a few fixed members, e.g., `diffuseLighting`.
+  - Defined by the shader template. It iterates over the assigned lights and evaluates the shadows.
+  - Uses several material-defined functions:
+    - `void InitializeLightingData(Surface surface, float3 viewPosition, inout LightingData lightingData);`
+    - Expects a `<LightType>Util` class for each light type with several functions that are specific to each light type, but mostly boil down to:
+      - `static <LightType>Util Init(<LightType> light, Surface surface, float3 cameraPositionWS);`
+      - `real GetFalloff();`
+        - The falloff value is used to skip a light before potentially costly shadow evaluations are performed.
+      - `void Apply(<LightType> light, Surface surface, real litRatio, inout LightingData lightingData);`
+    - `void FinalizeLightingData(Surface surface, inout LightingData lightingData);`
+
+
+### Excursion: Inheritance in HLSL
+
+Inheritance is not supported in [HLSL](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl). Sometimes, some "define magic" is used to achieve a similar effect like, e.g., the following in `BasePBR_VertexEval.azsli`:
+
+```cpp
+#ifndef EvaluateVertexGeometry
+#define EvaluateVertexGeometry EvaluateVertexGeometry_BasePBR
+#endif
+
+VsOutput EvaluateVertexGeometry_BasePBR() 
+...
+```
+
+This way, `#define EvaluateVertexGeometry EvaluateVertexGeometry_StandardPBR` can be used in `StandardPBR_VertexEval.azsli` before including the BasePBR file.
+This allows to call `EvaluateVertexGeometry_BasePBR()` from within the `EvaluateVertexGeometry_StandardPBR()` function, in addition to our own shader code. 
+In the rest of the shader code, `EvaluateVertexGeometry()` can be called in an agnostic way w.r.t. which function is actually called in these places.
+
+### ForwardPass_BaseLighting.azsli as a specific example for the Material Interface
+
+`Gems/Atom/Feature/Common/Assets/Materials/Pipelines/MainPipeline/ForwardPass_BaseLighting.azsli` with `BasePBR_Defines.azsli` and `BasePBR.azsli`.
+
+```cpp
+#pragma once
+
+#include <Atom/Features/SrgSemantics.azsli>
+
+// The following defines provide information about the current shader's capabilities to the material shader code.
+// E.g., the depth-shader (without custom z) has no pixel shader stage, so the material shader only needs to define 'EvaluatePixelGeometry()' and related structs.
+#define MATERIALPIPELINE_SHADER_HAS_PIXEL_STAGE 1
+#define OUTPUT_DEPTH 0
+#define FORCE_OPAQUE 1
+#define OUTPUT_SUBSURFACE 0
+#define ENABLE_TRANSMISSION 0
+#define ENABLE_SHADER_DEBUGGING 1
+#define ENABLE_CLEAR_COAT 0
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+#ifdef MATERIAL_TYPE_DEFINES_AZSLI_FILE_PATH
+#include MATERIAL_TYPE_DEFINES_AZSLI_FILE_PATH
+// -------------------------------------------------------------------------------------------------
+// This is declared in the file 'BasePBR.materialtype'---specifically in the field "materialShaderDefines", which has the value "BasePBR_Defines.azsli" in this example.
+// 
+// The material needs to define the following SRGs and structs in this file:
+
+// #if PIPELINE_HAS_OBJECT_SRG
+// ShaderResourceGroup ObjectSrg : SRG_PerObject {};
+// #endif /* PIPELINE_HAS_OBJECT_SRG */
+//
+// #if PIPELINE_HAS_DRAW_SRG
+// ShaderResourceGroup DrawSrg : SRG_PerDraw {};
+// #endif /* PIPELINE_HAS_DRAW_SRG */
+//
+// Usually these are declared by including DefaultDrawSrg.azsli and DefaultObjectSrg.azsli. When using a custom SRG, however, at least the same fields should 
+// be present, since they are filled by the feature processors during creation of the draw items.
+// NOTE: A deferred pipeline or a raytracing pipeline cannot have an ObjectSRG or a DrawSRG, meaning the relevant defines will be set to 0, and the information
+// has to be provided in a different way.
+// 
+// struct MaterialParameters{}; 
+// Parameter inputs for the material. This is generally generated by the material pipeline, and works by using '#include MATERIAL_PARAMETERS_AZSLI_FILE_PATH'.
+// When this struct is defined manually, SceneMaterialSrg cannot be used, since then the layout of the struct is not known in C++ and the parameters cannot be stored in a structured buffer.
+// 
+// ShaderResourceGroup MaterialSrg : SRG_PerMaterial {};
+// The definition of MaterialSrg is fairly strict, and usually it is the easiest to use '#include <Atom/Features/Materials/MaterialSrg.azsli>', which uses the MaterialParameters struct.
+// While a custom MaterialSrg can be used, the same limitations apply as with the MaterialParameters struct above: SceneMaterialSrg cannot be used.
+// 
+// The SceneMaterialSrg is required for accessing more than one set of material parameters at once, like, e.g., in a deferred pipeline.
+// 
+// These are usually not fully defined by the material, but are instead controlled by several defines: See 'BasePBR_VertexData.azsli' in conjunction with 'ForwardPassVertexData.azsli'.
+// struct VsInput{};            // Inputs to the vertex shader
+// struct VsOutput{};           // Output of the vertex shader, input to the pixel shader
+// struct VsSystemValues{};     // Values specific to the render pipeline (e.g. `instanceId` for Forward+), usually treated as an opaque struct that is passed to various utility functions, and never declared by the material directly.
+//   How everything fits together can become complex. E.g., the InstanceId needs to be passed from the vertex shader to the pixel shader and therefore, needs to be part of the VsOutput struct in a Forward+ pipeline only.
+
+// struct PixelGeometryData{}; // Converts the VsOutput to input for the surface data; 
+                               // Ideally, this does parallax handling and alpha clipping, so that the custom z pass can stop after this.
+                               // In practice, alpha clipping currently happens with the surface.
+// See 'BasePBR_PixelGeometryData.azsli' 
+
+// struct SurfaceData{};       // Contains all the surface data needed to evaluate the lighting on a pixel. 
+                               // Generally textures are sampled when creating this structure.
+// See 'BasePBR_SurfaceData.azsli' 
+
+// struct LightingData{};      // Contains input and output for the actual lighting evaluation.
+                               // The lighting evaluation functions use this during the evaluation of the separate light types.
+                               // The shader converts this to the actual output of the pixel shader (which depends on the render pipeline).
+                               // This means the struct needs to have least the members which are expected by these functions and shaders.
+// See 'BasePBR_LightingData.azsli'
+
+// -------------------------------------------------------------------------------------------------
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include <viewsrg_all.srgi>  // collects the 'partial ShaderResourceGroup ViewSrg {};'
+#include <scenesrg_all.srgi> // collects the 'partial ShaderResourceGroup SceneSrg {};'
+#include <Atom/Features/Pipeline/Forward/ForwardPassSrg.azsli>  // Defines the 'ShaderResourceGroup PassSrg : SRG_PerPass {};' as suitable for the forward pass in the main pipeline.
+#include <Atom/Features/Pipeline/Forward/ForwardPassVertexData.azsli>  // Uses the defines created by the material and actually declares the structs VsInput, VsOutput and VsSystemValues.
+#include <Atom/Features/Pipeline/Forward/ForwardPassPipelineCallbacks.azsli> // Wrapper functions to access the DrawSrg/ObjectSrg/MaterialSrg/etc.
+                                                                             // Currently, the following functions are defined:
+                                                                             // 'uint GetLightingChannelMask(VsSystemValues SV);'
+                                                                             // 'float4x4 GetObjectToWorldMatrix(VsSystemValues SV);'
+                                                                             // 'float3x3 GetObjectToWorldMatrixInverseTranspose(VsSystemValues SV);'
+                                                                             // 'float4x4 GetViewProjectionMatrix(VsSystemValues SV);'
+                                                                             // 'bool HasGoboTexture(int index);'
+                                                                             // 'Texture2D<float> GetGoboTexture(int index);'
+                                                                             // And when MATERIALPIPELINE_USES_PREV_VERTEX_POSITION is defined:
+                                                                             // 'float4x4 GetObjectToWorldMatrixPrev(VsSystemValues SV);'
+                                                                             // 'float4x4 GetViewProjectionPrevMatrix(VsSystemValues SV);'
+ 
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include MATERIAL_TYPE_AZSLI_FILE_PATH
+// -------------------------------------------------------------------------------------------------
+// This is declared in the file 'BasePBR.materialtype', specifically in the field "materialShaderCode", which has the value "BasePBR.azsli" for this example
+// 
+// The material needs to define at least the following functions:
+// 
+// - 'VsOutput EvaluateVertexGeometry(VsInput IN, VsSystemValues SV, const MaterialParameters params);'
+//     Called in the vertex shader (in the Forward+ pipeline at least), and converts VsInput into VsOutput, i.e., performs the view-projection transformation.
+//     If the material doesn't demand a custom z shader, only this function will be called for the depth pass.
+// 
+// - 'PixelGeometryData EvaluatePixelGeometry(VsOutput IN, VsSystemValues SV, bool isFrontFace, const MaterialParameters params);'
+//     This is called in the pixel shader, and converts VsOutput IN into PixelGeometryData.
+//
+// - 'Surface EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params);'
+//     Called in the pixel shader. It converts PixelGeometryData to surface data. 
+//     Most texture sampling happens in this functions, and the materials generally evaluate most of the material parameters (and shader options) here.
+//     For convenience, the material inputs should also be used here, e.g., 'BaseColorInput.azsli' provides 'GetBaseColorInput()' and 'BlendBaseColor()', which can be used together 
+//     with the shader options defined with 'COMMON_OPTIONS_BASE_COLOR', and it uses the material parameters defined in 'BaseColorPropertyGroup.json'.
+//     NOTE: The 'COMMON_SRG_INPUTS_BASE_COLOR' are obsolete; They were used to manually create the MaterialSRG before struct MaterialParameters existed.
+// 
+// - 'real3 GetDiffuseLighting(Surface surface, LightingData lightingData, real3 lightIntensity, real3 dirToLight);'
+// - 'real3 GetSpecularLighting(Surface surface, LightingData lightingData, const real3 lightIntensity, const real3 dirToLight);'
+//     Called during the (default) lighting evaluation. it allows the material to provide its own BRDF:
+// 
+// - 'void InitializeLightingData(inout Surface surface, float3 viewPosition, inout LightingData lightingData);'
+// - 'void FinalizeLightingData(Surface surface, inout LightingData lightingData);'
+//     Called before and after the lighting evaluation.
+//
+// - class CapsuleLightUtil 
+//   {
+//     static bool CheckLightChannel(CapsuleLight light, LightingData lightingData);
+//     static CapsuleLightUtil Init(CapsuleLight light, Surface surface, float3 cameraPositionWS);
+//     real GetFalloff();
+//     void Apply(CapsuleLight light, Surface surface, [real litRatio], inout LightingData lightingData);
+//     static void ApplySampled(CapsuleLight light, Surface surface, inout LightingData lightingData, const uint sampleCount); // optional
+//   };
+//     Used during the lighting evaluation for each light type (capsule, directional, disk, point, polygon, quad, simple point light, simple spot light).
+//     If the default PBR implementation shall be used in a custom shader, one has to include "Atom/Features/PBR/Lights/Lights.azsli". It provides a default
+//     <Type>LightUtil class for each supported light type.
+//
+// -------------------------------------------------------------------------------------------------
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Lighting eval functions from the forward pass that use functions and classes defined by the material
+#include <Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli>
+// - 'EvaluateLighting(Surface surface, float4 screenPosition, float3 viewPosition);'
+//     This function is specific to the Forward+ pipeline. It uses the TileLightingIterator from the PassSrg to iterate over each assigned light,
+//     evaluates the shadows (if applicable), and uses the '<Type>LightUtil' class to apply light to the surface (or skip it).
+
+// The actual vertex and pixel shader code:
+#include "ForwardPassVertexAndPixel.azsli"
+// The vertex shader calls:
+//    'VsOutput OUT = EvaluateVertexGeometry(VsInput IN, VsSystemValues SV, MaterialParameters params);'
+// The pixel shader subsequently calls:
+//    'PixelGeometryData geoData = EvaluatePixelGeometry(VsOutput IN, VsSystemValues SV, bool isFrontFace, MaterialParameters params);'
+//    'Surface surface = EvaluateSurface(VsOutput IN, PixelGeometryData geoData, MaterialParameters paramn);'
+//    'LightingData lightingData = EvaluateLighting(Surface surface, float4 IN.position, float3 ViewSrg::m_worldPosition.xyz);'
+// and then converts the 'lightingData' into whatever output the current pass requires.
+```

+ 6 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli

@@ -37,7 +37,12 @@ float3 T(float s)
     float3(0.078, 0.0, 0.0) * exp(-s*s/7.41);
 }
 
-real3 GetBackLighting(Surface surface, LightingData lightingData, real3 lightIntensity, real3 dirToLight, real transmissionDistance, real attenuationDistance)
+#ifndef GetBackLighting
+#define GetBackLighting GetBackLighting_Default
+#endif 
+
+// TODO: move this to the <MaterialType>_LightingBrdf.azsli files
+real3 GetBackLighting_Default(Surface surface, LightingData lightingData, real3 lightIntensity, real3 dirToLight, real transmissionDistance, real attenuationDistance)
 {
     real3 result = real3(0.0, 0.0, 0.0);
 

+ 2 - 37
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli

@@ -17,48 +17,13 @@
 #include <Atom/Features/BlendUtility.azsli>
 #include <Atom/Feature/Common/Assets/Shaders/Materials/MaterialInputs/BaseColorInput.azsli>
 #include <Atom/Features/Decals/DecalTextureUtil.azsli>
-#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
 #include <Atom/RPI/TangentSpace.azsli>
 
 #ifndef ENABLE_DECALS_CAP
 #define ENABLE_DECALS_CAP 20 // Default cap if it was not set by the pipeline. This is only applicable if gpu culling is disabled
 #endif
 
-
-void ApplyDecal(uint currDecalIndex, inout Surface surface);
-
 #if ENABLE_DECALS
-
-void ApplyDecalInternal(uint currDecalIndex, inout Surface surface)
-{
-    if (o_enableDecals)
-    {
-        ApplyDecal(currDecalIndex, surface);
-    }
-}
-
-void ApplyDecals(inout LightCullingTileIterator tileIterator, inout Surface surface)
-{
-#if ENABLE_LIGHT_CULLING
-    tileIterator.LoadAdvance();
-    
-    while( !tileIterator.IsDone() )
-    {
-        uint currDecalIndex = tileIterator.GetValue();
-        tileIterator.LoadAdvance();
-        
-        ApplyDecalInternal(currDecalIndex, surface);
-    }
-#else
-    // Since there's no GPU culling for decals, we rely on culling done by CPU
-    // Only apply visible decals
-    for(uint decalIndex = 0; (decalIndex < ENABLE_DECALS_CAP && decalIndex < ViewSrg::m_visibleDecalCount); decalIndex++)
-    {
-        ApplyDecalInternal(ViewSrg::m_visibleDecalIndices[decalIndex], surface);
-    }
-#endif
-}
- 
  
 real GetDecalAttenuation(real3 surfNormal, real3 decalUp, real decalAngleAttenutation)
 {
@@ -72,7 +37,7 @@ real GetDecalAttenuation(real3 surfNormal, real3 decalUp, real decalAngleAttenut
 
 void ApplyDecal(uint currDecalIndex, inout Surface surface)
 {
-    ViewSrg::Decal decal = ViewSrg::m_decals[currDecalIndex];
+    Decal decal = ViewSrg::m_decals[currDecalIndex];
 
     real3x3 decalRot = MatrixFromQuaternion(real4(decal.m_quaternion));
     decalRot = transpose(decalRot);
@@ -137,7 +102,7 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
     }
 }
 #else
-void ApplyDecals(inout LightCullingTileIterator tileIterator, inout Surface surface)
+void ApplyDecal(inout LightCullingTileIterator tileIterator, inout Surface surface)
 {
     //Not Enabled
 }

+ 0 - 6
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli

@@ -23,10 +23,6 @@
 
 class LightingData
 {
-    LightCullingTileIterator tileIterator;
-
-    uint lightingChannels;
-
     // Lighting outputs
     real3 diffuseLighting;
     real3 specularLighting[MAX_SHADING_VIEWS];
@@ -83,8 +79,6 @@ void LightingData::Init(float3 positionWS, real3 specularNormal, real roughnessL
     diffuseAmbientOcclusion = 1.0;
     specularOcclusion = 1.0;
 
-    lightingChannels = 0xFFFFFFFF;
-
     [unroll]
     for(uint i = 0; i < GET_SHADING_VIEW_COUNT; ++i)
     {

+ 17 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli

@@ -62,7 +62,6 @@ real3 GetSpecularLighting(Surface surface, LightingData lightingData, const real
     return specular;
 }
 
-
 // Then include everything else
 #include <Atom/Features/PBR/Lights/Lights.azsli>
 
@@ -104,3 +103,20 @@ PbrLightingOutput DebugOutput(real3 color)
 
     return output;
 }
+
+// These functions are kept for compatabiltiy, the functionality now resides in Atom/Features/Pipeline/Forward/*.azsli
+
+#include <Atom/Features/Pipeline/Forward/ForwardPassDecals.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassDirectLighting.azsli>
+
+void ApplyDecals(inout LightCullingTileIterator tileIterator, inout Surface surface)
+{
+    ApplyDecals_ForwardPass(tileIterator, surface);
+}
+
+void ApplyDirectLighting(Surface surface, inout LightingData lightingData, float4 screenPosition, inout LightCullingTileIterator tileIterator)
+{
+    ApplyDirectLighting_ForwardPass(surface, lightingData, screenPosition, tileIterator);
+}
+
+

+ 104 - 176
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/CapsuleLight.azsli

@@ -8,37 +8,61 @@
 
 #pragma once
 
+#if ENABLE_CAPSULE_LIGHTS
+
 #include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
-#include <Atom/Features/PBR/Lights/PointLight.azsli>
 
-#if ENABLE_CAPSULE_LIGHTS
+#ifndef CapsuleLightUtil
+#define CapsuleLightUtil CapsuleLightUtil_PBR
+#endif
 
-void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData lightingData)
+class CapsuleLightUtil_PBR
 {
-    float lightLength = light.m_length;
-    float3 startPoint = light.m_startPoint;
-    float3 startToEnd = light.m_direction * lightLength;
-    float3 endPoint = startPoint + startToEnd;
-
-    // Do simple distance check to line for falloff and tight culling.
-    float3 surfaceToStart = startPoint - surface.position;
-    float closestPointDistance = dot(-surfaceToStart, light.m_direction);
-    closestPointDistance = clamp(closestPointDistance, 0.0f, lightLength);
+    real  m_lightLength;
+    real3 m_startPoint;
+    real3 m_startToEnd;
+    real3 m_endPoint;
+    real3 m_surfaceToStart;
+    real  m_closestPointDistance;
+    real3 m_dirToClosestPoint;
+    real  m_d2;
+    real  m_falloff;
+
+    static CapsuleLightUtil_PBR Init(CapsuleLight light, Surface surface)
+    {
+        CapsuleLightUtil_PBR result;
+
+        result.m_lightLength = light.m_length;
+        result.m_startPoint = light.m_startPoint;
+        result.m_startToEnd = light.m_direction * result.m_lightLength;
+        result.m_endPoint = result.m_startPoint + result.m_startToEnd;
+        
+        // Do simple distance check to line for m_falloff and tight culling.
+        result.m_surfaceToStart = result.m_startPoint - surface.position;
+        result.m_closestPointDistance = dot(-result.m_surfaceToStart, light.m_direction);
+        result.m_closestPointDistance = clamp(result.m_closestPointDistance, 0.0f, result.m_lightLength);
+
+        result.m_dirToClosestPoint = result.m_surfaceToStart + result.m_closestPointDistance * light.m_direction;
+        result.m_d2 = dot(result.m_dirToClosestPoint, result.m_dirToClosestPoint);
+        result.m_falloff = result.m_d2 * light.m_invAttenuationRadiusSquared;
+        return result;
+    }
 
-    float3 dirToClosestPoint = surfaceToStart + closestPointDistance * light.m_direction;
-    float d2 = dot(dirToClosestPoint, dirToClosestPoint);
-    float falloff = d2 * light.m_invAttenuationRadiusSquared;
+    real GetFalloff()
+    {
+        return m_falloff;
+    }
 
-    if (falloff < 1.0)
+    void Apply(CapsuleLight light, Surface surface, real litRatio, inout LightingData lightingData)
     {
         // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        float radiusAttenuation = 1.0 - (falloff * falloff);
+        float radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
         // Check if the capsule intersects the plane formed by the surface normal. If so, the capsule should be
         // truncated to just the portion above the plane.
-        float startProjection = dot(surface.GetDiffuseNormal(), surfaceToStart);
-        float lengthProjection = dot(surface.GetDiffuseNormal(), startToEnd);
+        float startProjection = dot(surface.GetDiffuseNormal(), m_surfaceToStart);
+        float lengthProjection = dot(surface.GetDiffuseNormal(), m_startToEnd);
         float startOverLength = startProjection / lengthProjection;
         float ratioVisible = 1.0;
 
@@ -53,36 +77,37 @@ void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData l
             if (startProjection < 0.0f)
             {
                 // start point is behind the surface, so move it
-                startPoint = startPoint + lightLength * startOverLength * light.m_direction;
+                m_startPoint = m_startPoint + m_lightLength * startOverLength * light.m_direction;
                 ratioVisible = 1.0 - startOverLength;
-                lightLength *= ratioVisible;
-                surfaceToStart = startPoint - surface.position;
+                m_lightLength *= ratioVisible;
+                m_surfaceToStart = m_startPoint - surface.position;
             }
             else
             {
                 // end point is behind the surface so adjust the capsule length
-                lightLength *= startOverLength;
+                m_lightLength *= startOverLength;
                 ratioVisible = startOverLength;
-                endPoint = light.m_startPoint + light.m_direction * lightLength;
+                m_endPoint = light.m_startPoint + light.m_direction * m_lightLength;
             }
-            startToEnd = light.m_direction * lightLength;
+            m_startToEnd = light.m_direction * m_lightLength;
         }
 
         // Calculate the distances to start and end
-        float distanceToStart = length(surfaceToStart);
-        float3 surfaceToEnd = endPoint - surface.position;
+        float distanceToStart = length(m_surfaceToStart);
+        float3 surfaceToEnd = m_endPoint - surface.position;
         float distanceToEnd = length(surfaceToEnd);
 
         // Integration of lambert reflectance of a line segment to get diffuse intensity
         // See https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-        float NdotStart = dot(surface.GetDiffuseNormal(), surfaceToStart) / distanceToStart;
+        float NdotStart = dot(surface.GetDiffuseNormal(), m_surfaceToStart) / distanceToStart;
         float NdotEnd =  dot(surface.GetDiffuseNormal(), surfaceToEnd) / distanceToEnd;
-        float intensity = (NdotStart + NdotEnd) / (distanceToStart * distanceToEnd + dot(surfaceToStart, surfaceToEnd));
+        float intensity = (NdotStart + NdotEnd) / (distanceToStart * distanceToEnd + dot(m_surfaceToStart, surfaceToEnd));
         intensity *= INV_PI; // normalize for lambert reflectance.
 
         float3 lightIntensity = (intensity * radiusAttenuation * ratioVisible) * light.m_rgbIntensityCandelas;
-        lightingData.diffuseLighting += max(0.0, surface.albedo * lightIntensity);
+        lightingData.diffuseLighting += max(0.0, surface.albedo * lightIntensity) * litRatio;
 
+#if ENABLE_TRANSMISSION
         // Transmission contribution
         // We cannot compute the actual transmission distance so we want to:
         // - If transmission mode is thick object -> use transmission thickness parameter instead
@@ -92,8 +117,7 @@ void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData l
         // If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
         const float attenuationDistance = 0.0f;
 
-#if ENABLE_TRANSMISSION
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(dirToClosestPoint), transmissionDistance, attenuationDistance);
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(m_dirToClosestPoint), transmissionDistance, attenuationDistance) * litRatio;
 #endif
 
         // Calculate specular lighting for each view
@@ -105,11 +129,11 @@ void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData l
 
             // Find closest point on light to reflection vector.
             // See https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-            float reflDotStartToEnd = dot(reflectionDir, startToEnd);
-            float closestT = (dot(reflectionDir, surfaceToStart) * reflDotStartToEnd - dot(surfaceToStart, startToEnd)) / (dot(startToEnd, startToEnd) - reflDotStartToEnd * reflDotStartToEnd);
+            float reflDotm_startToEnd = dot(reflectionDir, m_startToEnd);
+            float closestT = (dot(reflectionDir, m_surfaceToStart) * reflDotm_startToEnd - dot(m_surfaceToStart, m_startToEnd)) / (dot(m_startToEnd, m_startToEnd) - reflDotm_startToEnd * reflDotm_startToEnd);
             closestT = saturate(closestT);
 
-            float3 closestIntersectionPoint = startPoint + closestT * startToEnd;
+            float3 closestIntersectionPoint = m_startPoint + closestT * m_startToEnd;
             float3 posToLight = closestIntersectionPoint - surface.position;
 
             // Calculate the offset from the nearest point on the reflection vector to the nearest point on the capsule light
@@ -118,11 +142,11 @@ void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData l
 
             // Adjust the direction to light based on the capsule radius
             posToLight -= lightReflectionOffset * saturate(light.m_radius / length(lightReflectionOffset));
-            d2 = dot(posToLight, posToLight);
-            d2 = max(light.m_radius * light.m_radius, d2);
+            m_d2 = dot(posToLight, posToLight);
+            m_d2 = max(light.m_radius * light.m_radius, m_d2);
 
             // Adjust the intensity of the light based on the capsule radius to conserve energy
-            float sphereIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_radius, d2);
+            float sphereIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_radius, m_d2);
 
             // Capsule specular is done just like spherical specular, we just move the position of the sphere along the capsule depending
             // on the point being shaded. However this means that the intensity needs to be reduced by the ratio of the capsule surface
@@ -130,160 +154,64 @@ void ApplyCapsuleLight(CapsuleLight light, Surface surface, inout LightingData l
             float sphereToCapsuleAreaRatio = 2.0 * light.m_radius / max(2.0 * light.m_radius + light.m_length, EPSILON);
 
             // Specular contribution
-            float3 viewLightIntensity = sphereToCapsuleAreaRatio * radiusAttenuation / d2;
+            float3 viewLightIntensity = sphereToCapsuleAreaRatio * radiusAttenuation / m_d2;
             viewLightIntensity *= light.m_rgbIntensityCandelas;
-            lightingData.specularLighting[viewIndex] += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, viewLightIntensity, normalize(posToLight), viewIndex);
+            lightingData.specularLighting[viewIndex] += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, viewLightIntensity, normalize(posToLight), viewIndex) * litRatio;
         }
     }
-}
 
-// Get a uniformly distributed point on the surface of a capsule from the provided uniformly distributed 2d point. Uses
-// capsRatio to determine if the point should be on the caps or cylinder to ensure an even distribution.
-void SampleCapsule(float2 randomPoint, CapsuleLight light, float capToCylinderAreaRatio, float3x3 localToWorld, out float3 outPosition, out float3 outDirection)
-{
-    float3 startToEnd = light.m_direction * light.m_length;
-    float3 endPoint = light.m_startPoint + startToEnd;
-    if (randomPoint.x < capToCylinderAreaRatio)
+    static void ApplySampled(CapsuleLight light, Surface surface, inout LightingData lightingData, const uint sampleCount = 1024)
     {
-        // Sample on cap
-        float2 spherePoint = randomPoint;
-        spherePoint.x /= capToCylinderAreaRatio; // normalize to 0.0 -> 1.0
-
-        float3 pointDirection = SampleSphere(spherePoint);
-        if (dot(pointDirection, light.m_direction) < 0.0)
+        float capArea =  4.0 * PI * light.m_radius * light.m_radius;
+        float cylinderArea = 2.0 * PI * light.m_radius * light.m_length;
+        float capToCylinderAreaRatio = capArea / (capArea + cylinderArea);
+        
+        // Construct local to world matrix for transforming sample points.
+        float3x3 localToWorld;
+        localToWorld[0] = light.m_direction;
+        if (abs(light.m_direction.z) > 0.9)
         {
-            // start point cap
-            outPosition = light.m_startPoint + pointDirection * light.m_radius;
+            // If vector is close to aligned with z axis choose y as up.
+            localToWorld[2] = normalize(cross(light.m_direction, float3(0.0, 1.0, 0.0)));
         }
         else
         {
-            // end point cap
-            outPosition = endPoint + pointDirection * light.m_radius;
+            localToWorld[2] = normalize(cross(light.m_direction, float3(0.0, 0.0, 1.0)));
         }
-        outDirection = pointDirection;
-    }
-    else
-    {
-        // Sample on cylinder
-        float angle = randomPoint.y * PI * 2.0;
-        outDirection.x = 0.0;
-        outDirection.y = sin(angle);
-        outDirection.z = cos(angle);
-        outDirection = mul(outDirection, localToWorld);
-
-        float positionAlongCylinder = (randomPoint.x - capToCylinderAreaRatio) / (1.0 - capToCylinderAreaRatio); // normalize to 0.0 -> 1.0
-        positionAlongCylinder *= light.m_length;
-
-        float3 positionInCylinder = light.m_startPoint + light.m_direction * positionAlongCylinder;
-        outPosition = positionInCylinder + outDirection * light.m_radius;
-    }
-}
-
-void ValidateCapsuleLight(CapsuleLight light, Surface surface, inout LightingData lightingData)
-{
-    const uint sampleCount = 1024;
-
-    float capArea =  4.0 * PI * light.m_radius * light.m_radius;
-    float cylinderArea = 2.0 * PI * light.m_radius * light.m_length;
-    float capToCylinderAreaRatio = capArea / (capArea + cylinderArea);
-
-    // Construct local to world matrix for transforming sample points.
-    float3x3 localToWorld;
-    localToWorld[0] = light.m_direction;
-    if (abs(light.m_direction.z) > 0.9)
-    {
-        // If vector is close to aligned with z axis choose y as up.
-        localToWorld[2] = normalize(cross(light.m_direction, float3(0.0, 1.0, 0.0)));
-    }
-    else
-    {
-        localToWorld[2] = normalize(cross(light.m_direction, float3(0.0, 0.0, 1.0)));
-    }
-    localToWorld[1]= cross(localToWorld[0], localToWorld[2]);
-
-    real3 diffuseAcc = float3(0.0, 0.0, 0.0);
-    real3 translucentAcc = float3(0.0, 0.0, 0.0);
-    real3 specularAcc[MAX_SHADING_VIEWS]; 
-
-    [unroll]
-    for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-        specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
-
-    for (uint i = 0; i < sampleCount; ++i)
-    {
-        float3 position;
-        float3 direction;
-        float2 randomPoint = GetHammersleyPoint(i, sampleCount);
-        SampleCapsule(randomPoint, light, capToCylinderAreaRatio, localToWorld, position, direction);
-        AddSampleContribution(surface, lightingData, position, direction, 0.0, diffuseAcc, specularAcc, translucentAcc);
-    }
+        localToWorld[1]= cross(localToWorld[0], localToWorld[2]);
 
-    // Lighting value is in Candela, convert to Lumen for total light output of the light
-    float3 intensityLumens = light.m_rgbIntensityCandelas * 4.0 * PI;
+        real3 diffuseAcc = float3(0.0, 0.0, 0.0);
+        real3 translucentAcc = float3(0.0, 0.0, 0.0);
+        real3 specularAcc[MAX_SHADING_VIEWS]; 
 
-    // Each of the N samples will contribute intensity / N lumens. However it will radiate in
-    // equal directions across the hemisphere, so we need to account for that
-    float3 intensity = intensityLumens * INV_PI;
-
-    lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
-
-    [unroll]
-    for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-        lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * intensity;
-
-#if ENABLE_TRANSMISSION
-    lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
-#endif
-}
+        [unroll]
+        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+            specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
 
-void ApplyCapsuleLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableCapsuleLights)
-    {
-        CapsuleLight light = ViewSrg::m_capsuleLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
+        for (uint i = 0; i < sampleCount; ++i)
         {
-            return;
+            float3 position;
+            float3 direction;
+            float2 randomPoint = GetHammersleyPoint(i, sampleCount);
+            SampleCapsule(randomPoint, light, capToCylinderAreaRatio, localToWorld, position, direction);
+            AddSampleContribution(surface, lightingData, position, direction, 0.0, diffuseAcc, specularAcc, translucentAcc);
         }
 
-#if ENABLE_AREA_LIGHT_VALIDATION
-        if (o_area_light_validation)
-        {
-            ValidateCapsuleLight(light, surface, lightingData);
-        }
-        else
-#endif // ENABLE_AREA_LIGHT_VALIDATION
-        {
-            ApplyCapsuleLight(light, surface, lightingData);
-        }
-    }
-}
-
-void ApplyCapsuleLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
-
-    while(!lightingData.tileIterator.IsDone())
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
+        // Lighting value is in Candela, convert to Lumen for total light output of the light
+        float3 intensityLumens = light.m_rgbIntensityCandelas * 4.0 * PI;
+        // Each of the N samples will contribute intensity / N lumens. However it will radiate in
+        // equal directions across the hemisphere, so we need to account for that
+        float3 intensity = intensityLumens * INV_PI;
+        
+        lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
+        [unroll]
+        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+            lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * intensity;
 
-        ApplyCapsuleLightInternal(currLightIndex, surface, lightingData);
-    }
-#else
-    for(uint lightIndex = 0; lightIndex < ViewSrg::m_capsuleLightCount; lightIndex++)
-    {
-        ApplyCapsuleLightInternal(lightIndex, surface, lightingData);
-    }
+#if ENABLE_TRANSMISSION
+        lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
 #endif
-}
-
-#else   // ENABLE_CAPSULE_LIGHTS
-
-void ApplyCapsuleLights(Surface surface, inout LightingData lightingData)
-{
-    //Not Enabled
-}
+    }
+};
 
-#endif  // ENABLE_CAPSULE_LIGHTS
+#endif // ENABLE_CAPSULE_LIGHTS

+ 28 - 82
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli

@@ -10,84 +10,46 @@
 
 #include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 
-#if ENABLE_SHADOWS
-#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
+#ifndef DirectionalLightUtil
+#define DirectionalLightUtil DirectionalLightUtil_PBR
 #endif
 
-void ApplyDirectionalLights(Surface surface, inout LightingData lightingData, float4 screenUv)
+class DirectionalLightUtil_PBR
 {
-    // Shadowed check
-    const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
-    real litRatio = 1.0;
-    real camToSurfDist = real(distance(ViewSrg::m_worldPosition, surface.position));
-
-    // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
-    // - If transmission mode is thick object -> use transmission thickness parameter instead
-    // - If transmission mode is thin object -> ignore back lighting
-    real transmissionDistance = -1.0;
+#if ENABLE_TRANSMISSION
+    real m_transmissionDistance;
+#endif
 
-#if ENABLE_SHADOWS
-    if (o_enableShadows && shadowIndex <  SceneSrg::m_directionalLightCount)
+    static DirectionalLightUtil_PBR Init(DirectionalLight light, Surface surface)
     {
+        DirectionalLightUtil_PBR result;
 #if ENABLE_TRANSMISSION
-        if (o_transmission_mode == TransmissionMode::ThickObject)
-        {
-            real2 visibilityAndThickness = DirectionalLightShadow::GetVisibilityThickTransmission(shadowIndex, real3(surface.position), surface.vertexNormal, screenUv);
-            litRatio = visibilityAndThickness.x;
-            transmissionDistance = visibilityAndThickness.y;
-        }
-        else if (o_transmission_mode == TransmissionMode::ThinObject)
-        {
-            real2 visibilityAndThickness = DirectionalLightShadow::GetVisibilityThinTransmission(
-                                                shadowIndex, real3(surface.position), surface.vertexNormal, screenUv, surface.transmission.GetShrinkFactor());
-
-            litRatio = visibilityAndThickness.x;
-            transmissionDistance = visibilityAndThickness.y;
-        }
-        else
-        {
-            litRatio = DirectionalLightShadow::GetVisibility(shadowIndex, real3(surface.position), surface.vertexNormal, screenUv);
-        }
-#else
-        litRatio = DirectionalLightShadow::GetVisibility(shadowIndex, surface.position, surface.vertexNormal, screenUv);
-#endif // ENABLE_TRANSMISSION
-
+        result.m_transmissionDistance = 0.0f;
+#endif
+        return result;
     }
-#endif // ENABLE_SHADOWS
 
-    // Add the lighting contribution for each directional light
-    for (int index = 0; index < SceneSrg::m_directionalLightCount; index++)
+#if ENABLE_TRANSMISSION
+    void SetTransmissionDistance(real distance)
     {
-        DirectionalLight light = SceneSrg::m_directionalLights[index];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
-        {
-            continue;
-        }
-
-        // [GFX TODO][ATOM-2012] care of multiple directional light
-        // Currently shadow check is done only for index == shadowIndex.
-        real currentLitRatio = 1.0;
-        real currentTransmissionDistance = -1.0;
-
-#if ENABLE_SHADOWS
-        if (o_enableShadows && index == shadowIndex)
-        {
-            // Add contribution only if current directional light is the active one for shadows
-            currentLitRatio = real(litRatio);
-            currentTransmissionDistance = transmissionDistance;
-        }
+        m_transmissionDistance = distance;
+    }
 #endif
 
+    void Apply(DirectionalLight light, Surface surface, real litRatio, inout LightingData lightingData)
+    {
+        real3 dirToLight = normalize(real3(-light.m_direction));
+
         // Transmission contribution
 #if ENABLE_TRANSMISSION
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, real3(light.m_rgbIntensityLux), normalize(real3(-light.m_direction)), real(currentTransmissionDistance), camToSurfDist);
+        // We evaluate translucent backlighting only for one camera
+        real camToSurfDist = real(distance(ViewSrg::m_worldPosition, surface.position));
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, real3(light.m_rgbIntensityLux), dirToLight, real(m_transmissionDistance), camToSurfDist);
 #endif
-
+        
         // Calculate diffuse lighting (same for all views)
-        real3 dirToLight = normalize(real3(-light.m_direction));
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, real3(light.m_rgbIntensityLux), dirToLight) * currentLitRatio;
-
-        // Calculate specular lighting for each view
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, real3(light.m_rgbIntensityLux), dirToLight) * litRatio;
+                // Calculate specular lighting for each view
         [unroll]
         for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
@@ -98,29 +60,13 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData, fl
             lightDirToReflectionDir = lightDirToReflectionDir / lightDirToReflectionDirLen; // normalize the length
             lightDirToReflectionDirLen = min(real(light.m_angularRadius), lightDirToReflectionDirLen);
             real3 viewDirToLight = dirToLight + lightDirToReflectionDir * lightDirToReflectionDirLen;
-
-            // Add specular lighting for this view
-            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, real3(light.m_rgbIntensityLux), viewDirToLight, viewIndex) * currentLitRatio;
+            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, real3(light.m_rgbIntensityLux), dirToLight, viewIndex) * litRatio;
         }
-
 #if ENABLE_SHADER_DEBUGGING
         if(IsDebuggingEnabled_PLACEHOLDER() && GetRenderDebugViewMode() == RenderDebugViewMode::CascadeShadows)
         {
-            customDebugFloats.rgb = currentLitRatio.xxx;
+            customDebugFloats.rgb = litRatio.xxx;
         }
 #endif
     }
-
-#if ENABLE_SHADOWS
-    // Add debug coloring for directional light shadow
-    if (o_enableShadows && shadowIndex <  SceneSrg::m_directionalLightCount)
-    {
-        // Apply debug coloring to all views
-        [unroll]
-        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-        {
-            lightingData.specularLighting[viewIndex] = DirectionalLightShadow::AddDebugColoring(lightingData.specularLighting[viewIndex], shadowIndex, real3(surface.position), surface.vertexNormal);
-        }
-    }
-#endif
-}
+};

+ 113 - 176
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli

@@ -8,109 +8,121 @@
 
 #pragma once
 
-#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
+#if ENABLE_DISK_LIGHTS
 
-#if ENABLE_SHADOWS
-#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+#ifndef DiskLightUtil
+#define DiskLightUtil DiskLightUtil_PBR
 #endif
 
-#if ENABLE_DISK_LIGHTS
+#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 
-void ApplyDiskLight(DiskLight light, Surface surface, inout LightingData lightingData)
+class DiskLightUtil_PBR
 {
-    float3 posToLight = light.m_position - surface.position;
-    float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
-    float falloff = distanceToLight2 * light.m_invAttenuationRadiusSquared;
+    real3 m_posToLight;
+    real3 m_posToLightDir;
+    real  m_lightDistanceSquared;
+    
+    real  m_falloff;
+    real  m_angleFalloff;
 
-    float3 posToLightDir = normalize(posToLight);
+    real3 m_dirToConeTip;
+    real  m_dotWithDirection;
 
-    // Reduce the brightness based on how much the disk is facing this pixel.
-    float angleFalloff = dot(posToLightDir, -light.m_direction);
+#if ENABLE_TRANSMISSION
+    // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+    // - If transmission mode is thick object -> use transmission thickness parameter instead
+    // - If transmission mode is thin object -> ignore back lighting
+    real m_transmissionDistance;
+#endif
 
-    // Only calculate shading if light is in range
-    if (falloff < 1.0f && angleFalloff > 0.0f)
+    static DiskLightUtil_PBR Init(DiskLight light, Surface surface)
     {
-        bool useConeAngle = light.m_flags & UseConeAngle;
-        float3 dirToConeTip;
-        float dotWithDirection;
+        DiskLightUtil_PBR result;
 
-        if (useConeAngle)
-        {
-            float3 coneTipPosition = light.m_position + light.m_bulbPositionOffset * -light.m_direction;
-            dirToConeTip = normalize(coneTipPosition - surface.position);
-            dotWithDirection = dot(dirToConeTip, -normalize(light.m_direction));
+        result.m_posToLight = light.m_position - surface.position;
+        result.m_lightDistanceSquared = dot(result.m_posToLight, result.m_posToLight); // light distance squared
+        result.m_falloff = result.m_lightDistanceSquared * light.m_invAttenuationRadiusSquared;
+        result.m_posToLightDir = normalize(result.m_posToLight);
+
+        // Reduce the brightness based on how much the disk is facing this pixel.
+        result.m_angleFalloff = dot(result.m_posToLightDir, -light.m_direction);
 
-            // If outside the outer cone angle return.
-            if (dotWithDirection < light.m_cosOuterConeAngle)
+        float3 coneTipPosition = light.m_position + light.m_bulbPositionOffset * -light.m_direction;
+        result.m_dirToConeTip = normalize(coneTipPosition - surface.position);
+        result.m_dotWithDirection = dot(result.m_dirToConeTip, -normalize(light.m_direction));
+
+        // Only calculate shading if light is in range
+        if (result.m_falloff < 1.0f && result.m_angleFalloff > 0.0f)
+        {
+            bool useConeAngle = light.m_flags & DiskLightFlag::UseConeAngle;
+            if (useConeAngle)
             {
-                return;
+                // If outside the outer cone angle: Set angle-Falloff to 0.
+                if (result.m_dotWithDirection < light.m_cosOuterConeAngle)
+                {
+                    result.m_angleFalloff = 0.0f;
+                }
             }
         }
+        return result;
+    }
+
+    real3 GetSurfaceToLightDirection()
+    {
+        return m_posToLightDir;
+    }
 
+#if ENABLE_TRANSMISSION
+    void SetTransmissionDistance(real distance)
+    {
+        m_transmissionDistance = distance;
+    }
+#endif
+
+    real GetFalloff()
+    {
+        return select(m_angleFalloff > 0.0f, m_falloff, 1.0f);
+    }
+
+    real3 GetDirectionToConeTip()
+    {
+        return m_dirToConeTip;
+    }
+
+    void Apply(DiskLight light, Surface surface, real litRatio, inout LightingData lightingData)
+    {
         // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        float radiusAttenuation = 1.0 - (falloff * falloff);
+        float radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
         // Find the distance to the closest point on the disk
-        float distanceToPlane = dot(posToLight, -light.m_direction);
+        float distanceToPlane = dot(m_posToLight, -light.m_direction);
         float distanceToPlane2 = distanceToPlane * distanceToPlane;
-        float pointOnPlaneToLightDistance = sqrt(distanceToLight2 - distanceToPlane2); // pythagorean theorem
-        float pointOnPlaneToDiskDistance = max(pointOnPlaneToLightDistance - light.m_diskRadius, 0.0f);
+        float pointOnPlaneToLightDistance = sqrt(m_lightDistanceSquared - distanceToPlane2); // pythagorean theorem 
+        float pointOnPlaneToDiskDistance = max(pointOnPlaneToLightDistance - light.m_diskRadius, 0.0f); 
         float distanceToDisk2 = pointOnPlaneToDiskDistance * pointOnPlaneToDiskDistance + distanceToPlane2;
 
         // Update the light direction based on the edges of the disk as visible from this point instead of the center.
         float3 pointOnPlane = -light.m_direction * distanceToPlane;
-        float3 pointOnPlaneToLightDir = normalize(posToLight - pointOnPlane);
+        float3 pointOnPlaneToLightDir = normalize(m_posToLight - pointOnPlane);
         float3 nearSideDir = normalize(pointOnPlane + pointOnPlaneToLightDir * (pointOnPlaneToLightDistance - light.m_diskRadius));
         float3 farSideDir = normalize(pointOnPlane + pointOnPlaneToLightDir * (pointOnPlaneToLightDistance + light.m_diskRadius));
-        posToLightDir = normalize((nearSideDir + farSideDir) * 0.5);
+        m_posToLightDir = normalize((nearSideDir + farSideDir) * 0.5);
 
-        // Standard quadratic falloff
-        distanceToDisk2 = max(0.001 * 0.001, distanceToDisk2); // clamp the light to at least 1mm away to avoid extreme values.
-        float3 lightIntensity = (light.m_rgbIntensityCandelas / distanceToDisk2) * radiusAttenuation * angleFalloff;
+        // Standard quadratic m_falloff
+        m_lightDistanceSquared = max(0.001 * 0.001, m_lightDistanceSquared); // clamp the light to at least 1mm away to avoid extreme values.
+        float3 lightIntensity = (light.m_rgbIntensityCandelas / m_lightDistanceSquared) * radiusAttenuation * m_angleFalloff;
 
         // Adjust brightness based on the disk size relative to its distance.
         // The larger the disk is relative to the surface point, the dimmer it becomes.
         // 0 radius disks are unaffected.
         lightIntensity /= ((light.m_diskRadius / distanceToPlane) + 1.0);
 
-        // shadow
-        float litRatio = 1.0;
-
-        // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
-        // - If transmission mode is thick object -> use transmission thickness parameter instead
-        // - If transmission mode is thin object -> ignore back lighting
-        float transmissionDistance = -1.0f;
-
-#if ENABLE_SHADOWS
-        if (o_enableShadows && o_enableDiskLightShadows)
-        {
-            litRatio = ProjectedShadow::GetVisibility(
-                light.m_shadowIndex,
-                light.m_position,
-                surface.position,
-                -dirToConeTip,
-                surface.vertexNormal);
-
-
-            // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
-#if ENABLE_TRANSMISSION
-            if (o_transmission_mode == TransmissionMode::ThickObject)
-            {
-                transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position);
-            }
-            else if (o_transmission_mode == TransmissionMode::ThinObject)
-            {
-                transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position - surface.transmission.GetShrinkFactor() * surface.vertexNormal);
-            }
-#endif
-        }
-#endif
-
-        if (useConeAngle && dotWithDirection < light.m_cosInnerConeAngle) // in penumbra
+        bool useConeAngle = light.m_flags & DiskLightFlag::UseConeAngle;
+        if (useConeAngle && m_dotWithDirection < light.m_cosInnerConeAngle) // in penumbra
         {
             // Normalize into 0.0 - 1.0 space.
-            float penumbraMask = (dotWithDirection - light.m_cosOuterConeAngle) / (light.m_cosInnerConeAngle - light.m_cosOuterConeAngle);
+            float penumbraMask = (m_dotWithDirection - light.m_cosOuterConeAngle) / (light.m_cosInnerConeAngle - light.m_cosOuterConeAngle);
 
             // Apply smoothstep
             penumbraMask = penumbraMask * penumbraMask * (3.0 - 2.0 * penumbraMask);
@@ -119,11 +131,11 @@ void ApplyDiskLight(DiskLight light, Surface surface, inout LightingData lightin
         }
 
         // Diffuse contribution
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, m_posToLightDir) * litRatio;
 
         // Transmission contribution
 #if ENABLE_TRANSMISSION
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, transmissionDistance, distanceToLight2);
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, m_posToLightDir, m_transmissionDistance, m_lightDistanceSquared) * litRatio;
 #endif
 
         // Calculate specular lighting for each view
@@ -148,135 +160,60 @@ void ApplyDiskLight(DiskLight light, Surface surface, inout LightingData lightin
                 // Reflection going away from the light. Choose a point far off and project it on the plane,
                 // then treat that as the reflection plane intersection.
                 float3 posToFarOffPoint = reflectionDir * distanceToPlane * 10000.0;
-                float3 lightToFarOffPoint = posToFarOffPoint - posToLight;
+                float3 lightToFarOffPoint = posToFarOffPoint - m_posToLight;
                 float3 intersectionToFarOffPoint = dot(lightToFarOffPoint, light.m_direction) * light.m_direction;
                 posToIntersection = posToFarOffPoint - intersectionToFarOffPoint;
             }
 
             // Calculate a vector from the reflection vector to the light
-            float3 intersectionToLight = posToLight - posToIntersection;
+            float3 intersectionToLight = m_posToLight - posToIntersection;
+
+            // Adjust the direction to light based on the bulb size
+            m_posToLight -= intersectionToLight * saturate(light.m_diskRadius / length(intersectionToLight));
 
             // Adjust the direction to light based on the bulb size
-            float3 viewPosToLight = posToLight - intersectionToLight * saturate(light.m_diskRadius / length(intersectionToLight));
+            float3 viewm_posToLight = m_posToLight - intersectionToLight * saturate(light.m_diskRadius / length(intersectionToLight));
 
             // Adjust the intensity of the light based on the bulb size to conserve energy
-            float diskIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_diskRadius, distanceToLight2);
+            float diskIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_diskRadius, m_lightDistanceSquared);
 
             // Specular contribution
-            lightingData.specularLighting[viewIndex] += diskIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(viewPosToLight), viewIndex) * litRatio;
+            lightingData.specularLighting[viewIndex] += diskIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(viewm_posToLight), viewIndex) * litRatio;
         }
     }
-}
 
-float3 SampleDisk(float2 randomPoint, DiskLight light)
-{
-    // Uniformly distributed point at light surface
-    float distanceFromCenter = sqrt(randomPoint.x) * light.m_diskRadius;
-    float angle = 2.0 * PI * randomPoint.y;
-    float3 outPoint = float3(distanceFromCenter * cos(angle), distanceFromCenter * sin(angle), 0.0);
-
-    // Orient point using the tangent / bitangent as a uv space.
-    float3 tangent;
-    if (abs(light.m_direction.z) > 0.9)
-    {
-        // If vector is close to aligned with z axis choose y as up.
-        tangent = normalize(cross(light.m_direction, float3(0.0, 1.0, 0.0)));
-    }
-    else
+    void ApplySampled(DiskLight light, Surface surface, inout LightingData lightingData, const uint sampleCount = 512)
     {
-        tangent = normalize(cross(light.m_direction, float3(0.0, 0.0, 1.0)));
-    }
-    float3 bitangent = cross(tangent, light.m_direction);
-    outPoint = tangent * outPoint.x + bitangent * outPoint.y;
-
-    // Translate by light position
-    outPoint += light.m_position;
-
-    return outPoint;
-}
 
-void ValidateDiskLight(DiskLight light, Surface surface, inout LightingData lightingData)
-{
-    const uint sampleCount = 512;
-
-    real3 diffuseAcc = float3(0.0, 0.0, 0.0);
-    real3 translucentAcc = float3(0.0, 0.0, 0.0);
-    real3 specularAcc[MAX_SHADING_VIEWS];
+        real3 diffuseAcc = float3(0.0, 0.0, 0.0);
+        real3 translucentAcc = float3(0.0, 0.0, 0.0);
+        real3 specularAcc[MAX_SHADING_VIEWS];
     
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-    {
-        specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
-    }
-
-    for (uint i = 0; i < sampleCount; ++i)
-    {
-        float2 randomPoint = GetHammersleyPoint(i, sampleCount);
-        float3 samplePoint = SampleDisk(randomPoint, light);
-        AddSampleContribution(surface, lightingData, samplePoint, light.m_direction, 0.0, diffuseAcc, specularAcc, translucentAcc);
-    }
-
-    lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
-    
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-    {
-        lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * light.m_rgbIntensityCandelas;
-    }
-
-#if ENABLE_TRANSMISSION
-    lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
-#endif
-
-}
-
-void ApplyDiskLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableDiskLights)
-    {
-        DiskLight light = ViewSrg::m_diskLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
-            return;
+            specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
         }
-#if ENABLE_AREA_LIGHT_VALIDATION
-        if (o_area_light_validation)
+
+        for (uint i = 0; i < sampleCount; ++i)
         {
-            ValidateDiskLight(light, surface, lightingData);
+            float2 randomPoint = GetHammersleyPoint(i, sampleCount);
+            float3 samplePoint = SampleDisk(randomPoint, light);
+            AddSampleContribution(surface, lightingData, samplePoint, light.m_direction, 0.0, diffuseAcc, specularAcc, translucentAcc);
         }
-        else
-#endif // ENABLE_AREA_LIGHT_VALIDATION
+        
+        lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
+
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
-            ApplyDiskLight(light, surface, lightingData);
+            lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * light.m_rgbIntensityCandelas;
         }
-    }
-}
 
-void ApplyDiskLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
-
-    while( !lightingData.tileIterator.IsDone() )
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
-
-        ApplyDiskLightInternal(currLightIndex, surface, lightingData);
-    }
-#else
-    for(uint lightIndex = 0; lightIndex < ViewSrg::m_diskLightCount; lightIndex++)
-    {
-        ApplyDiskLightInternal(lightIndex, surface, lightingData);
-    }
+#if ENABLE_TRANSMISSION
+        lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
 #endif
-}
-
-#else
-
-void ApplyDiskLights(Surface surface, inout LightingData lightingData)
-{
-    //Not Enabled
-}
+    }
+};
 
-#endif
+#endif // ENABLE_DISK_LIGHTS

+ 2 - 23
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/IblForward.azsli

@@ -5,30 +5,9 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-
-#pragma once
-
-// This file provides exentions to Ibl.azsli that are specific to forward render pipelines.
-
-#include "Ibl.azsli"
-
-option bool o_meshUseForwardPassIBLSpecular = false;
-option bool o_materialUseForwardPassIBLSpecular = false;
+#include <Atom/Features/Pipeline/Forward/ForwardPassIbl.azsli>
 
 void ApplyIblForward(Surface surface, inout LightingData lightingData)
 {
-#if FORCE_IBL_IN_FORWARD_PASS
-    bool useDiffuseIbl = true;
-    bool useSpecularIbl = true;
-#else
-    #if FORCE_OPAQUE
-        bool isTransparent = false;
-    #else
-        bool isTransparent = (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent);
-    #endif
-    bool useDiffuseIbl = isTransparent;
-    bool useSpecularIbl = (isTransparent || o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular);
-#endif
-
-    ApplyIBL(surface, lightingData, useDiffuseIbl, useSpecularIbl, ObjectSrg::m_reflectionProbeData, ObjectSrg::m_reflectionProbeCubeMap);
+    ApplyIbl_ForwardPass(surface, lightingData);
 }

+ 89 - 2
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli

@@ -8,10 +8,9 @@
 
 #pragma once
 
-#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
-
 #include <Atom/Features/PBR/BackLighting.azsli>
 #include <Atom/Features/PBR/Hammersley.azsli>
+#include <Atom/Features/PBR/Lights/LightStructures.azsli>
 
 //! Adjust the intensity of specular light based on the radius of the light source and roughness of the surface to approximate energy conservation.
 real GetIntensityAdjustedByRadiusAndRoughness(real roughnessA, real radius, real distance2)
@@ -73,3 +72,91 @@ bool IsSameLightChannel(uint channelA, uint channelB)
     uint bitResult = channelA & channelB;
     return bitResult;
 }
+
+float3 SampleDisk(float2 randomPoint, DiskLight light)
+{
+    // Uniformly distributed point at light surface
+    float distanceFromCenter = sqrt(randomPoint.x) * light.m_diskRadius;
+    float angle = 2.0 * PI * randomPoint.y;
+    float3 outPoint = float3(distanceFromCenter * cos(angle), distanceFromCenter * sin(angle), 0.0);
+
+    // Orient point using the tangent / bitangent as a uv space.
+    float3 tangent;
+    if (abs(light.m_direction.z) > 0.9)
+    {
+        // If vector is close to aligned with z axis choose y as up.
+        tangent = normalize(cross(light.m_direction, float3(0.0, 1.0, 0.0)));
+    }
+    else
+    {
+        tangent = normalize(cross(light.m_direction, float3(0.0, 0.0, 1.0)));
+    }
+    float3 bitangent = cross(tangent, light.m_direction);
+    outPoint = tangent * outPoint.x + bitangent * outPoint.y;
+
+    // Translate by light position
+    outPoint += light.m_position;
+
+    return outPoint;
+}
+
+// Generates uniformly distributed sample on a unit sphere from a 2d position in the 0.0 -> 1.0 range.
+real3 SampleSphere(real2 randomPoint)
+{
+    real angle = 2.0 * PI * randomPoint.y;
+    real cosTheta = 1.0 - 2.0 * randomPoint.x; // Transform x from 0.0 -> 1.0 to -1.0 -> 1.0
+    real sinTheta = sqrt(saturate(1.0 - cosTheta * cosTheta));
+    return real3(sinTheta * cos(angle), sinTheta * sin(angle), cosTheta);
+}
+
+float3 SampleRectangle(float2 randomPoint, QuadLight light)
+{
+    randomPoint = randomPoint * 2.0 - 1.0; // transform to -1 to 1 range.
+    randomPoint *= float2(light.m_halfWidth, light.m_halfHeight);
+    // Random point on light surface
+    float3 outPoint = light.m_position + light.m_leftDir * randomPoint.x + light.m_upDir * randomPoint.y;
+
+    return outPoint;
+}
+
+// Get a uniformly distributed point on the surface of a capsule from the provided uniformly distributed 2d point. Uses
+// capsRatio to determine if the point should be on the caps or cylinder to ensure an even distribution. 
+void SampleCapsule(float2 randomPoint, CapsuleLight light, float capToCylinderAreaRatio, float3x3 localToWorld, out float3 outPosition, out float3 outDirection)
+{
+    float3 startToEnd = light.m_direction * light.m_length;
+    float3 endPoint = light.m_startPoint + startToEnd;
+    if (randomPoint.x < capToCylinderAreaRatio)
+    {
+        // Sample on cap
+        float2 spherePoint = randomPoint;
+        spherePoint.x /= capToCylinderAreaRatio; // normalize to 0.0 -> 1.0
+
+        float3 pointDirection = SampleSphere(spherePoint);
+        if (dot(pointDirection, light.m_direction) < 0.0)
+        {
+            // start point cap
+            outPosition = light.m_startPoint + pointDirection * light.m_radius;
+        }
+        else
+        {
+            // end point cap
+            outPosition = endPoint + pointDirection * light.m_radius;
+        }
+        outDirection = pointDirection;
+    }
+    else
+    {
+        // Sample on cylinder
+        float angle = randomPoint.y * PI * 2.0;
+        outDirection.x = 0.0;
+        outDirection.y = sin(angle);
+        outDirection.z = cos(angle);
+        outDirection = mul(outDirection, localToWorld);
+
+        float positionAlongCylinder = (randomPoint.x - capToCylinderAreaRatio) / (1.0 - capToCylinderAreaRatio); // normalize to 0.0 -> 1.0
+        positionAlongCylinder *= light.m_length;
+        
+        float3 positionInCylinder = light.m_startPoint + light.m_direction * positionAlongCylinder;
+        outPosition = positionInCylinder + outDirection * light.m_radius;
+    }
+}

+ 63 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightUtilTemplate.azsli

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#error "This file exists as starting point for a custom lightTypeUtil class, and can't be used directly."
+
+#pragma once
+
+#ifndef _LIGHTTYPE_Util
+#define _LIGHTTYPE_Util _LIGHTTYPE_Util__NAME_
+#endif
+
+#include <Atom/Features/PBR/Lights/_LIGHTTYPE_.azsli>
+
+// "Manual" template for a custom <LightType>Util class that holds the <LightType>Util_PBR base-class, and redirects all
+// functions to that base class. This exists as a workarround to allow extension or modification of parts of the <LightType>Util_PBR base-class, 
+// since azsl doesn't really support inheritance and virtual function overrides.
+// Copy this file and replace _LIGHTTYPE_ and _NAME_ with your editor to get started quickly with your custom light-util.
+// Also, keep in mind that not every lightType needs the 'ApplySampled' function
+class _LIGHTTYPE_Util__NAME_
+{
+    _LIGHTTYPE_Util_PBR base;
+
+    static _LIGHTTYPE_Util__NAME_ Init(_LIGHTTYPE_ light, Surface surface)
+    {
+        _LIGHTTYPE_Util__NAME_ result;
+        result.base = _LIGHTTYPE_Util_PBR::Init(light, surface);
+        return result;
+    }
+
+    real3 GetSurfaceToLightDirection()
+    {
+        return base.GetSurfaceToLightDirection();
+    }
+
+#if ENABLE_TRANSMISSION
+    void SetTransmissionDistance(real distance)
+    {
+        base.SetTransmissionDistance(distance);
+    }
+#endif
+
+    real GetFalloff()
+    {
+        return base.GetFalloff();
+    }
+
+    void Apply(_LIGHTTYPE_ light, Surface surface, real litRatio, inout LightingData lightingData)
+    {
+        base.Apply(light, surface, litRatio, lightingData);
+    }
+
+    void ApplySampled(_LIGHTTYPE_ light, Surface surface, inout LightingData lightingData, const uint sampleCount = 512)
+    {
+        base.ApplySampled(light, surface, lightingData, sampleCount);
+    }
+};
+
+#endif // ENABLE_DISK_LIGHTS

+ 10 - 48
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Lights.azsli

@@ -5,10 +5,18 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-
 #pragma once
 
-#include <Atom/Features/Debug.azsli>
+/* Each light implements a Util - class similar to this:
+
+// This define construct enables something like inhertiance, since you can implement your own class before including this file, 
+// and still use functions from this class by refering to CapsuleLightUtil_PBR directly.
+// Also see LightUtilTemplate.azsli as a rough guide on the suggested structure of the <LightType>Util - class
+#ifndef CapsuleLightUtil
+#define CapsuleLightUtil CapsuleLightUtil_PBR
+#endif
+*/
+
 #include <Atom/Features/PBR/Lights/CapsuleLight.azsli>
 #include <Atom/Features/PBR/Lights/DirectionalLight.azsli>
 #include <Atom/Features/PBR/Lights/DiskLight.azsli>
@@ -17,49 +25,3 @@
 #include <Atom/Features/PBR/Lights/QuadLight.azsli>
 #include <Atom/Features/PBR/Lights/SimplePointLight.azsli>
 #include <Atom/Features/PBR/Lights/SimpleSpotLight.azsli>
-
-void ApplyDirectLighting(Surface surface, inout LightingData lightingData, float4 screenUv)
-{
-    if( IsDirectLightingEnabled() )
-    {
-        if (o_enableDirectionalLights)
-        {
-            ApplyDirectionalLights(surface, lightingData, screenUv);
-        }
-        if (o_enablePunctualLights)
-        {
-            ApplySimplePointLights(surface, lightingData);
-            ApplySimpleSpotLights(surface, lightingData);
-        }
-        if (o_enableAreaLights)
-        {
-            ApplyPointLights(surface, lightingData);
-            ApplyDiskLights(surface, lightingData);
-            ApplyCapsuleLights(surface, lightingData);
-            ApplyQuadLights(surface, lightingData);
-            ApplyPolygonLights(surface, lightingData);
-        }
-    }
-    else if(IsDebuggingEnabled_PLACEHOLDER() && GetRenderDebugViewMode() == RenderDebugViewMode::CascadeShadows)
-    {
-        if (o_enableDirectionalLights)
-        {
-            ApplyDirectionalLights(surface, lightingData, screenUv);
-        }
-    }
-    else if( UseDebugLight() )
-    {
-        real3 lightIntensity = real3(SceneSrg::m_debugLightingIntensity);
-        real3 lightDirection = real3(SceneSrg::m_debugLightingDirection);
-
-        // Diffuse lighting is view-independent
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, lightDirection);
-
-        // Calculate specular lighting for each view
-        [unroll]
-        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-        {
-            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, lightIntensity, lightDirection, viewIndex);
-        }
-    }
-}

+ 85 - 193
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli

@@ -8,128 +8,78 @@
 
 #pragma once
 
+#if ENABLE_SPHERE_LIGHTS
+
 #include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 
-#if ENABLE_SHADOWS
-#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+#ifndef PointLightUtil
+#define PointLightUtil PointLightUtil_PBR
 #endif
 
-#if ENABLE_SPHERE_LIGHTS
+class PointLightUtil_PBR
+{
+    real3 m_surfaceToLight;
+    real3 m_surfaceToLightDirection;
+    real  m_lightDistanceSquared;
+    real  m_falloff;
 
-#if ENABLE_SHADOWS
+    // calculated in Apply(), but can be useful afterwards
+    real3 m_lightIntensity;
 
-int GetPointLightShadowCubemapFace(const float3 targetPos, const float3 lightPos)
-{
-    const float3 toPoint = targetPos - lightPos;
-    const float maxElement = max(abs(toPoint.z), max(abs(toPoint.x), abs(toPoint.y)));
-    if (toPoint.x == -maxElement)
-    {
-        return 0;
-    }
-    else if (toPoint.x == maxElement)
-    {
-        return 1;
-    }
-    else if (toPoint.y == -maxElement)
+#if ENABLE_TRANSMISSION
+    // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+    // - If transmission mode is thick object -> use transmission thickness parameter instead
+    // - If transmission mode is thin object -> ignore back lighting
+    real m_transmissionDistance;
+#endif
+
+    static PointLightUtil_PBR Init(PointLight light, Surface surface)
     {
-        return 2;
+        PointLightUtil_PBR result;
+
+        result.m_surfaceToLight = real3(light.m_position) - real3(surface.position);
+        result.m_surfaceToLightDirection = normalize(result.m_surfaceToLight);
+        result.m_lightDistanceSquared = dot(result.m_surfaceToLight, result.m_surfaceToLight);
+        result.m_falloff = result.m_lightDistanceSquared * real(light.m_invAttenuationRadiusSquared);
+        return result;
     }
-    else if (toPoint.y == maxElement)
+
+
+    real3 GetSurfaceToLightDirection()
     {
-        return 3;
+        return m_surfaceToLightDirection;
     }
-    else if (toPoint.z == -maxElement)
+
+#if ENABLE_TRANSMISSION
+    void SetTransmissionDistance(real distance)
     {
-        return 4;
+        m_transmissionDistance = distance;
     }
-    else
+#endif
+
+    real GetFalloff()
     {
-        return 5;
+        return m_falloff;
     }
-}
 
-// PointLight::m_shadowIndices actually consists of uint16_t x 6 on the CPU, but visible as a uint32_t x 3 on the GPU.
-// This function returns the proper uint16_t value given an input face in the range 0-5
-int UnpackPointLightShadowIndex(const PointLight light, const int face)
-{
-    const int index = face >> 1;
-    const int shiftAmount = (face & 1) * 16;
-    return (light.m_shadowIndices[index] >> shiftAmount) & 0xFFFF;
-}
-
-uint ComputeShadowIndex(const PointLight light, const Surface surface)
-{
-    // shadow map size and bias are the same across all shadowmaps used by a specific point light, so just grab the first one
-    const uint lightIndex0 = UnpackPointLightShadowIndex(light, 0);
-    const float shadowmapSize = ViewSrg::m_projectedFilterParams[lightIndex0].m_shadowmapSize;
-
-    // Note that the normal bias offset could potentially move the shadowed position from one map to another map inside the same point light shadow.
-    const float normalBias = ViewSrg::m_projectedShadows[lightIndex0].m_normalShadowBias;
-    const float3 biasedPosition = surface.position + ComputeNormalShadowOffset(normalBias, surface.vertexNormal, shadowmapSize);
-
-    const int shadowCubemapFace = GetPointLightShadowCubemapFace(biasedPosition, light.m_position);
-    return UnpackPointLightShadowIndex(light, shadowCubemapFace);
-}
-
-#endif // ENABLE_SHADOWS
-
-void ApplyPointLight(PointLight light, Surface surface, inout LightingData lightingData)
-{
-    real3 posToLight = real3(light.m_position) - real3(surface.position);
-    real posToLightDist = length(posToLight);
-    real d2 = dot(posToLight, posToLight); // light distance squared
-    real falloff = d2 * real(light.m_invAttenuationRadiusSquared);
-
-    // Only calculate shading if light is in range
-    if (falloff < 1.0)
+    void Apply(PointLight light, Surface surface, real litRatio, inout LightingData lightingData)
     {
+
         // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        real radiusAttenuation = 1.0 - (falloff * falloff);
+        real radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
-        // Standard quadratic falloff
-        d2 = max(0.001 * 0.001, d2); // clamp the light to at least 1mm away to avoid extreme values.
-        real3 lightIntensity = (real3(light.m_rgbIntensityCandelas) / d2) * radiusAttenuation;
-
-        // shadow
-        real litRatio = 1.0;
-
-        // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
-        // - If transmission mode is thick object -> use transmission thickness parameter instead
-        // - If transmission mode is thin object -> ignore back lighting
-        real transmissionDistance = -1.0;
-
-#if ENABLE_SHADOWS
-        if (o_enableShadows && o_enableSphereLightShadows)
-        {
-            const uint shadowIndex = ComputeShadowIndex(light, surface);
-            litRatio *= real(ProjectedShadow::GetVisibility(
-                    shadowIndex,
-                    light.m_position,
-                    surface.position,
-                    posToLight,
-                    surface.vertexNormal));
-
-#if ENABLE_TRANSMISSION
-            // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
-            if (o_transmission_mode == TransmissionMode::ThickObject)
-            {
-                transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position);
-            }
-            else if (o_transmission_mode == TransmissionMode::ThinObject)
-            {
-                transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position - surface.transmission.GetShrinkFactor() * surface.vertexNormal);
-            }
-#endif // ENABLE_TRANSMISSION
-        }
-#endif // ENABLE_SHADOWS
+        // Standard quadratic m_falloff
+        m_lightDistanceSquared = max(0.001 * 0.001, m_lightDistanceSquared); // clamp the light to at least 1mm away to avoid extreme values.
+        m_lightIntensity = (real3(light.m_rgbIntensityCandelas) / m_lightDistanceSquared) * radiusAttenuation;
 
         // Diffuse contribution
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, m_lightIntensity, m_surfaceToLightDirection) * litRatio;
 
         // Transmission contribution
 #if ENABLE_TRANSMISSION
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), transmissionDistance, posToLightDist);
+        real posToLightDist = length(m_surfaceToLight);
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, m_lightIntensity, m_surfaceToLightDirection, m_transmissionDistance, posToLightDist) * litRatio;
 #endif
 
         // Calculate specular lighting for each view
@@ -140,117 +90,59 @@ void ApplyPointLight(PointLight light, Surface surface, inout LightingData light
             real3 reflectionDir = reflect(-lightingData.dirToCamera[viewIndex], surface.GetSpecularNormal());
 
             // Calculate a vector from the reflection vector to the light
-            real3 reflectionPosToLight = posToLight - dot(posToLight, reflectionDir) * reflectionDir;
+            real3 reflectionPosToLight = m_surfaceToLight - dot(m_surfaceToLight, reflectionDir) * reflectionDir;
 
             // Adjust the direction to light based on the bulb size
-            real3 viewPosToLight = posToLight - reflectionPosToLight * saturate(real(light.m_bulbRadius) / length(reflectionPosToLight));
-
+            real3 m_surfaceToLightAdjusted = m_surfaceToLight - (reflectionPosToLight * saturate(real(light.m_bulbRadius) / length(reflectionPosToLight)));
+            
             // Adjust the intensity of the light based on the bulb size to conserve energy
-            real sphereIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, real(light.m_bulbRadius), d2);
+            real sphereIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, real(light.m_bulbRadius), m_lightDistanceSquared);
 
             // Specular contribution
-            lightingData.specularLighting[viewIndex] += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(viewPosToLight), viewIndex) * litRatio;
+            lightingData.specularLighting[viewIndex] += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, m_lightIntensity, normalize(m_surfaceToLightAdjusted), viewIndex) * litRatio;
         }
     }
-}
 
-real3 SampleSphere(real2 randomPoint)
-{
-    // Generates uniformly distributed sample on a unit sphere from a 2d position in the 0.0 -> 1.0 range.
-    real angle = 2.0 * PI * randomPoint.y;
-    real cosTheta = 1.0 - 2.0 * randomPoint.x; // Transform x from 0.0 -> 1.0 to -1.0 -> 1.0
-    real sinTheta = sqrt(saturate(1.0 - cosTheta * cosTheta));
-    return real3(sinTheta * cos(angle), sinTheta * sin(angle), cosTheta);
-}
-
-void ValidatePointLight(PointLight light, Surface surface, inout LightingData lightingData)
-{
-    const uint sampleCount = 512;
-
-    real3 diffuseAcc = float3(0.0, 0.0, 0.0);
-    real3 translucentAcc = float3(0.0, 0.0, 0.0);
-    real3 specularAcc[MAX_SHADING_VIEWS];
-    
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-    {
-        specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
-    }
-
-    for (uint i = 0; i < sampleCount; ++i)
+    void ApplySampled(PointLight light, Surface surface, inout LightingData lightingData, const uint sampleCount = 512)
     {
-        real2 randomPoint = GetHammersleyPoint(i, sampleCount);
-        real3 sampleDirection = SampleSphere(randomPoint);
-        real3 samplePoint = real3(light.m_position) + sampleDirection * real(light.m_bulbRadius);
-        AddSampleContribution(surface, lightingData, samplePoint, sampleDirection, 0.0, diffuseAcc, specularAcc, translucentAcc);
-    }
-
-    // Lighting value is in Candela, convert to Lumen for total light output of the light
-    real3 intensityLumens = real3(light.m_rgbIntensityCandelas) * 4.0 * PI;
-
-    // Each of the N samples will contribute intensity / N lumens. However it will radiate in
-    // equal directions across the hemisphere, so we need to account for that
-    real3 intensity = intensityLumens * INV_PI;
-
-    lightingData.diffuseLighting += (diffuseAcc / real(sampleCount)) * intensity;
-
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-    {
-        lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / real(sampleCount)) * intensity;
-    }
-
-#if ENABLE_TRANSMISSION
-    lightingData.translucentBackLighting += (translucentAcc / real(sampleCount)) * intensity;
-#endif
-}
-
-void ApplyPointLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableSphereLights)
-    {
-        PointLight light = ViewSrg::m_pointLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
-        {
-            return;
-        }
-#if ENABLE_AREA_LIGHT_VALIDATION
-        if (o_area_light_validation)
+        real3 diffuseAcc = real3(0.0, 0.0, 0.0);
+        real3 translucentAcc = real3(0.0, 0.0, 0.0);
+        real3 specularAcc[MAX_SHADING_VIEWS];
+        
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
-            ValidatePointLight(light, surface, lightingData);
+            specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
         }
-        else
-#endif // ENABLE_AREA_LIGHT_VALIDATION
+
+        for (uint i = 0; i < sampleCount; ++i)
         {
-            ApplyPointLight(light, surface, lightingData);
+            real2 randomPoint = GetHammersleyPoint(i, sampleCount);
+            real3 sampleDirection = SampleSphere(randomPoint);
+            real3 samplePoint = real3(light.m_position) + sampleDirection * real(light.m_bulbRadius);
+            AddSampleContribution(surface, lightingData, samplePoint, sampleDirection, 0.0, diffuseAcc, specularAcc, translucentAcc);
         }
-    }
-}
 
-void ApplyPointLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
+        // Lighting value is in Candela, convert to Lumen for total light output of the light
+        real3 intensityLumens = real3(light.m_rgbIntensityCandelas) * 4.0 * PI;
+        // Each of the N samples will contribute intensity / N lumens. However it will radiate in
+        // equal directions across the hemisphere, so we need to account for that
+        real3 intensity = intensityLumens * INV_PI;
 
-    while( !lightingData.tileIterator.IsDone() )
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
+        lightingData.diffuseLighting += (diffuseAcc / real(sampleCount)) * intensity;
 
-        ApplyPointLightInternal(currLightIndex, surface, lightingData);
-    }
-#else
-    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
-    {
-        ApplyPointLightInternal(lightIndex, surface, lightingData);
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+        {
+            lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / real(sampleCount)) * intensity;
+        }
+
+    #if ENABLE_TRANSMISSION
+        lightingData.translucentBackLighting += (translucentAcc / real(sampleCount)) * intensity;
+    #endif
     }
-#endif
-}
 
-#else
+};
+
+#endif // ENABLE_SPHERE_LIGHTS
 
-void ApplyPointLights(Surface surface, inout LightingData lightingData)
-{
-    //Not Enabled
-}
-#endif

+ 58 - 59
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli

@@ -8,85 +8,84 @@
 
 #pragma once
 
+
+#if ENABLE_POLYGON_LTC_LIGHTS
+
 #include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 #include <Atom/Features/PBR/Lights/Ltc.azsli>
 #include <Atom/RPI/Math.azsli>
 
-#if ENABLE_POLYGON_LTC_LIGHTS
-// Polygon lights using Linearly Transformed Cosines
-void ApplyPoylgonLight(PolygonLight light, Surface surface, inout LightingData lightingData)
-{
-    float3 posToLight = light.m_position - surface.position;
-    float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
-    float falloff = distanceToLight2 * abs(light.m_invAttenuationRadiusSquared);
 
-    if (falloff > 1.0f)
-    {
-        return; // light out of range
-    }
+#ifndef PolygonLightUtil
+#define PolygonLightUtil PolygonLightUtil_PBR
+#endif
+
+
+class PolygonLightUtil_PBR
+{
 
-    bool doubleSided = light.m_invAttenuationRadiusSquared < 0.0;
-    uint startIndex = ViewSrg::PolygonLightGetStartIndex(light.m_startEndIndex);
-    uint endIndex = ViewSrg::PolygonLightGetEndIndex(light.m_startEndIndex);
+    real3 m_posToLight;
+    real m_distanceToLight2;
+    bool m_doubleSided;
+    real m_falloff;
 
-    if (!doubleSided)
+    static PolygonLightUtil_PBR Init(PolygonLight light, Surface surface)
     {
-        if (dot(posToLight, -light.m_direction) <= 0.0)
+        PolygonLightUtil_PBR result;
+        result.m_posToLight = light.m_position - surface.position;
+        result.m_distanceToLight2 = dot(result.m_posToLight, result.m_posToLight); // light distance squared
+        result.m_falloff = result.m_distanceToLight2 * abs(light.m_invAttenuationRadiusSquared);
+    
+        result.m_doubleSided = light.m_invAttenuationRadiusSquared < 0.0;
+        if (!result.m_doubleSided)
         {
-            return; // Light isn't facing the surface
+            if (dot(result.m_posToLight, -light.m_direction) <= 0.0)
+            {
+                result.m_falloff = 1.0f; // Light isn't facing the surface
+            }
         }
+        return result;
     }
 
-    // Above test passed, so evaluate the polygon light.
-
-    // Smoothly adjusts the light intensity so it reaches 0 at the attenuation radius
-    float radiusAttenuation = 1.0 - (falloff * falloff);
-    radiusAttenuation = radiusAttenuation * radiusAttenuation;
-
-    // Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
-    float3 intensity = 0.5 * INV_PI * radiusAttenuation * abs(light.m_rgbIntensityNits);
-
-    // Calculate lighting for each view
-    [unroll]
-    for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+    real GetFalloff()
     {
-        float diffuse = 0.0;
-        float3 specularRgb = 0.0;
+        return m_falloff;
+    }
 
-        LtcPolygonEvaluate(surface, lightingData, SceneSrg::m_ltcMatrix, SceneSrg::m_ltcAmplification,
-                           ViewSrg::m_polygonLightPoints, startIndex, endIndex, viewIndex, diffuse, specularRgb);
+    void Apply(PolygonLight light, Surface surface, real litRatio, inout LightingData lightingData)
+    {
+        // Smoothly adjusts the light intensity so it reaches 0 at the attenuation radius
+        
+        uint startIndex = ViewSrg::PolygonLightGetStartIndex(light.m_startEndIndex);
+        uint endIndex = ViewSrg::PolygonLightGetEndIndex(light.m_startEndIndex);
+        
+        float radiusAttenuation = 1.0 - (m_falloff * m_falloff);
+        radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
-        diffuse = doubleSided ? abs(diffuse) : max(0.0, diffuse);
-        specularRgb = doubleSided ? abs(specularRgb) : max(0.0, specularRgb);
+        // Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
+        float3 intensity = 0.5 * INV_PI * radiusAttenuation * abs(light.m_rgbIntensityNits);
 
-        // Only add diffuse lighting once (for the first view)
-        if (viewIndex == 0)
+        // Calculate lighting for each view
+        [unroll]
+        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
-            lightingData.diffuseLighting += surface.albedo * diffuse * intensity;
-        }
 
-        lightingData.specularLighting[viewIndex] += specularRgb * intensity;
-    }
-}
+            float diffuse = 0.0;
+            float3 specularRgb = 0.0;
 
-void ApplyPolygonLights(Surface surface, inout LightingData lightingData)
-{
-    if (o_enablePolygonLights)
-    {
-        for (uint currLightIndex = 0; currLightIndex <  ViewSrg::m_polygonLightCount; ++currLightIndex)
-        {
-            PolygonLight light = ViewSrg::m_polygonLights[currLightIndex];
-            if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
+            LtcPolygonEvaluate(surface, lightingData, SceneSrg::m_ltcMatrix, SceneSrg::m_ltcAmplification, ViewSrg::m_polygonLightPoints, startIndex, endIndex, viewIndex, diffuse, specularRgb);
+
+            diffuse = m_doubleSided ? abs(diffuse) : max(0.0, diffuse);
+            specularRgb = m_doubleSided ? abs(specularRgb) : max(0.0, specularRgb);
+
+            // Only add diffuse lighting once (for the first view)
+            if (viewIndex == 0)
             {
-                continue;
+                lightingData.diffuseLighting += surface.albedo * diffuse * intensity * litRatio;
             }
-            ApplyPoylgonLight(light, surface, lightingData);
+            lightingData.specularLighting[viewIndex] += specularRgb * intensity * litRatio;
         }
     }
-}
-#else
-void ApplyPolygonLights(Surface surface, inout LightingData lightingData)
-{
-    return;
-}
-#endif
+};
+
+#endif // ENABLE_POLYGON_LTC_LIGHTS

+ 91 - 125
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli

@@ -8,11 +8,11 @@
 
 #pragma once
 
-#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
-#include <Atom/Features/PBR/Lights/Ltc.azsli>
-
 #if ENABLE_QUAD_LIGHTS
 
+#include <Atom/Features/PBR/Lights/Ltc.azsli>
+#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
+
 // Returns the solid angle at origin of a rectangle defined by points p0-p3. Does not handle horizon.
 // Modified from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf Listing 11 on page 49.
 float RectangleSolidAngle(float3 v0, float3 v1, float3 v2, float3 v3)
@@ -32,12 +32,12 @@ float RectangleSolidAngle(float3 v0, float3 v1, float3 v2, float3 v3)
 
 // Unlike standard ray-plane intersection, this version is made for light planes to avoid discontinuities when the
 // reflection vector is going away from a light plane.
-float3 RayLightPlaneIntersection(in float3 pos, in float3 rayDirection, in float3 lightOrigin, in float3 lightDirection)
+float3 RayLightPlaneIntersection(in float3 pos, in float3 rayDirection, in float3 lightOrigin, in float3 m_lightDirection)
 {
     float3 lightToPos = pos - lightOrigin;
-    float reflectionDotLight = dot(rayDirection, -lightDirection);
+    float reflectionDotLight = dot(rayDirection, -m_lightDirection);
 
-    float distanceToPlane = dot(lightToPos, lightDirection);
+    float distanceToPlane = dot(lightToPos, m_lightDirection);
     if (reflectionDotLight >= 0.0001)
     {
         // Reflection going towards the light
@@ -50,7 +50,7 @@ float3 RayLightPlaneIntersection(in float3 pos, in float3 rayDirection, in float
         float3 posToFarOffPoint = rayDirection * distanceToPlane * 10000.0;
         float3 lightToFarOffPoint = lightToPos + posToFarOffPoint;
         // Here "intersection" refers to the projection of the far off point onto the light plane.
-        float3 intersectionToFarOffPoint = dot(lightToFarOffPoint, lightDirection) * lightDirection;
+        float3 intersectionToFarOffPoint = dot(lightToFarOffPoint, m_lightDirection) * m_lightDirection;
         return pos + posToFarOffPoint - intersectionToFarOffPoint;
     }
 }
@@ -69,38 +69,65 @@ float3 GetSpecularDominantDirection(float3 normal, float3 reflection, float roug
     return normalize(lerp(normal, reflection, lerpFactor));
 }
 
-// Quad light approximation. Diffuse portion based on https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf Pages 49-50.
-void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightingData)
+
+#ifndef QuadLightUtil
+#define QuadLightUtil QuadLightUtil_PBR
+#endif
+
+class QuadLightUtil_PBR
 {
-    float3 lightDirection = cross(light.m_leftDir, light.m_upDir); // left and up are already normalized.
 
-    float3 posToLight = light.m_position - surface.position;
-    float posToLightDotLightDirection = dot(posToLight, -lightDirection);
+    real3 m_lightDirection;
+    real3 m_posToLight;
+    real m_posToLightDotLightDirection;
+    real m_distanceToLight2;
+    real m_falloff;
+    bool m_doubleSided;
+
+    static QuadLightUtil_PBR Init(QuadLight light, Surface surface)
+    {
+        QuadLightUtil_PBR result;
+
+        result.m_lightDirection = cross(light.m_leftDir, light.m_upDir); // left and up are already normalized.
 
-    float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
-    float falloff = distanceToLight2 * light.m_invAttenuationRadiusSquared;
+        result.m_posToLight = light.m_position - surface.position;
+        result.m_posToLightDotLightDirection = dot(result.m_posToLight, -result.m_lightDirection);
+
+        result.m_distanceToLight2 = dot(result.m_posToLight, result.m_posToLight); // light distance squared
+        result.m_falloff = result.m_distanceToLight2 * light.m_invAttenuationRadiusSquared;
+
+        result.m_doubleSided = (light.m_flags & EmitsBothDirections) > 0;
+        if (result.m_doubleSided)
+        {
+            result.m_lightDirection *= sign(result.m_posToLightDotLightDirection);
+            result.m_posToLightDotLightDirection = abs(result.m_posToLightDotLightDirection);
+        }
+        if (result.m_posToLightDotLightDirection <= 0.0f)
+        {
+            result.m_falloff = 1.0f;
+        }
+        return result;
+    }
 
-    bool doubleSided = (light.m_flags & EmitsBothDirections) > 0;
-    if (doubleSided)
+    real GetFalloff()
     {
-        lightDirection *= sign(posToLightDotLightDirection);
-        posToLightDotLightDirection = abs(posToLightDotLightDirection);
+        return m_falloff;
     }
 
-    if(falloff < 1.0f && posToLightDotLightDirection > 0.0)
+    void Apply(QuadLight light, Surface surface, real litRatio, inout LightingData lightingData)
     {
-        // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        float radiusAttenuation = 1.0 - (falloff * falloff);
+                // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
+        float radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
         float3 left = light.m_leftDir * light.m_halfWidth;
         float3 up = light.m_upDir * light.m_halfHeight;
 
         // Calculation positions for 4 corners relative to the surface
-        float3 p0 = posToLight + -left +  up;
-        float3 p1 = posToLight + -left + -up;
-        float3 p2 = posToLight +  left + -up;
-        float3 p3 = posToLight +  left +  up;
+        float3 p0 = m_posToLight + -left +  up;
+        float3 p1 = m_posToLight + -left + -up;
+        float3 p2 = m_posToLight +  left + -up;
+        float3 p3 = m_posToLight +  left +  up;
 
         bool useFastApproximation = (light.m_flags & UseFastApproximation) > 0;
         if (!useFastApproximation && o_enableQuadLightLTC)
@@ -117,7 +144,7 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 float viewDiffuse = 0.0;
                 float3 viewSpecular = float3(0.0, 0.0, 0.0);
 
-                LtcQuadEvaluate(surface, lightingData, SceneSrg::m_ltcMatrix, SceneSrg::m_ltcAmplification, p, doubleSided, viewIndex, viewDiffuse, viewSpecular);
+                LtcQuadEvaluate(surface, lightingData, SceneSrg::m_ltcMatrix, SceneSrg::m_ltcAmplification, p, m_doubleSided, viewIndex, viewDiffuse, viewSpecular);
 
                 // Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
                 float3 intensity = 0.5 * INV_PI * radiusAttenuation * light.m_rgbIntensityNits;
@@ -125,10 +152,10 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 // Only add diffuse lighting once (for the first view)
                 if (viewIndex == 0)
                 {
-                    lightingData.diffuseLighting += surface.albedo * viewDiffuse * intensity;
+                    lightingData.diffuseLighting += surface.albedo * viewDiffuse * intensity * litRatio;
                 }
 
-                lightingData.specularLighting[viewIndex] += viewSpecular * intensity;
+                lightingData.specularLighting[viewIndex] += viewSpecular * intensity * litRatio;
             }
         }
         else if (useFastApproximation && o_enableQuadLightApprox)
@@ -142,7 +169,7 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
             p2 = normalize(p2);
             p3 = normalize(p3);
 
-            float3 dirToLightCenter = normalize(posToLight);
+            float3 dirToLightCenter = normalize(m_posToLight);
 
             // Intensity is the solid angle times the brightness of the light surface.
             // Each position contributes 1/5 of the light (4 corners + center)
@@ -155,8 +182,9 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 GetDiffuseLighting(surface, lightingData, intensity, p2) +
                 GetDiffuseLighting(surface, lightingData, intensity, p3) +
                 GetDiffuseLighting(surface, lightingData, intensity, dirToLightCenter)
-            );
-
+            ) * litRatio;
+            
+#if ENABLE_TRANSMISSION
             // Transmission contribution
             // We cannot compute the actual transmission distance so we want to:
             // - If transmission mode is thick object -> use transmission thickness parameter instead
@@ -166,7 +194,6 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
             // If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
             const float attenuationDistance = 0.0f;
 
-#if ENABLE_TRANSMISSION
             lightingData.translucentBackLighting +=
             (
                 GetBackLighting(surface, lightingData, intensity, p0, transmissionDistance, attenuationDistance) +
@@ -174,7 +201,7 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 GetBackLighting(surface, lightingData, intensity, p2, transmissionDistance, attenuationDistance) +
                 GetBackLighting(surface, lightingData, intensity, p3, transmissionDistance, attenuationDistance) +
                 GetBackLighting(surface, lightingData, intensity, dirToLightCenter, transmissionDistance, attenuationDistance)
-            );
+            ) * litRatio;
 #endif
 
             // Calculate specular lighting for each view
@@ -186,7 +213,7 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
 
                 // First find the reflection-plane intersection, then find the closest point on the rectangle to that intersection.
                 float2 halfSize = float2(light.m_halfWidth, light.m_halfHeight);
-                float3 positionOnPlane = RayLightPlaneIntersection(surface.position, reflectionDir, light.m_position, lightDirection);
+                float3 positionOnPlane = RayLightPlaneIntersection(surface.position, reflectionDir, light.m_position, m_lightDirection);
                 float3 lightPositionWorld = ClosestPointRect(positionOnPlane, light.m_position, light.m_leftDir, light.m_upDir, halfSize);
                 float3 lightPositionLocal = lightPositionWorld - surface.position;
 
@@ -194,7 +221,7 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 // is at a grazing angle. This can cause the "closest" point to tend towards the corners of the rectangle. To correct this,
                 // we find the closest point on the reflection line to this first attempt, and use it for a second attempt.
                 float3 localPositionOnLine = reflectionDir * dot(reflectionDir, lightPositionLocal);
-                lightPositionLocal = ClosestPointRect(localPositionOnLine, posToLight, light.m_leftDir, light.m_upDir, halfSize);
+                lightPositionLocal = ClosestPointRect(localPositionOnLine, m_posToLight, light.m_leftDir, light.m_upDir, halfSize);
 
                 float3 dirToLight = normalize(lightPositionLocal);
                 float lightPositionDist2 = dot(lightPositionLocal, lightPositionLocal);
@@ -203,110 +230,49 @@ void ApplyQuadLight(QuadLight light, Surface surface, inout LightingData lightin
                 float quadIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, 0.0, lightPositionDist2);
 
                 // Specular contribution
-                lightingData.specularLighting[viewIndex] += quadIntensityNormalization * GetSpecularLighting(surface, lightingData, intensity, dirToLight, viewIndex);
+                lightingData.specularLighting[viewIndex] += quadIntensityNormalization * GetSpecularLighting(surface, lightingData, intensity, dirToLight, viewIndex) * litRatio;
             }
         }
     }
-}
-
-float3 SampleRectangle(float2 randomPoint, QuadLight light)
-{
-    randomPoint = randomPoint * 2.0 - 1.0; // transform to -1 to 1 range.
-    randomPoint *= float2(light.m_halfWidth, light.m_halfHeight);
-    // Random point on light surface
-    float3 outPoint = light.m_position + light.m_leftDir * randomPoint.x + light.m_upDir * randomPoint.y;
-
-    return outPoint;
-}
-
-void ValidateQuadLight(QuadLight light, Surface surface, inout LightingData lightingData)
-{
-    float3 lightDirection = cross(light.m_leftDir, light.m_upDir); // left and up are already normalized.
-
-    const uint sampleCount = 512;
 
-    real3 diffuseAcc = float3(0.0, 0.0, 0.0);
-    real3 translucentAcc = float3(0.0, 0.0, 0.0);
-    real3 specularAcc[MAX_SHADING_VIEWS];
-    
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+    void ApplySampled(QuadLight light, Surface surface, inout LightingData lightingData, const uint sampleCount = 512)
     {
-        specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
-    }
-
-    bool emitsBothDirections = (light.m_flags & EmitsBothDirections) > 0;
-    float bothDirectionsFactor = emitsBothDirections ? -1.0 : 0.0;
+        real3 diffuseAcc = float3(0.0, 0.0, 0.0);
+        real3 translucentAcc = float3(0.0, 0.0, 0.0);
+        real3 specularAcc[MAX_SHADING_VIEWS];
 
-    for (uint i = 0; i < sampleCount; ++i)
-    {
-        float2 randomPoint = GetHammersleyPoint(i, sampleCount);
-        float3 samplePoint = SampleRectangle(randomPoint, light);
-        AddSampleContribution(surface, lightingData, samplePoint, lightDirection, bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
-    }
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+        {
+            specularAcc[viewIndex] = float3(0.0, 0.0, 0.0);
+        }
 
-    float area = light.m_halfWidth * light.m_halfHeight * 4.0;
-    float3 intensityCandelas = light.m_rgbIntensityNits * area;
+        float bothDirectionsFactor = m_doubleSided ? -1.0 : 0.0;
 
-    lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensityCandelas;
+        for (uint i = 0; i < sampleCount; ++i)
+        {
+            float2 randomPoint = GetHammersleyPoint(i, sampleCount);
+            float3 samplePoint = SampleRectangle(randomPoint, light);
+            AddSampleContribution(surface, lightingData, samplePoint, m_lightDirection, bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
+        }
 
-    [unroll]
-    for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
-    {
-        lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * intensityCandelas;
-    }
+        float area = light.m_halfWidth * light.m_halfHeight * 4.0;
+        float3 intensityCandelas = light.m_rgbIntensityNits * area;
 
-#if ENABLE_TRANSMISSION
-    lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensityCandelas;
-#endif
-}
+        lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensityCandelas;
 
-void ApplyQuadLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableQuadLightApprox || o_enableQuadLightLTC)
-    {
-        QuadLight light = ViewSrg::m_quadLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
-        {
-            return;
-        }
-#if ENABLE_AREA_LIGHT_VALIDATION
-        if (o_area_light_validation)
+        [unroll]
+        for(uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
-            ValidateQuadLight(light, surface, lightingData);
+            lightingData.specularLighting[viewIndex] += (specularAcc[viewIndex] / float(sampleCount)) * intensityCandelas;
         }
-        else
-#endif //ENABLE_AREA_LIGHT_VALIDATION
-        {
-            ApplyQuadLight(light, surface, lightingData);
-        }
-    }
-}
-
-void ApplyQuadLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
-
-    while( !lightingData.tileIterator.IsDone() )
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
 
-        ApplyQuadLightInternal(currLightIndex, surface, lightingData);
+    #if ENABLE_TRANSMISSION
+        lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensityCandelas;
+    #endif
     }
-#else
-    for(uint lightIndex = 0; lightIndex < ViewSrg::m_quadLightCount; lightIndex++)
-    {
-        ApplyQuadLightInternal(lightIndex, surface, lightingData);
-    }
-#endif
-}
 
-#else
+};
+
+#endif // ENABLE_QUAD_LIGHTS
 
-void ApplyQuadLights(Surface surface, inout LightingData lightingData)
-{
-    //Not Enabled
-}
-#endif

+ 39 - 55
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/SimplePointLight.azsli

@@ -8,76 +8,60 @@
 
 #pragma once
 
-#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 
-void ApplySimplePointLight(SimplePointLight light, Surface surface, inout LightingData lightingData)
+#ifndef SimplePointLightUtil
+#define SimplePointLightUtil SimplePointLightUtil_PBR
+#endif
+
+
+class SimplePointLightUtil_PBR
 {
-    real3 posToLight = real3(light.m_position - surface.position);
-    real d2 = dot(posToLight, posToLight); // light distance squared
-    real falloff = d2 * real(light.m_invAttenuationRadiusSquared);
+    real3 m_surfaceToLightDirection;
+    real  m_lightDistanceSquared;
+    real  m_falloff;
+
+    static SimplePointLightUtil_PBR Init(SimplePointLight light, Surface surface)
+    {
+        SimplePointLightUtil_PBR result;
+
+        real3 posToLight = real3(light.m_position - surface.position);
+        result.m_lightDistanceSquared = dot(posToLight, posToLight); // light distance squared
+        result.m_falloff = result.m_lightDistanceSquared * real(light.m_invAttenuationRadiusSquared);
+        result.m_surfaceToLightDirection = normalize(posToLight);
+        return result;
+    }
 
-    // Only calculate shading if light is in range
-    if (falloff < 1.0f)
+    real3 GetSurfaceToLightDirection()
+    {
+        return m_surfaceToLightDirection;
+    }
+
+    real GetFalloff()
+    {
+        return m_falloff;
+    }
+
+    void Apply(SimplePointLight light, Surface surface, real litRatio, inout LightingData lightingData)
     {
         // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        real radiusAttenuation = 1.0 - (falloff * falloff);
+        real radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
-        // Standard quadratic falloff
-        d2 = max(0.001 * 0.001, d2); // clamp the light to at least 1mm away to avoid extreme values.
-        real3 lightIntensity = (real3(light.m_rgbIntensityCandelas) / d2) * radiusAttenuation;
-        real3 posToLightDir = normalize(posToLight);
+        // Standard quadratic m_falloff
+        m_lightDistanceSquared = max(0.001 * 0.001, m_lightDistanceSquared); // clamp the light to at least 1mm away to avoid extreme values.
+        real3 lightIntensity = (real3(light.m_rgbIntensityCandelas) / m_lightDistanceSquared) * radiusAttenuation;
 
         // Diffuse contribution
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir);
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, m_surfaceToLightDirection) * litRatio;
 
         // Calculate specular lighting for each view
         [unroll]
         for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
             // Specular contribution
-            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, lightIntensity, posToLightDir, viewIndex);
-        }
-    }
-}
-
-void ApplySimplePointLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableSimplePointLights)
-    {
-        SimplePointLight light = ViewSrg::m_simplePointLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
-        {
-            return;
+            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, lightIntensity, m_surfaceToLightDirection, viewIndex) * litRatio;
         }
-        ApplySimplePointLight(light, surface, lightingData);
-    }
-}
-
-void ApplySimplePointLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
-    while( !lightingData.tileIterator.IsDone() )
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
-
-        ApplySimplePointLightInternal(currLightIndex, surface, lightingData);
     }
-#else
-
-    // For perf we cap light count. If it was not set by the pipeline it will use the value specified below
-    // This is only applicable if ENABLE_LIGHT_CULLING is disabled (i.e no gpu culling)
-    #ifndef ENABLE_SIMPLE_POINTLIGHTS_CAP
-        #define ENABLE_SIMPLE_POINTLIGHTS_CAP 20
-    #endif
+};
 
-    // Since there's no GPU culling for simple point lights, we rely on culling done by CPU
-    // Only apply visible point lights
-    for(uint lightIndex = 0; (lightIndex < ENABLE_SIMPLE_POINTLIGHTS_CAP && lightIndex < ViewSrg::m_visibleSimplePointLightCount); lightIndex++)
-    {
-        ApplySimplePointLightInternal(ViewSrg::m_visibleSimplePointLightIndices[lightIndex], surface, lightingData);
-    }
-#endif
-}

+ 50 - 84
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/SimpleSpotLight.azsli

@@ -10,119 +10,85 @@
 
 #include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
 
-#if ENABLE_SHADOWS
-#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+#ifndef SimpleSpotLightUtil
+#define SimpleSpotLightUtil SimpleSpotLightUtil_PBR
 #endif
 
-void ApplySimpleSpotLight(SimpleSpotLight light, Surface surface, inout LightingData lightingData)
+class SimpleSpotLightUtil_PBR
 {
-    real3 posToLight = real3(light.m_position - surface.position);
+    real3 m_surfaceToLightDirection;
+    real  m_lightDistanceSquared;
+    real  m_dotWithDirection;
+    real  m_falloff;
 
-    real3 dirToLight = normalize(posToLight);
-    real dotWithDirection = dot(dirToLight, -real3(light.m_direction));
+    // filled in Apply(), but can be useful afterwards
+    real3 m_lightIntensity;
 
-    // If outside the outer cone angle return.
-    if (dotWithDirection < real(light.m_cosOuterConeAngle))
+    static SimpleSpotLightUtil_PBR Init(SimpleSpotLight light, Surface surface)
     {
-        return;
+        SimpleSpotLightUtil_PBR result;
+        real3 posToLight = real3(light.m_position - surface.position);
+        result.m_surfaceToLightDirection = normalize(posToLight);
+
+        result.m_lightDistanceSquared = dot(posToLight, posToLight); // light distance squared
+        result.m_falloff = result.m_lightDistanceSquared * real(light.m_invAttenuationRadiusSquared);
+
+        result.m_dotWithDirection = dot(result.m_surfaceToLightDirection, -real3(light.m_direction));
+        // If outside the outer cone angle return.
+        if (result.m_dotWithDirection < real(light.m_cosOuterConeAngle))
+        {
+            result.m_falloff = 1.0f;
+        }
+
+        return result;
+    }
+
+    real3 GetSurfaceToLightDirection()
+    {
+        return m_surfaceToLightDirection;
     }
 
-    real d2 = dot(posToLight, posToLight); // light distance squared
-    real falloff = d2 * real(light.m_invAttenuationRadiusSquared);
-    real cosInnerConeAngle = real(light.m_cosInnerConeAngle);
+    real GetFalloff()
+    {
+        return m_falloff;
+    }
 
-    // Only calculate shading if light is in range
-    if (falloff < 1.0)
+    void Apply(SimpleSpotLight light, Surface surface, real litRatio, inout LightingData lightingData)
     {
         // Smoothly adjusts the light intensity so it reaches 0 at light.m_attenuationRadius distance
-        real radiusAttenuation = 1.0 - (falloff * falloff);
+        real radiusAttenuation = 1.0 - (m_falloff * m_falloff);
         radiusAttenuation = radiusAttenuation * radiusAttenuation;
 
-        // Standard quadratic falloff
-        d2 = max(0.001 * 0.001, d2); // clamp the light to at least 1mm away to avoid extreme values.
-        real3 lightIntensity = (real3(light.m_rgbIntensityCandelas) / d2) * radiusAttenuation;
-        real3 posToLightDir = normalize(posToLight);
-
-        // shadow
-        float litRatio = 1.0;
-
-#if ENABLE_SHADOWS
-        if (o_enableShadows && o_enableSimpleSpotLightShadows)
-        {
-            const float3 lightDir = normalize(light.m_position - surface.position);
-            litRatio = ProjectedShadow::GetVisibility(
-                light.m_shadowIndex,
-                light.m_position,
-                surface.position,
-                lightDir,
-                surface.vertexNormal);
-        }
-#endif
+        // Standard quadratic m_falloff
+        m_lightDistanceSquared = max(0.001 * 0.001, m_lightDistanceSquared); // clamp the light to at least 1mm away to avoid extreme values.
+        m_lightIntensity = (real3(light.m_rgbIntensityCandelas) / m_lightDistanceSquared) * radiusAttenuation;
 
-        lightIntensity *=  SampleGoboTexture(light.m_goboTexIndex, PassSrg::LinearSampler, light.m_viewProjectionMatrix, surface.position);
+        // apply gobo texture
+        #if PIPELINE_HAS_PASS_SRG
+        m_lightIntensity *= SampleGoboTexture(light.m_goboTexIndex, PassSrg::LinearSampler, light.m_viewProjectionMatrix, surface.position);
+        #endif 
 
-        if (dotWithDirection < cosInnerConeAngle) // in penumbra
+        real cosInnerConeAngle = real(light.m_cosInnerConeAngle);
+        if (m_dotWithDirection < cosInnerConeAngle) // in penumbra
         {
             // Normalize into 0.0 - 1.0 space.
-            real penumbraMask = (dotWithDirection - real(light.m_cosOuterConeAngle)) / (cosInnerConeAngle - real(light.m_cosOuterConeAngle));
+            real penumbraMask = (m_dotWithDirection - real(light.m_cosOuterConeAngle)) / (cosInnerConeAngle - real(light.m_cosOuterConeAngle));
 
             // Apply smoothstep
             penumbraMask = penumbraMask * penumbraMask * (3.0 - 2.0 * penumbraMask);
 
-            lightIntensity *= penumbraMask;
+            m_lightIntensity *= penumbraMask;
         }
 
         // Diffuse contribution
-        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, m_lightIntensity, m_surfaceToLightDirection) * litRatio;
 
         // Calculate specular lighting for each view
         [unroll]
         for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
         {
             // Specular contribution
-            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, lightIntensity, posToLightDir, viewIndex) * litRatio;
-        }
-    }
-}
-
-void ApplySimpleSpotLightInternal(uint lightIndex, Surface surface, inout LightingData lightingData)
-{
-    if (o_enableSimpleSpotLights)
-    {
-        SimpleSpotLight light = ViewSrg::m_simpleSpotLights[lightIndex];
-        if(!IsSameLightChannel(lightingData.lightingChannels, light.m_lightingChannelMask))
-        {
-            return;
+            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, m_lightIntensity, m_surfaceToLightDirection, viewIndex) * litRatio;
         }
-        ApplySimpleSpotLight(light, surface, lightingData);
-    }
-}
-
-void ApplySimpleSpotLights(Surface surface, inout LightingData lightingData)
-{
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.LoadAdvance();
-
-    while( !lightingData.tileIterator.IsDone() )
-    {
-        uint currLightIndex = lightingData.tileIterator.GetValue();
-        lightingData.tileIterator.LoadAdvance();
-
-        ApplySimpleSpotLightInternal(currLightIndex, surface, lightingData);
-    }
-#else
-
-    // For perf we cap light count. If it was not set by the pipeline it will use the value specified below
-    // This is only applicable if ENABLE_LIGHT_CULLING is disabled (i.e no gpu culling)
-    #ifndef ENABLE_SIMPLE_SPOTLIGHTS_CAP
-        #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 20
-    #endif
-
-    // Since there's no GPU culling for simple spot lights, we rely on culling done by CPU
-    // Only apply visible spot lights
-    for(uint lightIndex = 0; (lightIndex < ENABLE_SIMPLE_SPOTLIGHTS_CAP && lightIndex < ViewSrg::m_visibleSimpleSpotLightCount); lightIndex++)
-    {
-        ApplySimpleSpotLightInternal(ViewSrg::m_visibleSimpleSpotLightIndices[lightIndex], surface, lightingData);
     }
-#endif
-}
+};

+ 5 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli

@@ -43,6 +43,11 @@ class Surface
     ClearCoatSurfaceData clearCoat;
 #endif
 
+    // ------- Lighting Channels -------
+
+    // from the object
+    uint lightingChannels;
+
     // ------- BasePbrSurfaceData -------
     
     precise real3 position;    //!< Position in world-space

+ 68 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassCapsuleLights.azsli

@@ -0,0 +1,68 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the CapsuleLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/CapsuleLight.azsli
+
+#if ENABLE_CAPSULE_LIGHTS
+
+#ifndef CapsuleLightUtil
+#error "CapsuleLightUtil needs to be defined"
+#endif // CapsuleLightUtil
+
+void ApplyCapsuleLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    CapsuleLight srgLight = ViewSrg::m_capsuleLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+
+    CapsuleLightUtil light = CapsuleLightUtil::Init(srgLight, surface);
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+    // CapsuleLights don't get shadows
+    real litRatio = 1.0f;
+
+#if ENABLE_AREA_LIGHT_VALIDATION
+    if (o_area_light_validation)
+    {
+        light.ApplySampled(srgLight, surface, lightingData);
+    }
+    else
+#endif // ENABLE_AREA_LIGHT_VALIDATION
+    {
+        light.Apply(srgLight, surface, litRatio, lightingData);
+    }
+}
+
+void ApplyCapsuleLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplyCapsuleLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
+    {
+        ApplyCapsuleLight(lightIndex, surface, lightingData);
+    }
+#endif /* ENABLE_LIGHT_CULLING */
+}
+#else 
+void ApplyCapsuleLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+    // empty
+}
+#endif /* ENABLE_CAPSULE_LIGHTS */
+
+

+ 48 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDecals.azsli

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+#include <Atom/Features/PBR/Decals.azsli>
+
+#if ENABLE_DECALS
+void ApplyDecals_ForwardPass(inout LightCullingTileIterator tileIterator, inout Surface surface)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();
+
+    while( !tileIterator.IsDone() )
+    {
+        uint currDecalIndex = tileIterator.GetValue();
+        tileIterator.LoadAdvance();
+
+        if (o_enableDecals)  // i need to advance the iterator, even if decals are disabled
+        {
+            ApplyDecal(currDecalIndex, surface);
+        }
+    }
+#else
+        // Since there's no GPU culling for decals, we rely on culling done by CPU
+        // Only apply visible decals
+    if (o_enableDecals)
+    {
+        for(uint decalIndex = 0; (decalIndex < ENABLE_DECALS_CAP && decalIndex < ViewSrg::m_visibleDecalCount); decalIndex++)
+        {
+            ApplyDecal(decalIndex, surface);
+        }
+    }
+#endif
+}
+
+#else
+void ApplyDecals_ForwardPass(inout LightCullingTileIterator tileIterator, inout Surface surface)
+{
+    //Not Enabled
+}
+#endif

+ 67 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDirectLighting.azsli

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <Atom/Features/Debug.azsli>
+
+#include <Atom/Features/Pipeline/Forward/ForwardPassCapsuleLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassDirectionalLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassDiskLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassPointLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassPolygonLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassQuadLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassSimplePointLights.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassSimpleSpotLights.azsli>
+
+void ApplyDirectLighting_ForwardPass(Surface surface, inout LightingData lightingData, float4 screenUv, inout LightCullingTileIterator tileIterator)
+{
+    if( IsDirectLightingEnabled() )
+    {
+        if (o_enableDirectionalLights)
+        {
+            ApplyDirectionalLights(surface, lightingData, screenUv);
+        }
+        if (o_enablePunctualLights)
+        {
+            ApplySimplePointLights(surface, lightingData, tileIterator);
+            ApplySimpleSpotLights(surface, lightingData, tileIterator);
+        }
+        if (o_enableAreaLights)
+        {
+            ApplyPointLights(surface, lightingData, tileIterator);
+            ApplyDiskLights(surface, lightingData, tileIterator);
+            ApplyCapsuleLights(surface, lightingData, tileIterator);
+            ApplyQuadLights(surface, lightingData, tileIterator);
+            ApplyPolygonLights(surface, lightingData, tileIterator);
+        }
+    }
+    else if(IsDebuggingEnabled_PLACEHOLDER() && GetRenderDebugViewMode() == RenderDebugViewMode::CascadeShadows)
+    {
+        if (o_enableDirectionalLights)
+        {
+            ApplyDirectionalLights(surface, lightingData, screenUv);
+        }
+    }
+    else if( UseDebugLight() )
+    {
+        real3 lightIntensity = real3(SceneSrg::m_debugLightingIntensity);
+        real3 lightDirection = real3(SceneSrg::m_debugLightingDirection);
+
+        // Diffuse lighting is view-independent
+        lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, lightDirection);
+
+        // Calculate specular lighting for each view
+        [unroll]
+        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+        {
+            lightingData.specularLighting[viewIndex] += GetSpecularLighting(surface, lightingData, lightIntensity, lightDirection, viewIndex);
+        }
+    }
+}
+

+ 100 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDirectionalLights.azsli

@@ -0,0 +1,100 @@
+#pragma once
+
+#if ENABLE_SHADOWS
+#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
+#endif
+
+#ifndef DirectionalLightUtil
+#error "DirectionalLightUtil needs to be defined"
+#endif // DirectionalLightUtil
+
+void ApplyDirectionalLights(Surface surface, inout LightingData lightingData, float4 screenUv)
+{
+    // Shadowed check
+    const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
+    real litRatio = 1.0;
+
+    // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+    // - If transmission mode is thick object -> use transmission thickness parameter instead
+    // - If transmission mode is thin object -> ignore back lighting
+    real transmissionDistance = -1.0;
+
+#if ENABLE_SHADOWS
+    if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount)
+    {           
+#if ENABLE_TRANSMISSION
+        if (o_transmission_mode == TransmissionMode::ThickObject)
+        {
+            real2 visibilityAndThickness = DirectionalLightShadow::GetVisibilityThickTransmission(shadowIndex, real3(surface.position), surface.vertexNormal, screenUv);
+            litRatio = visibilityAndThickness.x;
+            transmissionDistance = visibilityAndThickness.y;
+        } 
+        else if (o_transmission_mode == TransmissionMode::ThinObject) 
+        {
+            real2 visibilityAndThickness = DirectionalLightShadow::GetVisibilityThinTransmission(
+                                                shadowIndex, real3(surface.position), surface.vertexNormal, screenUv, surface.transmission.GetShrinkFactor());
+
+            litRatio = visibilityAndThickness.x;
+            transmissionDistance = visibilityAndThickness.y;
+        }
+        else
+        {
+            litRatio = DirectionalLightShadow::GetVisibility(shadowIndex, real3(surface.position), surface.vertexNormal, screenUv);
+        }
+#else 
+        litRatio = DirectionalLightShadow::GetVisibility(shadowIndex, surface.position, surface.vertexNormal, screenUv);
+#endif // ENABLE_TRANSMISSION
+    }
+#endif // ENABLE_SHADOWS
+
+    // Add the lighting contribution for each directional light
+    for (int index = 0; index < SceneSrg::m_directionalLightCount; index++)
+    {
+        DirectionalLight srgLight = SceneSrg::m_directionalLights[index];
+        
+        if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+        {
+            return;
+        }
+
+        DirectionalLightUtil light = DirectionalLightUtil::Init(srgLight, surface);
+
+        real currentLitRatio = 1.0;
+        real currentTransmissionDistance = -1.0;
+
+#if ENABLE_SHADOWS
+        if (o_enableShadows && index == shadowIndex)
+        {
+            // Add contribution only if current directional light is the active one for shadows
+            currentLitRatio = real(litRatio);
+            currentTransmissionDistance = transmissionDistance;
+        }
+#endif
+
+#if ENABLE_TRANSMISSION
+        light.SetTransmissionDistance(transmissionDistance);
+#endif /* ENABLE_TRANSMISSION */
+
+        // calculate the light contributions
+        light.Apply(SceneSrg::m_directionalLights[index], surface, currentLitRatio, lightingData);
+
+#if ENABLE_SHADER_DEBUGGING
+        if(IsDebuggingEnabled_PLACEHOLDER() && GetRenderDebugViewMode() == RenderDebugViewMode::CascadeShadows)
+        {
+            customDebugFloats.rgb = currentLitRatio.xxx;
+        }
+#endif
+    }
+
+#if ENABLE_SHADOWS    
+    // Add debug coloring for directional light shadow
+    if (o_enableShadows && shadowIndex <  SceneSrg::m_directionalLightCount)
+    {
+        [unroll]
+        for (uint viewIndex = 0; viewIndex < GET_SHADING_VIEW_COUNT; ++viewIndex)
+        {
+            lightingData.specularLighting[viewIndex] = DirectionalLightShadow::AddDebugColoring(lightingData.specularLighting[viewIndex], shadowIndex, real3(surface.position), surface.vertexNormal);
+        }
+    }
+#endif
+}

+ 100 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDiskLights.azsli

@@ -0,0 +1,100 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the DiskLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/DiskLight.azsli
+
+#if ENABLE_DISK_LIGHTS
+
+#if ENABLE_SHADOWS
+#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+#endif
+
+
+#ifndef DiskLightUtil
+#error "DiskLightUtil needs to be defined"
+#endif // DiskLightUtil
+
+void ApplyDiskLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    DiskLight srgLight = ViewSrg::m_diskLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+
+    DiskLightUtil light = DiskLightUtil::Init(srgLight, surface);
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    real litRatio = 1.0f;
+
+#if ENABLE_SHADOWS
+    if (o_enableShadows && o_enableDiskLightShadows)
+    {
+        litRatio = ProjectedShadow::GetVisibility(
+                srgLight.m_shadowIndex,
+                srgLight.m_position,
+                surface.position,
+                -light.GetDirectionToConeTip(),
+                surface.vertexNormal);
+             
+        real transmissionDistance = -1.0f;
+        // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
+#if ENABLE_TRANSMISSION            
+        if (o_transmission_mode == TransmissionMode::ThickObject)
+        {
+            transmissionDistance = ProjectedShadow::GetThickness(srgLight.m_shadowIndex, surface.position);
+        }
+        else if (o_transmission_mode == TransmissionMode::ThinObject)
+        {
+            transmissionDistance = ProjectedShadow::GetThickness(srgLight.m_shadowIndex, surface.position - surface.transmission.GetShrinkFactor() * surface.vertexNormal);
+        }
+        light.SetTransmissionDistance(transmissionDistance);
+#endif // ENABLE_TRANSMISSION
+    }
+#endif // ENABLE_SHADOWS
+
+#if ENABLE_AREA_LIGHT_VALIDATION
+    if (o_area_light_validation)
+    {
+        light.ApplySampled(srgLight, surface, lightingData);
+    }
+    else
+#endif // ENABLE_AREA_LIGHT_VALIDATION
+    {
+        light.Apply(srgLight, surface, litRatio, lightingData);
+    }
+}
+
+
+void ApplyDiskLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplyDiskLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
+    {
+        ApplyDiskLight(lightIndex, surface, lightingData);
+    }
+#endif /* ENABLE_LIGHT_CULLING */
+}
+#else 
+void ApplyDiskLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+    // empty
+}
+#endif /* ENABLE_DISK_LIGHTS */
+
+

+ 45 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassEvaluateLighting.azsli

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#ifndef EvaluateLighting
+#define EvaluateLighting EvaluateLighting_ForwardPass
+#endif
+
+#include <Atom/Features/Pipeline/Forward/ForwardPassDirectLighting.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassDecals.azsli>
+#include <Atom/Features/Pipeline/Forward/ForwardPassIbl.azsli>
+
+
+LightingData EvaluateLighting_ForwardPass(Surface surface, float4 screenPosition, float3 viewPositions[MAX_SHADING_VIEWS])
+{
+    LightingData lightingData;
+    InitializeLightingData(surface, viewPositions, lightingData);
+
+    // Light iterator
+    LightCullingTileIterator tileIterator;
+#if ENABLE_LIGHT_CULLING
+    tileIterator.Init(screenPosition, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
+#else
+    tileIterator.InitNoCulling();
+#endif
+
+    // Apply Decals
+    ApplyDecals_ForwardPass(tileIterator, surface);
+
+    // Apply Direct Lighting
+    ApplyDirectLighting_ForwardPass(surface, lightingData, screenPosition, tileIterator);
+
+    // Apply Image Based Lighting (IBL)
+    ApplyIbl_ForwardPass(surface, lightingData);
+
+    FinalizeLightingData(surface, lightingData);
+    return lightingData;
+}
+

+ 46 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassIbl.azsli

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+// This file provides exentions to Ibl.azsli that are specific to forward render pipelines.
+
+#include <Atom/Features/PBR/Lights/Ibl.azsli>
+
+option bool o_meshUseForwardPassIBLSpecular = false;
+option bool o_materialUseForwardPassIBLSpecular = false;
+
+#ifndef PIPELINE_HAS_OBJECT_SRG
+// assume by default that we have an object-srg
+#define PIPELINE_HAS_OBJECT_SRG 1
+#endif
+
+#if PIPELINE_HAS_OBJECT_SRG
+void ApplyIbl_ForwardPass(Surface surface, inout LightingData lightingData)
+{
+#if FORCE_IBL_IN_FORWARD_PASS
+    bool useDiffuseIbl = true;
+    bool useSpecularIbl = true;
+#else
+    #if FORCE_OPAQUE
+        bool isTransparent = false;
+    #else
+        bool isTransparent = (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent);
+    #endif
+    bool useDiffuseIbl = isTransparent;
+    bool useSpecularIbl = (isTransparent || o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular);
+#endif
+
+    ApplyIBL(surface, lightingData, useDiffuseIbl, useSpecularIbl, ObjectSrg::m_reflectionProbeData, ObjectSrg::m_reflectionProbeCubeMap);
+}
+#else 
+void ApplyIbl_ForwardPass(Surface surface, inout LightingData lightingData)
+{
+    // empty
+}
+#endif

+ 156 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPointLights.azsli

@@ -0,0 +1,156 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+#if ENABLE_SPHERE_LIGHTS
+// the PointLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/PointLight.azsli
+#if ENABLE_SHADOWS
+#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+
+int GetPointLightShadowCubemapFace(const float3 targetPos, const float3 lightPos)
+{
+    const float3 toPoint = targetPos - lightPos;    
+    const float maxElement = max(abs(toPoint.z), max(abs(toPoint.x), abs(toPoint.y)));
+    if (toPoint.x == -maxElement)
+    {
+        return 0;
+    }
+    else if (toPoint.x == maxElement)
+    {
+        return 1;
+    }   
+    else if (toPoint.y == -maxElement)
+    {
+        return 2;
+    }
+    else if (toPoint.y == maxElement)
+    {
+        return 3;
+    }
+    else if (toPoint.z == -maxElement)
+    {
+        return 4;
+    }
+    else 
+    {
+        return 5;
+    }   
+}
+
+// PointLight::m_shadowIndices actually consists of uint16_t x 6 on the CPU, but visible as a uint32_t x 3 on the GPU. 
+// This function returns the proper uint16_t value given an input face in the range 0-5
+int UnpackPointLightShadowIndex(const PointLight light, const int face)
+{
+    const int index = face >> 1;
+    const int shiftAmount = (face & 1) * 16;
+    return (light.m_shadowIndices[index] >> shiftAmount) & 0xFFFF;
+} 
+
+uint ComputeShadowIndex(const PointLight light, const Surface surface)
+{
+    // shadow map size and bias are the same across all shadowmaps used by a specific point light, so just grab the first one
+    const uint lightIndex0 = UnpackPointLightShadowIndex(light, 0);
+    const float shadowmapSize = ViewSrg::m_projectedFilterParams[lightIndex0].m_shadowmapSize;
+    
+    // Note that the normal bias offset could potentially move the shadowed position from one map to another map inside the same point light shadow.
+    const float normalBias = ViewSrg::m_projectedShadows[lightIndex0].m_normalShadowBias;
+    const float3 biasedPosition = surface.position + ComputeNormalShadowOffset(normalBias, surface.vertexNormal, shadowmapSize);
+    
+    const int shadowCubemapFace = GetPointLightShadowCubemapFace(biasedPosition, light.m_position);
+    return UnpackPointLightShadowIndex(light, shadowCubemapFace);
+}
+
+#endif // ENABLE_SHADOWS
+
+
+#ifndef PointLightUtil
+#error "PointLightUtil needs to be defined"
+#endif // PointLightUtil
+// the PointLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/PointLight.azsli
+
+void ApplyPointLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    PointLight srgLight = ViewSrg::m_pointLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+
+    PointLightUtil light = PointLightUtil::Init(srgLight, surface);
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    real litRatio = 1.0f;
+
+#if ENABLE_SHADOWS
+    if (o_enableShadows && o_enableSphereLightShadows)
+    {
+        const uint shadowIndex = ComputeShadowIndex(srgLight, surface);
+        litRatio *= real(ProjectedShadow::GetVisibility(
+                    shadowIndex,
+                    srgLight.m_position,
+                    surface.position,
+                    light.GetSurfaceToLightDirection(),
+                    surface.vertexNormal));
+
+#if ENABLE_TRANSMISSION
+        real transmissionDistance = -1.0f;
+        // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
+        if (o_transmission_mode == TransmissionMode::ThickObject)
+        {
+            transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position);
+        }
+        else if (o_transmission_mode == TransmissionMode::ThinObject)
+        {
+            transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position - surface.transmission.GetShrinkFactor() * surface.vertexNormal);
+        }
+        light.SetTransmissionDistance(transmissionDistance);
+#endif // ENABLE_TRANSMISSION
+    }
+#endif // ENABLE_SHADOWS
+
+#if ENABLE_AREA_LIGHT_VALIDATION
+    if (o_area_light_validation)
+    {
+        light.ApplySampled(srgLight, surface, lightingData);
+    }
+    else
+#endif // ENABLE_AREA_LIGHT_VALIDATION
+    {
+        light.Apply(srgLight, surface, litRatio, lightingData);
+    }
+
+}
+
+
+void ApplyPointLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplyPointLight(currLightIndex, surface, lightingData);
+    }
+#else  // ENABLE_LIGHT_CULLING
+
+    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
+    {
+        ApplyPointLight(lightIndex, surface, lightingData);
+    }
+#endif // ENABLE_LIGHT_CULLING
+}
+
+#else // ENABLE_SPHERE_LIGHTS
+void ApplyPointLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+    // empty
+}
+
+#endif /* ENABLE_SPHERE_LIGHTS */ 
+

+ 61 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPolygonLights.azsli

@@ -0,0 +1,61 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the PolygonLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/PolygonLight.azsli
+
+#if ENABLE_POLYGON_LTC_LIGHTS
+
+#ifndef PolygonLightUtil
+#error "PolygonLightUtil needs to be defined"
+#endif // PolygonLightUtil
+
+void ApplyPolygonLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    PolygonLight srgLight = ViewSrg::m_polygonLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+
+    PolygonLightUtil light = PolygonLightUtil::Init(srgLight, surface);
+
+    // Polygon Lights don't get shadows
+    real litRatio = 1.0f;
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    light.Apply(srgLight, surface, litRatio, lightingData);
+}
+
+
+void ApplyPolygonLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplyPolygonLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
+    {
+        ApplyPolygonLight(lightIndex, surface, lightingData);
+    }
+#endif /* ENABLE_LIGHT_CULLING */
+}
+#else 
+void ApplyPolygonLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+    // empty
+}
+#endif /* ENABLE_POLYGON_LTC_LIGHTS */
+
+

+ 70 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassQuadLights.azsli

@@ -0,0 +1,70 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the QuadLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/QuadLight.azsli
+
+#if ENABLE_QUAD_LIGHTS
+
+#ifndef QuadLightUtil
+#error "QuadLightUtil needs to be defined"
+#endif // QuadLightUtil
+
+void ApplyQuadLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    QuadLight srgLight = ViewSrg::m_quadLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+    
+    QuadLightUtil light = QuadLightUtil::Init(srgLight, surface);
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    // QuadLights don't get shadows
+    real litRatio = 1.0f;
+
+#if ENABLE_AREA_LIGHT_VALIDATION
+    if (o_area_light_validation)
+    {
+        light.ApplySampled(srgLight, surface, lightingData);
+    }
+    else
+#endif // ENABLE_AREA_LIGHT_VALIDATION
+    {
+        light.Apply(srgLight, surface, litRatio, lightingData);
+    }
+}
+
+
+void ApplyQuadLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplyQuadLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    for(uint lightIndex = 0; lightIndex < ViewSrg::m_pointLightCount; lightIndex++)
+    {
+        ApplyQuadLight(lightIndex, surface, lightingData);
+    }
+#endif /* ENABLE_LIGHT_CULLING */
+}
+#else 
+void ApplyQuadLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+    // empty
+}
+#endif /* ENABLE_DISK_LIGHTS */
+
+

+ 60 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimplePointLights.azsli

@@ -0,0 +1,60 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the SimplePointLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/SimplePointLight.azsli
+
+#ifndef SimplePointLightUtil
+#error "SimplePointLightUtil needs to be defined"
+#endif // SimplePointLightUtil
+
+void ApplySimplePointLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    SimplePointLight srgLight = ViewSrg::m_simplePointLights[lightIndex];
+
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+    
+    SimplePointLightUtil light = SimplePointLightUtil::Init(srgLight, surface);
+
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    // Simple Point lights don't get shadows
+    real litRatio = 1.0f; 
+
+    light.Apply(srgLight, surface, litRatio, lightingData);
+}
+
+
+void ApplySimplePointLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();    
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+        ApplySimplePointLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    // For perf we cap light count. If it was not set by the pipeline it will use the value specified below
+    // This is only applicable if ENABLE_LIGHT_CULLING is disabled (i.e no gpu culling)
+    #ifndef ENABLE_SIMPLE_POINTLIGHTS_CAP
+        #define ENABLE_SIMPLE_POINTLIGHTS_CAP 20 
+    #endif
+
+    // Since there's no GPU culling for simple point lights, we rely on culling done by CPU
+    // Only apply visible point lights
+    for(uint lightIndex = 0; (lightIndex < ENABLE_SIMPLE_POINTLIGHTS_CAP && lightIndex < ViewSrg::m_visibleSimplePointLightCount); lightIndex++)
+    {
+        ApplySimplePointLight(ViewSrg::m_visibleSimplePointLightIndices[lightIndex], surface, lightingData);
+    }
+#endif
+}
+

+ 76 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimpleSpotLights.azsli

@@ -0,0 +1,76 @@
+#pragma once
+
+#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
+
+// the SimpleSpotLight class is defined by the Material, but usually points to Atom/Features/Light/PBR/SimpleSpotLight.azsli
+
+#if ENABLE_SHADOWS
+#include <Atom/Features/Shadow/ProjectedShadow.azsli>
+#endif
+
+#ifndef SimpleSpotLightUtil
+#error "SimpleSpotLightUtil needs to be defined"
+#endif // SimpleSpotLightUtil
+
+void ApplySimpleSpotLight(int lightIndex, Surface surface, inout LightingData lightingData)
+{
+    SimpleSpotLight srgLight = ViewSrg::m_simpleSpotLights[lightIndex];
+ 
+    if (!IsSameLightChannel(srgLight.m_lightingChannelMask, surface.lightingChannels))
+    {
+        return;
+    }
+    
+    SimpleSpotLightUtil light = SimpleSpotLightUtil::Init(srgLight, surface);
+    
+    if (light.GetFalloff() >= 1.0f)
+    {
+        return;
+    }
+
+    real litRatio = 1.0f;
+
+#if ENABLE_SHADOWS
+    if (o_enableShadows && o_enableSimpleSpotLightShadows)
+    {
+        litRatio = ProjectedShadow::GetVisibility(
+                srgLight.m_shadowIndex,
+                srgLight.m_position,
+                surface.position,
+                light.GetSurfaceToLightDirection(),
+                surface.vertexNormal);
+    }
+#endif
+
+    light.Apply(srgLight, surface, litRatio, lightingData);
+}
+
+
+void ApplySimpleSpotLights(Surface surface, inout LightingData lightingData, inout LightCullingTileIterator tileIterator)
+{
+#if ENABLE_LIGHT_CULLING
+    tileIterator.LoadAdvance();
+    while( !tileIterator.IsDone() ) 
+    { 
+        uint currLightIndex = tileIterator.GetValue(); 
+        tileIterator.LoadAdvance();
+
+        ApplySimpleSpotLight(currLightIndex, surface, lightingData);
+    }
+#else
+
+    // For perf we cap light count. If it was not set by the pipeline it will use the value specified below
+    // This is only applicable if ENABLE_LIGHT_CULLING is disabled (i.e no gpu culling)
+    #ifndef ENABLE_SIMPLE_SPOTLIGHTS_CAP
+        #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 20 
+    #endif
+
+    // Since there's no GPU culling for simple spot lights, we rely on culling done by CPU
+    // Only apply visible spot lights
+    for(uint lightIndex = 0; (lightIndex < ENABLE_SIMPLE_SPOTLIGHTS_CAP && lightIndex < ViewSrg::m_visibleSimpleSpotLightCount); lightIndex++)
+    {
+        ApplySimpleSpotLight(ViewSrg::m_visibleSimpleSpotLightIndices[lightIndex], surface, lightingData);
+    }
+#endif
+
+}

+ 40 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Pipeline/Forward/Readme.md

@@ -0,0 +1,40 @@
+## Light-evaluation for the Forward pass
+
+The files in this folder are intended to be used with the Forward+ - pipeline, or something very similar to it.
+
+The main entry point is in `ForwardPassEvaluateLighting.azsli`, with the function `LightingData EvaluateLighting(Surface surface, float4 screenPosition, float3 viewPosition, VsSystemValues SV);`
+
+The main assumptions are:
+- The light assignment is tile-based in screenspace, and uses the `LightCullingTileIterator`
+  - The variables `PassSrg::m_lightListRemapped` and `PassSrg::m_tileLightData` are accessible
+  - The define `ENABLE_LIGHT_CULLING` can be used if this is not the case, and no culling should be used.
+  - The lights are stored in the `ViewSrg`
+
+- Following light-types with various different methods of shadows are supported:
+  - Decals
+    - Not strictly speaking lights, but they use the same light assignment method
+    - Decal data is from the `ViewSrg`
+  - CapsuleLight
+    - No Shadows
+    - Can be analytical (ltc) or sampled, although sampling is slow and exists mostly to verify the analytical solution
+  - DirectionalLight
+    - only one directional light can cast shadows
+    - Uses Cascaded shadowmaps from the `PassSrg` (via the `DirectionalLightShadow` class)
+  - DiskLight (aka spot-light with area)
+    - Uses projected shadows from the `ViewSrg`
+    - Can be analytical (ltc) or sampled, although sampling is slow
+  - IBL
+    - Uses environment maps from the `SceneSrg` and reflection probes from the `ObjectSrg`
+  - PointLight (aka sphere light)
+    - Uses 6 projected shadows from the `ViewSrg` as a cube-map
+    - Can be analytical (ltc) or sampled, although sampling is slow
+  - PolygonLight
+    - No Shadows
+    - Analytical evaluation with linearly transformed cosines (LTC)
+  - QuadLight
+    - No Shadows
+    - Can be analytical (ltc) or sampled, although sampling is slow
+  - SimplePointLights (withouth volume)
+    - No shadows
+  - SimpleSpotLights (aka spot-light without diameter)
+    - uses projected shadows from the `ViewSrg`

+ 0 - 17
Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli

@@ -12,23 +12,6 @@
 
 partial ShaderResourceGroup ViewSrg
 { 
-    struct Decal
-    {
-        float3 m_position;
-        float m_opacity;
-        
-        float4 m_quaternion;
-        float3 m_halfSize;
-        float m_angleAttenuation;
-        float m_normalMapOpacity;
-        uint m_sortKeyPacked;
-        uint m_textureArrayIndex;
-        uint m_textureIndex;
-        float3 m_decalColor;
-        float m_decalColorFactor;
-        [[pad_to(16)]]
-    };
-
     StructuredBuffer<Decal> m_decals; 
     Buffer<uint> m_visibleDecalIndices; 
 

+ 3 - 31
Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli

@@ -17,7 +17,6 @@
 
 #include <viewsrg_all.srgi>
 #include <Atom/Features/Debug.azsli>
-#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
 #include <Atom/Features/PBR/LightingUtils.azsli>
 #include <Atom/Features/PBR/LightingOptions.azsli>
 
@@ -29,12 +28,11 @@
 #define GET_SHADING_VIEW_COUNT 1
 #endif
 
+// Forward declaration for a wrapper arround SampleBrdfMap(). 
+float2 MySampleBrdfMap(float2 brdfUV);
+
 class LightingData_BasePBR
 {
-    LightCullingTileIterator tileIterator;
-
-    uint lightingChannels;
-
     // Lighting outputs
     real3 diffuseLighting;
     real3 specularLighting[MAX_SHADING_VIEWS];
@@ -75,32 +73,6 @@ class LightingData_BasePBR
     float GetSpecularNdotV(uint i) { return NdotV[i]; }
 };
 
-void LightingData_BasePBR::Init(float3 positionWS, real3 specularNormal, real roughnessLinear, float3 viewPosition[MAX_SHADING_VIEWS])
-{
-    diffuseLighting = real3(0.0, 0.0, 0.0);
-    multiScatterCompensation = real3(1.0, 1.0, 1.0);
-    emissiveLighting = real3(0.0, 0.0, 0.0);
-#if ENABLE_TRANSMISSION
-    translucentBackLighting = real3(0.0, 0.0, 0.0);
-#endif
-    diffuseAmbientOcclusion = 1.0;
-    specularOcclusion = 1.0;
-
-    lightingChannels = 0xFFFFFFFF;
-
-    [unroll]
-    for(uint i = 0; i < GET_SHADING_VIEW_COUNT; ++i)
-    {
-        specularLighting[i] = real3(0.0, 0.0, 0.0);
-        dirToCamera[i] = normalize(viewPosition[i] - positionWS);
-        NdotV[i] = saturate(dot(specularNormal, dirToCamera[i]));
-
-        // sample BRDF map (indexed by smoothness values rather than roughness)
-        float2 brdfUV = float2(NdotV[i], (1.0 - roughnessLinear));
-        brdf[i] = real2(SampleBrdfMap(brdfUV));
-    }
-}
-
 void LightingData_BasePBR::CalculateMultiscatterCompensation(real3 specularF0, bool enabled)
 {
     // Default to using 0 for brdf viewIndex as we don't want to pay the VGPR cost of

+ 40 - 34
Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli

@@ -8,29 +8,49 @@
 
 #pragma once
 
-// This #define magic lets you use the EvaluateLighting function in this file without making it the final EvaluateLighting
-// used in your shader. Simply #define EvaluateLighting to your custom definition before including this file
-//
-#ifndef EvaluateLighting
-#define EvaluateLighting EvaluateLighting_BasePBR
+#ifndef InitializeLightingData
+#define InitializeLightingData InitializeLightingData_BasePBR
 #endif
 
-#include <Atom/Features/PBR/Lights/Lights.azsli>
-#include <Atom/Features/PBR/Lights/IblForward.azsli>
-#include <Atom/Features/PBR/Decals.azsli>
+#ifndef FinalizeLightingData
+#define FinalizeLightingData FinalizeLightingData_BasePBR
+#endif
 
-void InitializeLightingData_BasePBR(inout Surface surface, float4 screenPosition, float3 viewPositions[MAX_SHADING_VIEWS], inout LightingData lightingData)
+// Wrapper arround SampleBrdfMap(). 
+// We need this because while the function is defined between the LightingData_BasePBR - class definition and the LightingData_BasePBR::Init() declaration, 
+// azslc puts the function declaration with the class definition, and then doesn't find the SampleBrdfMap() function.
+// And azslc tends to crash if we add a forward declaration for SampleBrdfMap() directly.
+float2 MySampleBrdfMap(float2 brdfUV)
 {
-    // Light iterator
-#if ENABLE_LIGHT_CULLING
-    lightingData.tileIterator.Init(screenPosition, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
-#else
-    lightingData.tileIterator.InitNoCulling();
+    return SampleBrdfMap(brdfUV);
+}
+
+void LightingData_BasePBR::Init(float3 positionWS, real3 specularNormal, real roughnessLinear, float3 viewPosition[MAX_SHADING_VIEWS])
+{
+    diffuseLighting = real3(0.0, 0.0, 0.0);
+    multiScatterCompensation = real3(1.0, 1.0, 1.0);
+    emissiveLighting = real3(0.0, 0.0, 0.0);
+#if ENABLE_TRANSMISSION
+    translucentBackLighting = real3(0.0, 0.0, 0.0);
 #endif
+    diffuseAmbientOcclusion = 1.0;
+    specularOcclusion = 1.0;
 
-    lightingData.Init(surface.position, surface.GetSpecularNormal(), surface.roughnessLinear, viewPositions);
+    [unroll]
+    for(uint i = 0; i < GET_SHADING_VIEW_COUNT; ++i)
+    {
+        specularLighting[i] = real3(0.0, 0.0, 0.0);
+        dirToCamera[i] = normalize(viewPosition[i] - positionWS);
+        NdotV[i] = saturate(dot(specularNormal, dirToCamera[i]));
+        // sample BRDF map (indexed by smoothness values rather than roughness)
+        float2 brdfUV = float2(NdotV[i], (1.0 - roughnessLinear));
+        brdf[i] = real2(MySampleBrdfMap(brdfUV));
+    }
+}
 
-    lightingData.lightingChannels = ObjectSrg::m_lightingChannelMask;
+void InitializeLightingData_BasePBR(Surface surface, float3 viewPositions[MAX_SHADING_VIEWS], inout LightingData lightingData)
+{
+    lightingData.Init(surface.position, surface.GetSpecularNormal(),  surface.roughnessLinear, viewPositions);
 
     // Diffuse and Specular response (used in IBL calculations)
     real3 specularResponse = FresnelSchlickWithRoughness(lightingData.GetSpecularNdotV(), surface.GetSpecularF0(), surface.roughnessLinear);
@@ -40,25 +60,11 @@ void InitializeLightingData_BasePBR(inout Surface surface, float4 screenPosition
     lightingData.CalculateMultiscatterCompensation(surface.GetSpecularF0(), o_specularF0_enableMultiScatterCompensation);
 }
 
-void CalculateLighting_BasePBR(inout Surface surface, float4 screenPosition, inout LightingData lightingData)
+void FinalizeLightingData_BasePBR(Surface surface, inout LightingData lightingData)
 {
-    // Apply Decals
-    ApplyDecals(lightingData.tileIterator, surface);
-
-    // Apply Direct Lighting
-    ApplyDirectLighting(surface, lightingData, screenPosition);
-
-    // Apply Image Based Lighting (IBL)
-    ApplyIblForward(surface, lightingData);
-
-    // Finalize Lighting
     lightingData.FinalizeLighting(surface);
 }
 
-LightingData EvaluateLighting_BasePBR(inout Surface surface, float4 screenPosition, float3 viewPositions[MAX_SHADING_VIEWS])
-{
-    LightingData lightingData;
-    InitializeLightingData_BasePBR(surface, screenPosition, viewPositions, lightingData);
-    CalculateLighting_BasePBR(surface, screenPosition, lightingData);
-    return lightingData;
-}
+// use the default PBR lights for all supported light types
+#include <Atom/Features/PBR/Lights/Lights.azsli>
+

+ 3 - 0
Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceData.azsli

@@ -20,6 +20,9 @@
 
 class SurfaceData_BasePBR
 {   
+
+    uint lightingChannels;     //!< Lightingchannel of the object
+
     precise float3 position;   //!< Position in world-space
     real3 normal;              //!< Normal in world-space
     real3 vertexNormal;        //!< Vertex normal in world-space

+ 20 - 12
Gems/Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_SurfaceEval.azsli

@@ -43,10 +43,12 @@ Surface EvaluateSurface_BasePBR(
     bool isFrontFace,
     float4 uvDxDy,
     bool customDerivatives,
-    float4 vertexColor)
+    float4 vertexColor,
+    uint lightingChannels)
 {
     Surface surface;
     surface.position = positionWS;
+    surface.lightingChannels = lightingChannels;
 
     // ------- Normal -------
 
@@ -114,7 +116,8 @@ Surface EvaluateSurface_BasePBR(
     float2 uvs[UvSetCount],
     bool isFrontFace,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     return EvaluateSurface_BasePBR(
         params,
@@ -126,7 +129,8 @@ Surface EvaluateSurface_BasePBR(
         isFrontFace,
         uvDxDy,
         customDerivatives,
-        float4(1.0f, 1.0f, 1.0f, 1.0f));
+        float4(1.0f, 1.0f, 1.0f, 1.0f), /* VertexColor */
+        lightingChannels);
 }
 
 Surface EvaluateSurface_BasePBR(
@@ -136,7 +140,8 @@ Surface EvaluateSurface_BasePBR(
     float3 tangents[UvSetCount],
     float3 bitangents[UvSetCount],
     float2 uvs[UvSetCount],
-    bool isFrontFace)
+    bool isFrontFace,
+    uint lightingChannels)
 {
     return EvaluateSurface_BasePBR(
         params,
@@ -146,11 +151,12 @@ Surface EvaluateSurface_BasePBR(
         bitangents,
         uvs,
         isFrontFace,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false, /* customDerivatives */
+        lightingChannels);
 }
 
-Surface EvaluateSurface_BasePBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_BasePBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_BasePBR(
         params,
@@ -162,10 +168,11 @@ Surface EvaluateSurface_BasePBR(VsOutput IN, PixelGeometryData geoData, const Ma
         geoData.isFrontFace,
         uvDxDy,
         customDerivatives,
-        GetVertexColor(IN));
+        GetVertexColor(IN),
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_BasePBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_BasePBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_BasePBR(
         params,
@@ -175,7 +182,8 @@ Surface EvaluateSurface_BasePBR(VsOutput IN, PixelGeometryData geoData, const Ma
         geoData.bitangents,
         geoData.uvs,
         geoData.isFrontFace,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false,
-        GetVertexColor(IN));
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false,  /* customDerivatives */     
+        GetVertexColor(IN),
+        GetLightingChannelMask(SV));
 }

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceData.azsli

@@ -48,6 +48,8 @@
         TransmissionSurfaceData transmission;
 #endif
 
+        uint lightingChannels;     //!< Lightingchannel of the object
+
         // ------- BasePbrSurfaceData -------
     
         float3 position;            //!< Position in world-space

+ 18 - 10
Gems/Atom/Feature/Common/Assets/Shaders/Materials/EnhancedPBR/EnhancedPBR_SurfaceEval.azsli

@@ -30,10 +30,12 @@ Surface EvaluateSurface_EnhancedPBR(
     bool isDisplacementClipped,
     float4 uvDxDy,
     bool customDerivatives,
-    float4 vertexColor)
+    float4 vertexColor,
+    uint lightingChannels)
 {
     Surface surface;
     surface.position = positionWS;
+    surface.lightingChannels = lightingChannels;
 
     // ------- Detail Layer Setup -------
     
@@ -191,7 +193,8 @@ Surface EvaluateSurface_EnhancedPBR(
     bool isFrontFace,
     bool isDisplacementClipped,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     return EvaluateSurface_EnhancedPBR(
             params,
@@ -205,7 +208,8 @@ Surface EvaluateSurface_EnhancedPBR(
             isDisplacementClipped,
             uvDxDy,
             customDerivatives,
-            float4(1.0f, 1.0f, 1.0f, 1.0f));
+            float4(1.0f, 1.0f, 1.0f, 1.0f) /* VertexColor */,
+            lightingChannels);
 }
 
 Surface EvaluateSurface_EnhancedPBR(
@@ -217,7 +221,8 @@ Surface EvaluateSurface_EnhancedPBR(
     float2 uvs[UvSetCount],
     float2 detailUvs[UvSetCount],
     bool isFrontFace,
-    bool isDisplacementClipped)
+    bool isDisplacementClipped,
+    uint lightingChannels)
 {
     return EvaluateSurface_EnhancedPBR(
         params,
@@ -229,11 +234,12 @@ Surface EvaluateSurface_EnhancedPBR(
         detailUvs,
         isFrontFace,
         isDisplacementClipped,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f) /* uvDxDy */,
+        false /* customDerivatives*/,
+        lightingChannels);
 }
 
-Surface EvaluateSurface_EnhancedPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_EnhancedPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData,  const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_EnhancedPBR(
         params,
@@ -247,10 +253,11 @@ Surface EvaluateSurface_EnhancedPBR(VsOutput IN, PixelGeometryData geoData, cons
         geoData.isDisplacementClipped,
         uvDxDy,
         customDerivatives,
-        GetVertexColor(IN));
+        GetVertexColor(IN), 
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_EnhancedPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_EnhancedPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_EnhancedPBR(
         params,
@@ -264,5 +271,6 @@ Surface EvaluateSurface_EnhancedPBR(VsOutput IN, PixelGeometryData geoData, cons
         geoData.isDisplacementClipped,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
         false,
-        GetVertexColor(IN));
+        GetVertexColor(IN), 
+        GetLightingChannelMask(SV));
 }

+ 13 - 7
Gems/Atom/Feature/Common/Assets/Shaders/Materials/Eye/Eye_SurfaceEval.azsli

@@ -63,10 +63,12 @@ Surface EvaluateSurface_Eye(
     float4x4 worldMatrix,
     float3x3 worldMatrixIT,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     Surface surface;
     surface.position = positionWS;
+    surface.lightingChannels = lightingChannels;
 
     // ------- Iris/Sclera Layer Setup -------
 
@@ -252,7 +254,8 @@ Surface EvaluateSurface_Eye(
     float3 localPosition,
     bool isFrontFace,
     real4x4 worldMatrix,
-    real3x3 worldMatrixIT)
+    real3x3 worldMatrixIT,
+    uint lightingChannels)
 {
     return EvaluateSurface_Eye(
         params,
@@ -266,10 +269,11 @@ Surface EvaluateSurface_Eye(
         worldMatrix,
         worldMatrixIT,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        lightingChannels);
 }
 
-Surface EvaluateSurface_Eye(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_Eye(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_Eye(
         params,
@@ -283,10 +287,11 @@ Surface EvaluateSurface_Eye(VsOutput IN, PixelGeometryData geoData, const Materi
         geoData.objectToWorld,
         geoData.objectToWorldIT,
         uvDxDy,
-        customDerivatives);
+        customDerivatives,
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_Eye(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_Eye(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_Eye(
         params,
@@ -300,5 +305,6 @@ Surface EvaluateSurface_Eye(VsOutput IN, PixelGeometryData geoData, const Materi
         geoData.objectToWorld,
         geoData.objectToWorldIT,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        GetLightingChannelMask(SV));
 }

+ 10 - 9
Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_LightingEval.azsli

@@ -11,26 +11,27 @@
 // This #define magic lets you use the EvaluateLighting function in this file without making it the final EvaluateLighting
 // used in your shader. Simply #define EvaluateLighting to your custom definition before including this file
 //
-#ifndef EvaluateLighting
-#define EvaluateLighting EvaluateLighting_SkinPBR
+#ifndef InitializeLightingData
+#define InitializeLightingData InitializeLightingData_SkinPBR
 #endif
 
+#ifndef FinalizeLightingData
+#define FinalizeLightingData FinalizeLightingData_SkinPBR
+#endif
 #include "../BasePBR/BasePBR_LightingEval.azsli"
 
-void InitializeLightingData_SkinPBR(inout Surface surface, float4 screenPosition, float3 viewPosition[MAX_SHADING_VIEWS], inout LightingData lightingData)
+
+void InitializeLightingData_SkinPBR(inout Surface surface, float3 viewPosition[MAX_SHADING_VIEWS], inout LightingData lightingData)
 {
     // --- Base PBR ---
-    InitializeLightingData_BasePBR(surface, screenPosition, viewPosition, lightingData);
+    InitializeLightingData_BasePBR(surface, viewPosition, lightingData);
 
     // --- Skin PBR ---
     lightingData.diffuseAmbientOcclusion = surface.diffuseAmbientOcclusion;
     lightingData.specularOcclusion = surface.specularOcclusion;
 }
 
-LightingData EvaluateLighting_SkinPBR(inout Surface surface, float4 screenPosition, float3 viewPosition[MAX_SHADING_VIEWS])
+void FinalizeLightingData_SkinPBR(inout Surface surface, inout LightingData lightingData)
 {
-    LightingData lightingData;
-    InitializeLightingData_SkinPBR(surface, screenPosition, viewPosition, lightingData);
-    CalculateLighting_BasePBR(surface, screenPosition, lightingData);
-    return lightingData;
+    FinalizeLightingData_BasePBR(surface, lightingData);
 }

+ 3 - 1
Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceData.azsli

@@ -25,7 +25,9 @@ class SurfaceData_Skin
 #endif
 
     // ------- BasePbrSurfaceData -------
-    
+
+    uint lightingChannels;     //!< Lightingchannel of the object
+
     float3 position;           //!< Position in world-space
     real3 normal;              //!< Normal in world-space
     real3 vertexNormal;        //!< Vertex normal in world-space

+ 15 - 9
Gems/Atom/Feature/Common/Assets/Shaders/Materials/Skin/Skin_SurfaceEval.azsli

@@ -95,10 +95,12 @@ Surface EvaluateSurface_Skin(
     real4 wrinkleBlendFactors,
     bool isFrontFace,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives, 
+    VsSystemValues SV)
 {
     Surface surface;
     surface.position = positionWS;
+    surface.lightingChannels = GetLightingChannelMask(SV);
 
     // ------- Detail Layer Setup -------
     
@@ -289,7 +291,8 @@ Surface EvaluateSurface_Skin(
     float2 uvs[UvSetCount],
     float2 detailUv,
     real4 wrinkleBlendFactors,
-    bool isFrontFace)
+    bool isFrontFace,
+    VsSystemValues SV)
 {
     return EvaluateSurface_Skin(
         params,
@@ -301,11 +304,12 @@ Surface EvaluateSurface_Skin(
         detailUv,
         wrinkleBlendFactors,
         isFrontFace,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false,  /* customDerivatives */
+        SV);
 }
 
-Surface EvaluateSurface_Skin(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_Skin(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_Skin(
         params,
@@ -318,10 +322,11 @@ Surface EvaluateSurface_Skin(VsOutput IN, PixelGeometryData geoData, const Mater
         geoData.wrinkleBlendFactors,
         geoData.isFrontFace,
         uvDxDy,
-        customDerivatives);
+        customDerivatives, 
+        SV);
 }
 
-Surface EvaluateSurface_Skin(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_Skin(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_Skin(
         params,
@@ -333,6 +338,7 @@ Surface EvaluateSurface_Skin(VsOutput IN, PixelGeometryData geoData, const Mater
         geoData.detailUv,
         geoData.wrinkleBlendFactors,
         geoData.isFrontFace,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */ 
+        false,  /* customDerivatives */
+        SV);
 }

+ 14 - 8
Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardMultilayerPBR/StandardMultilayerPBR_SurfaceEval.azsli

@@ -209,7 +209,8 @@ Surface EvaluateSurface_StandardMultilayerPBR(
     bool isDisplacementClipped,
     real3 vertexBlendMask,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     LayerBlendSource blendSource = GetFinalLayerBlendSource();
     
@@ -303,6 +304,7 @@ Surface EvaluateSurface_StandardMultilayerPBR(
     // ------- Combine all layers ---------
 
     Surface surface;
+    surface.lightingChannels = lightingChannels;
     surface.position = positionWS;
     // ------- Combine Normals ---------
     real3 normalTS = real3(0,0,1);
@@ -365,7 +367,8 @@ Surface EvaluateSurface_StandardMultilayerPBR(
     float2 uvs[UvSetCount],
     bool isFrontFace,
     bool isDisplacementClipped,
-    real3 vertexBlendMask)
+    real3 vertexBlendMask,
+    uint lightingChannels)
 {
     return EvaluateSurface_StandardMultilayerPBR(
         params,
@@ -378,10 +381,11 @@ Surface EvaluateSurface_StandardMultilayerPBR(
         isDisplacementClipped,
         vertexBlendMask,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        lightingChannels);
 }
 
-Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_StandardMultilayerPBR(
         params,
@@ -394,10 +398,11 @@ Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, PixelGeometryData geo
         geoData.isDisplacementClipped,
         geoData.m_vertexBlendMask,
         uvDxDy,
-        customDerivatives);
+        customDerivatives, 
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_StandardMultilayerPBR(
         params,
@@ -409,6 +414,7 @@ Surface EvaluateSurface_StandardMultilayerPBR(VsOutput IN, PixelGeometryData geo
         geoData.isFrontFace,
         geoData.isDisplacementClipped,
         geoData.m_vertexBlendMask,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false, /* customDerivatives */
+        GetLightingChannelMask(SV));
 }

+ 11 - 9
Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli

@@ -11,16 +11,20 @@
 // This #define magic lets you use the EvaluateLighting function in this file without making it the final EvaluateLighting
 // used in your shader. Simply #define EvaluateLighting to your custom definition before including this file
 //
-#ifndef EvaluateLighting
-#define EvaluateLighting EvaluateLighting_StandardPBR
+#ifndef InitializeLightingData
+#define InitializeLightingData InitializeLightingData_StandardPBR
+#endif
+
+#ifndef FinalizeLightingData
+#define FinalizeLightingData FinalizeLightingData_StandardPBR
 #endif
 
 #include "../BasePBR/BasePBR_LightingEval.azsli"
 
-void InitializeLightingData_StandardPBR(inout Surface surface, float4 screenPosition, float3 viewPosition[MAX_SHADING_VIEWS], inout LightingData lightingData)
+void InitializeLightingData_StandardPBR(inout Surface surface, float3 viewPosition[MAX_SHADING_VIEWS], inout LightingData lightingData)
 {
     // --- Base PBR ---
-    InitializeLightingData_BasePBR(surface, screenPosition, viewPosition, lightingData);
+    InitializeLightingData_BasePBR(surface, viewPosition, lightingData);
 
     // --- Standard PBR ---
 
@@ -37,10 +41,8 @@ void InitializeLightingData_StandardPBR(inout Surface surface, float4 screenPosi
 #endif
 }
 
-LightingData EvaluateLighting_StandardPBR(inout Surface surface, float4 screenPosition, float3 viewPosition[MAX_SHADING_VIEWS])
+void FinalizeLightingData_StandardPBR(inout Surface surface, inout LightingData lightingData)
 {
-    LightingData lightingData;
-    InitializeLightingData_StandardPBR(surface, screenPosition, viewPosition, lightingData);
-    CalculateLighting_BasePBR(surface, screenPosition, lightingData);
-    return lightingData;
+    FinalizeLightingData_BasePBR(surface, lightingData);
 }
+

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceData.azsli

@@ -46,6 +46,8 @@
     class SurfaceData_StandardPBR
     {
 
+        uint lightingChannels;     //!< Lightingchannel of the object
+
     #if ENABLE_CLEAR_COAT
         ClearCoatSurfaceData clearCoat;
     #endif

+ 21 - 13
Gems/Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_SurfaceEval.azsli

@@ -30,9 +30,11 @@ Surface EvaluateSurface_StandardPBR(
     bool isDisplacementClipped,
     float4 uvDxDy,
     bool customDerivatives,
-    float4 vertexColor)
+    float4 vertexColor,
+    uint lightingChannels
+    )
 {
-    Surface surface = EvaluateSurface_BasePBR(params, positionWS, vertexNormal, tangents, bitangents, uvs, isFrontFace, uvDxDy, customDerivatives, vertexColor);
+    Surface surface = EvaluateSurface_BasePBR(params, positionWS, vertexNormal, tangents, bitangents, uvs, isFrontFace, uvDxDy, customDerivatives, vertexColor, lightingChannels);
 
     // ------- Parallax Clipping -------
 
@@ -95,7 +97,8 @@ Surface EvaluateSurface_StandardPBR(
     bool isFrontFace,
     bool isDisplacementClipped,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     return EvaluateSurface_StandardPBR(
         params,
@@ -108,7 +111,8 @@ Surface EvaluateSurface_StandardPBR(
         isDisplacementClipped,
         uvDxDy,
         customDerivatives,
-        float4(1.0f, 1.0f, 1.0f, 1.0f));
+        float4(1.0f, 1.0f, 1.0f, 1.0f) /* VertexColor */,
+        lightingChannels);
 }
 
 Surface EvaluateSurface_StandardPBR(
@@ -119,7 +123,8 @@ Surface EvaluateSurface_StandardPBR(
     float3 bitangents[UvSetCount],
     float2 uvs[UvSetCount],
     bool isFrontFace,
-    bool isDisplacementClipped)
+    bool isDisplacementClipped,
+    uint lightingChannels)
 {
     return EvaluateSurface_StandardPBR(
         params,
@@ -130,11 +135,12 @@ Surface EvaluateSurface_StandardPBR(
         uvs,
         isFrontFace,
         isDisplacementClipped,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false, /* customDerivatives */
+        lightingChannels);
 }
 
-Surface EvaluateSurface_StandardPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_StandardPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_StandardPBR(
         params,
@@ -147,10 +153,11 @@ Surface EvaluateSurface_StandardPBR(VsOutput IN, PixelGeometryData geoData, cons
         geoData.isDisplacementClipped,
         uvDxDy,
         customDerivatives,
-        GetVertexColor(IN));
+        GetVertexColor(IN),
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_StandardPBR(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_StandardPBR(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_StandardPBR(
         params,
@@ -161,7 +168,8 @@ Surface EvaluateSurface_StandardPBR(VsOutput IN, PixelGeometryData geoData, cons
         geoData.uvs,
         geoData.isFrontFace,
         geoData.isDisplacementClipped,
-        float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false,
-        GetVertexColor(IN));
+        float4(0.0f, 0.0f, 0.0f, 0.0f), /* uvDxDy */
+        false,  /* customDerivatives */
+        GetVertexColor(IN),
+        GetLightingChannelMask(SV));
 }

+ 2 - 0
Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/DepthToLinearDepth.azsl

@@ -5,6 +5,8 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+#include <Atom/Features/PBR/Lights/LightStructures.azsli>
+#include <Atom/Features/Decals/DecalStructures.azsli>
 
 #include <viewsrg_all.srgi>
 

+ 13 - 2
Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake

@@ -308,7 +308,6 @@ set(FILES
     ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli
     ShaderLib/Atom/Features/PBR/Hammersley.azsli
     ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli
-    ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli
     ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli
     ShaderLib/Atom/Features/PBR/LightingOptions.azsli
     ShaderLib/Atom/Features/PBR/LightingUtils.azsli
@@ -316,7 +315,6 @@ set(FILES
     ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli
     ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli
     ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli
-    ShaderLib/Atom/Features/PBR/Lights/IblForward.azsli
     ShaderLib/Atom/Features/PBR/Lights/Lights.azsli
     ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli
     ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli
@@ -336,8 +334,21 @@ set(FILES
     ShaderLib/Atom/Features/PBR/Surfaces/DualSpecularSurface.azsli
     ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli
     ShaderLib/Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassCapsuleLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDecals.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDirectionalLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassDiskLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassIbl.azsli
     ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassOutput.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPipelineCallbacks.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPointLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassPolygonLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassQuadLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimplePointLights.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSimpleSpotLights.azsli
     ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassSrg.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/ForwardPassVertexData.azsli
+    ShaderLib/Atom/Features/Pipeline/Forward/Readme.md
     ShaderLib/Atom/Features/PostProcessing/Aces.azsli
     ShaderLib/Atom/Features/PostProcessing/AcesColorSpaceConversion.azsli
     ShaderLib/Atom/Features/PostProcessing/FullscreenPixelInfo.azsli

+ 1 - 1
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/PersistentIndexAllocator.h

@@ -65,7 +65,7 @@ namespace AZ::RPI
             }
         }
 
-        T Max() const
+        T MaxCount() const
         {
             return m_max;
         }

+ 0 - 9
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h

@@ -161,15 +161,6 @@ namespace AZ
 
 namespace AZStd
 {
-    template<>
-    struct hash<AZ::RPI::ShaderVariantId>
-    {
-        size_t operator()(const AZ::RPI::ShaderVariantId& variantId) const
-        {
-            return AZStd::hash_range(variantId.m_key.data(), variantId.m_key.data() + variantId.m_key.num_words());
-        }
-    };
-
     template<>
     struct hash<AZ::RPI::ShaderVariantAsyncLoader::TupleShaderAssetAndShaderVariantId>
     {

+ 12 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h

@@ -122,3 +122,15 @@ namespace AZ
         };
     } // namespace RPI
 } // namespace AZ
+
+namespace AZStd
+{
+    template<>
+    struct hash<AZ::RPI::ShaderVariantId>
+    {
+        size_t operator()(const AZ::RPI::ShaderVariantId& variantId) const
+        {
+            return AZStd::hash_range(variantId.m_key.data(), variantId.m_key.data() + variantId.m_key.num_words());
+        }
+    };
+} // namespace AZStd

+ 8 - 8
Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp

@@ -208,7 +208,7 @@ namespace AZ::RPI
         if (materialTypeAssetIterator == m_materialTypeIndicesMap.end())
         {
             materialTypeIndex = m_materialTypeIndices.Aquire();
-            m_materialTypeData.resize(m_materialTypeIndices.Max());
+            m_materialTypeData.resize(m_materialTypeIndices.MaxCount());
             m_materialTypeIndicesMap.insert(AZStd::make_pair(materialTypeAsset.GetId(), materialTypeIndex));
             MaterialTypeData& materialTypeData = m_materialTypeData[materialTypeIndex];
 
@@ -236,7 +236,7 @@ namespace AZ::RPI
         MaterialTypeData& materialTypeData = m_materialTypeData[materialTypeIndex];
 
         auto materialInstanceIndex = materialTypeData.m_instanceIndices.Aquire();
-        materialTypeData.m_instanceData.resize(materialTypeData.m_instanceIndices.Max());
+        materialTypeData.m_instanceData.resize(materialTypeData.m_instanceIndices.MaxCount());
         auto& instanceData = materialTypeData.m_instanceData[materialInstanceIndex];
 
         instanceData.m_material = material.get();
@@ -307,7 +307,7 @@ namespace AZ::RPI
             materialTypeData.m_materialTypeAssetHint.c_str(),
             materialInstanceIndex,
             instanceData.m_material->GetAsset().GetHint().c_str(),
-            materialTypeData.m_instanceIndices.Max());
+            materialTypeData.m_instanceIndices.MaxCount());
 #endif
 
         return result;
@@ -328,7 +328,7 @@ namespace AZ::RPI
             materialTypeData->m_materialTypeAssetHint.c_str(),
             materialInstance.m_materialInstanceId,
             materialInstanceData->m_material->GetAsset().GetHint().c_str(),
-            materialTypeData->m_instanceIndices.Max());
+            materialTypeData->m_instanceIndices.MaxCount());
 #endif
 
         materialTypeData->m_instanceData[materialInstance.m_materialInstanceId] = {};
@@ -443,7 +443,7 @@ namespace AZ::RPI
             {
                 AZ_Assert(shaderParamsSize > 0, "MaterialSystem: Material uses SceneMaterialSrg, but has no Shader Parameters");
             }
-            for (int32_t instanceIndex = 0; instanceIndex < materialTypeEntry.m_instanceIndices.Max(); instanceIndex++)
+            for (int32_t instanceIndex = 0; instanceIndex < materialTypeEntry.m_instanceIndices.MaxCount(); instanceIndex++)
             {
                 auto& instanceData = materialTypeEntry.m_instanceData[instanceIndex];
                 if (instanceData.m_material && instanceData.m_material->GetCurrentChangeId() != instanceData.m_compiledChangeId)
@@ -533,7 +533,7 @@ namespace AZ::RPI
                     break;
                 }
             }
-            auto bufferSize = bufferEntrySize * materialTypeEntry.m_instanceIndices.Max();
+            auto bufferSize = bufferEntrySize * materialTypeEntry.m_instanceIndices.MaxCount();
 
             // create or resize the MaterialParameter-buffer for this material-type
             if (materialTypeEntry.m_parameterBuffer == nullptr)
@@ -594,7 +594,7 @@ namespace AZ::RPI
             AZStd::unordered_map<int, AZStd::vector<int32_t>> deviceBufferData;
             for (auto deviceIndex{ 0 }; deviceIndex < deviceCount; ++deviceIndex)
             {
-                deviceBufferData[deviceIndex].resize(m_materialTypeIndices.Max(), -1);
+                deviceBufferData[deviceIndex].resize(m_materialTypeIndices.MaxCount(), -1);
             }
 
             // collect the per-device read indices of the material parameter buffers
@@ -612,7 +612,7 @@ namespace AZ::RPI
             }
 
             // prepare / resize the GPU buffer
-            auto indicesBufferSize = static_cast<uint64_t>(sizeof(int32_t) * m_materialTypeIndices.Max());
+            auto indicesBufferSize = static_cast<uint64_t>(sizeof(int32_t) * m_materialTypeIndices.MaxCount());
             if (!m_materialTypeBufferIndicesBuffer)
             {
                 m_materialTypeBufferIndicesBuffer = createBuffer(m_materialTypeData.size());

+ 4 - 0
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp

@@ -62,6 +62,10 @@ namespace AZ
                 if (currentSrgLayout->GetHash() != newSrgLayout->GetHash())
                 {
                     ReportError("All shaders in a material must use the same %s ShaderResourceGroup.", srgDebugName);
+                    ReportError(
+                        "SRG layout from %s clashes with layout from %s.",
+                        srgShaderAssetToUpdate.GetHint().c_str(),
+                        newShaderAsset.GetHint().c_str());
                     return false;
                 }
             }

+ 2 - 2
Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp

@@ -2079,9 +2079,9 @@ namespace UnitTest
 
         AZ_TEST_START_ASSERTTEST;
         creator.AddShader(shaderB);
-        AZ_TEST_STOP_ASSERTTEST(1);
+        AZ_TEST_STOP_ASSERTTEST(2);
 
-        EXPECT_EQ(1, creator.GetErrorCount());
+        EXPECT_EQ(2, creator.GetErrorCount());
     }
 
     TEST_F(MaterialTypeAssetTests, Error_ShaderInputNotFound)

+ 4 - 0
Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/BasePBR/MaterialGraphName_Common.azsli

@@ -31,6 +31,10 @@ bool NeedsTangentFrame()
 
 #include "MaterialGraphName_SurfaceEval.azsli"
 
+#include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+#include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingBrdf.azsli>
+#include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingEval.azsli>
+
 #else
 
 #include "MaterialGraphName_DepthVertexEval.azsli"

+ 13 - 7
Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/BasePBR/MaterialGraphName_SurfaceEval.azsli

@@ -28,7 +28,8 @@ Surface EvaluateSurface_MaterialGraphName(
     float2 uvs[UvSetCount],
     bool isFrontFace,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     const float3 bitangent = cross((float3)IN.normal, (float3)IN.tangent);
     const float3 O3DE_MC_POSITION = (float3)IN.position;
@@ -60,6 +61,7 @@ Surface EvaluateSurface_MaterialGraphName(
     #undef O3DE_MC_UV
 
     Surface surface;
+    surface.lightingChannels = lightingChannels;
     surface.position = positionWS;
     surface.vertexNormal = normalWS;
     surface.normal = normalize(o_normal_override_enabled ? GetWorldSpaceNormal((real2)inNormal, normalWS, tangents[0], bitangents[0]) : normalWS);
@@ -81,7 +83,8 @@ Surface EvaluateSurface_MaterialGraphName(
     float3 tangents[UvSetCount],
     float3 bitangents[UvSetCount],
     float2 uvs[UvSetCount],
-    bool isFrontFace)
+    bool isFrontFace,
+    uint lightingChannels)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -93,10 +96,11 @@ Surface EvaluateSurface_MaterialGraphName(
         uvs,
         isFrontFace,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        lightingChannels);
 }
 
-Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_MaterialGraphName(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -108,10 +112,11 @@ Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData
         geoData.uvs,
         geoData.isFrontFace,
         uvDxDy,
-        customDerivatives);
+        customDerivatives,
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_MaterialGraphName(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -123,5 +128,6 @@ Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData
         geoData.uvs,
         geoData.isFrontFace,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        GetLightingChannelMask(SV));
 }

+ 5 - 1
Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/StandardPBR/MaterialGraphName_Common.azsli

@@ -79,12 +79,16 @@ bool NeedsTangentFrame()
 #include "MaterialGraphName_PixelGeometryEval.azsli"
 #include "MaterialGraphName_SurfaceEval.azsli"
 
+#include <Atom/Feature/Common/Assets/Shaders/Materials/BasePBR/BasePBR_LightingData.azsli>
+#include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingBrdf.azsli>
+#include <Atom/Feature/Common/Assets/Shaders/Materials/StandardPBR/StandardPBR_LightingEval.azsli>
+
 #elif MATERIALPIPELINE_SHADER_HAS_GEOMETRIC_PIXEL_STAGE
 
 #include "MaterialGraphName_VertexEval.azsli"
 #include "MaterialGraphName_PixelGeometryEval.azsli"
     
-void EvaluateSurface(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+void EvaluateSurface(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     const float3 bitangent = cross((float3)IN.normal, (float3)IN.tangent);
     const float3 O3DE_MC_POSITION = (float3)IN.position;

+ 13 - 7
Gems/Atom/Tools/MaterialCanvas/Assets/MaterialCanvas/GraphData/Nodes/MaterialOutputs/StandardPBR/MaterialGraphName_SurfaceEval.azsli

@@ -30,7 +30,8 @@ Surface EvaluateSurface_MaterialGraphName(
     bool isFrontFace,
     bool isDisplacementClipped,
     float4 uvDxDy,
-    bool customDerivatives)
+    bool customDerivatives,
+    uint lightingChannels)
 {
     const float3 bitangent = cross((float3)IN.normal, (float3)IN.tangent);
     const float3 O3DE_MC_POSITION = (float3)IN.position;
@@ -72,6 +73,7 @@ Surface EvaluateSurface_MaterialGraphName(
     CheckClipping((real)inAlpha, (real)inOpacityFactor);
 
     Surface surface;
+    surface.lightingChannels = lightingChannels;
     surface.position = positionWS;
     surface.vertexNormal = normalWS;
     surface.normal = normalize(o_normal_override_enabled ? GetWorldSpaceNormal((real2)inNormal, normalWS, tangents[0], bitangents[0]) : normalWS);
@@ -125,7 +127,8 @@ Surface EvaluateSurface_MaterialGraphName(
     float3 bitangents[UvSetCount],
     float2 uvs[UvSetCount],
     bool isFrontFace,
-    bool isDisplacementClipped)
+    bool isDisplacementClipped,
+    uint lightingChannels)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -138,11 +141,12 @@ Surface EvaluateSurface_MaterialGraphName(
         isFrontFace,
         isDisplacementClipped,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false); 
+        false,
+        lightingChannels); 
 }
 
 
-Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
+Surface EvaluateSurface_MaterialGraphName(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params, float4 uvDxDy, bool customDerivatives)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -155,10 +159,11 @@ Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData
         geoData.isFrontFace,
         geoData.isDisplacementClipped,
         uvDxDy,
-        customDerivatives);
+        customDerivatives,
+        GetLightingChannelMask(SV));
 }
 
-Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData, const MaterialParameters params)
+Surface EvaluateSurface_MaterialGraphName(VsOutput IN, VsSystemValues SV, PixelGeometryData geoData, const MaterialParameters params)
 {
     return EvaluateSurface_MaterialGraphName(
         IN,
@@ -171,5 +176,6 @@ Surface EvaluateSurface_MaterialGraphName(VsOutput IN, PixelGeometryData geoData
         geoData.isFrontFace,
         geoData.isDisplacementClipped,
         float4(0.0f, 0.0f, 0.0f, 0.0f),
-        false);
+        false,
+        GetLightingChannelMask(SV));
 }

+ 4 - 0
Gems/AtomContent/TestData/Assets/TestData/Materials/BasePbrTestCases/001_DefaultWhite.material

@@ -0,0 +1,4 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/BasePBR.materialtype",
+    "materialTypeVersion": 5
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio