Ver código fonte

Add new AcesFitted, AcesFilmic and Filmic tonemapping (#17977) (#17999)

* Add AcesFitted, AcesFilmic and Filmic tonemappers
* Add DisplayMapper pass to Mobile pipeline
* Add manual exposure and LDR color grading merging to OutputTransform pass
* Add shader override for OutputTransform pass
* Add shader variants for output transform mobile

Signed-off-by: Akio Gaule <[email protected]>
Akio Gaule 1 ano atrás
pai
commit
28c0672e91
26 arquivos alterados com 464 adições e 127 exclusões
  1. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_BaseLighting.azsli
  2. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting.azsli
  3. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/ForwardPass_StandardLighting_CustomZ.azsli
  4. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/MobileForwardPassVertexAndPixel.azsli
  5. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Pipelines/Mobile/Transparent_StandardLighting.azsli
  6. 10 3
      Gems/Atom/Feature/Common/Assets/Passes/Mobile/Pipeline.pass
  7. 50 0
      Gems/Atom/Feature/Common/Assets/Passes/Mobile/PostProcessParent.pass
  8. 26 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Shapers.azsli
  9. 36 21
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Tonemap.azsli
  10. 1 15
      Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl
  11. 38 12
      Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.azsl
  12. 23 0
      Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransformMobile.shader
  13. 14 0
      Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransformMobile.shadervariantlist
  14. 8 2
      Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h
  15. 3 2
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h
  16. 4 0
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h
  17. 3 1
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h
  18. 2 1
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h
  19. 9 1
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h
  20. 8 2
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h
  21. 7 3
      Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp
  22. 17 4
      Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp
  23. 2 0
      Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp
  24. 116 43
      Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp
  25. 75 12
      Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp
  26. 7 0
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp

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

@@ -26,7 +26,7 @@
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_PHYSICAL_SKY 0
 #define ENABLE_PHYSICAL_SKY 0
-#define ENABLE_MERGE_FILMIC_TONEMAP 1
+#define ENABLE_MERGE_FILMIC_TONEMAP 0
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5

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

@@ -27,7 +27,7 @@
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_PHYSICAL_SKY 0
 #define ENABLE_PHYSICAL_SKY 0
-#define ENABLE_MERGE_FILMIC_TONEMAP 1
+#define ENABLE_MERGE_FILMIC_TONEMAP 0
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5

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

@@ -27,7 +27,7 @@
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_AREA_LIGHT_VALIDATION    0
 #define ENABLE_PHYSICAL_SKY 0
 #define ENABLE_PHYSICAL_SKY 0
-#define ENABLE_MERGE_FILMIC_TONEMAP 1
+#define ENABLE_MERGE_FILMIC_TONEMAP 0
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5

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

@@ -50,7 +50,7 @@ ForwardPassOutput PixelShader(VsOutput IN, bool isFrontFace : SV_IsFrontFace)
     color = AcesCg_To_LinearSrgb(color);
     color = AcesCg_To_LinearSrgb(color);
 
 
     // We could add other forms of tonemapping in future via shader options.
     // We could add other forms of tonemapping in future via shader options.
-    color = ApplyFilmicTonemap(color);
+    color = TonemapFilmic(color);
 #endif
 #endif
 
 
     OUT.m_color.rgb = color;
     OUT.m_color.rgb = color;

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

@@ -25,7 +25,7 @@
 #define ENABLE_LIGHT_CULLING 0
 #define ENABLE_LIGHT_CULLING 0
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_AREA_LIGHTS 0
 #define ENABLE_PHYSICAL_SKY 0
 #define ENABLE_PHYSICAL_SKY 0
-#define ENABLE_MERGE_FILMIC_TONEMAP 1
+#define ENABLE_MERGE_FILMIC_TONEMAP 0
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_DECALS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_SPOTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5
 #define ENABLE_SIMPLE_POINTLIGHTS_CAP 5

+ 10 - 3
Gems/Atom/Feature/Common/Assets/Passes/Mobile/Pipeline.pass

@@ -207,9 +207,16 @@
                                 "Pass": "ForwardPass",
                                 "Pass": "ForwardPass",
                                 "Attachment": "DepthStencilOutput"
                                 "Attachment": "DepthStencilOutput"
                             }
                             }
+                        },
+                        {
+                            "LocalSlot": "PipelineOutput",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "PipelineOutput"
+                            }
                         }
                         }
                     ]
                     ]
-                },
+                },               
                 {
                 {
                     "Name": "AuxGeomPass",
                     "Name": "AuxGeomPass",
                     "TemplateName": "AuxGeomPassTemplate",
                     "TemplateName": "AuxGeomPassTemplate",
@@ -218,8 +225,8 @@
                         {
                         {
                             "LocalSlot": "ColorInputOutput",
                             "LocalSlot": "ColorInputOutput",
                             "AttachmentRef": {
                             "AttachmentRef": {
-                                "Pass": "MobileTransparentPass",
-                                "Attachment": "InputOutput"
+                                "Pass": "MobilePostProcessPass",
+                                "Attachment": "Output"
                             }
                             }
                         },
                         },
                         {
                         {

+ 50 - 0
Gems/Atom/Feature/Common/Assets/Passes/Mobile/PostProcessParent.pass

@@ -15,6 +15,25 @@
                 {
                 {
                     "Name": "Depth",
                     "Name": "Depth",
                     "SlotType": "Input"
                     "SlotType": "Input"
+                },
+                // SwapChain here is only used to reference the frame height and format
+                {
+                    "Name": "PipelineOutput",
+                    "SlotType": "InputOutput"
+                },
+                // Outputs...
+                {
+                    "Name": "Output",
+                    "SlotType": "Output"
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "Output",
+                    "AttachmentRef": {
+                        "Pass": "DisplayMapperPass",
+                        "Attachment": "Output"
+                    }
                 }
                 }
             ],
             ],
             "PassRequests": [
             "PassRequests": [
@@ -52,6 +71,37 @@
                             }
                             }
                         }
                         }
                     ]
                     ]
+                },
+                {
+                    "Name": "DisplayMapperPass",
+                    "TemplateName": "DisplayMapperTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "BloomPass",
+                                "Attachment": "InputOutput"
+                            }
+                        },
+                        {
+                            "LocalSlot": "PipelineOutput",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "PipelineOutput"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "DisplayMapperPassData",
+                        "MergeLdrGradingLut": true,
+                        "DisplayMapperConfig": {
+                            "OperationType": "AcesFitted"
+                        },
+                         "OutputTransformShaderOverride": {
+                            "FilePath": "Shaders/PostProcessing/OutputTransformMobile.shader"
+                        }
+                    }
                 }
                 }
             ]
             ]
         }
         }

+ 26 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Shapers.azsli

@@ -50,3 +50,29 @@ float3 LinearToShaper(float3 linearColor, ShaperType shaperType, float shaperBia
     }
     }
     return linearColor;
     return linearColor;
 }
 }
+
+
+float3 ApplyShaperLookupTable(
+    ShaperType shaperType,
+    float3 color,
+    float shaperBias,
+    float shaperScale,
+    Texture3D<float4> lutTex,
+    sampler linearSampler)
+{
+    // Convert from working color space to lut coordinates by applying the shaper function
+    float3 lutCoordinate = LinearToShaper(color, shaperType, shaperBias, shaperScale);
+
+    // Adjust coordinate to the domain excluding the outer half texel in all directions
+    uint3 outputDimensions;
+    lutTex.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z);
+    float3 coordBias = 1.0 / (2.0 * outputDimensions);
+    float3 coordScale = (outputDimensions - 1.0) / outputDimensions;
+    lutCoordinate = (lutCoordinate * coordScale) + coordBias;
+
+    float3 lutColor = lutTex.Sample(linearSampler, lutCoordinate).rgb;
+
+    // Apply the inverse of the shaper function to give the color in the working color space
+    float3 finalColor = ShaperToLinear(lutColor, shaperType, shaperBias, shaperScale);
+    return finalColor;
+}

+ 36 - 21
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Tonemap.azsli

@@ -8,35 +8,50 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <Atom/Features/ColorManagement/TransformColor.azsli>
 
 
-
-real3 ApplyManualExposure(real3 color, real exposure)
+// Simple reinhard tone mapping algorithm based on below paper.
+// http://www.cmap.polytechnique.fr/~peyre/cours/x2005signal/hdr_photographic.pdf
+float3 TonemapReinhard(const float3 inputColor)
 {
 {
-    // Apply Manual exposure
-    return color * pow(2.0, exposure);
+    return inputColor / (1.0 + inputColor);
 }
 }
 
 
-real3 TonemapFilmic(real3 x) {
-    // See: https://github.com/dmnsgn/glsl-tone-map/blob/main/filmic.glsl
-    real3 X = max(real3(0.0, 0.0, 0.0), x - 0.004);
-    real3 result = (X * (6.2 * X + 0.5)) / (X * (6.2 * X + 1.7) + 0.06);
-    return pow(result, real3(2.2, 2.2, 2.2));
+// Originally written by Stephen Hill (@self_shadow)
+// Input and output color are in AcesCG space
+float3 TonemapAcesFitted(const float3 inputColor)
+{
+    // Apply RRT and ODT
+    float a = 0.0245786f;
+    float b = 0.000090537f;
+    float c = 0.983729f;
+    float d = 0.4329510f;
+    float e = 0.238081f;
+    return saturate((inputColor * (inputColor + a) - b) / (inputColor * (c * inputColor + d) + e));
 }
 }
 
 
-real3 TonemapAces(real3 x) {
-    // See: https://github.com/dmnsgn/glsl-tone-map/blob/main/aces.glsl
-    // Note: On Quest, this function makes skin look overly red
-    const real a = 2.51;
-    const real b = 0.03;
-    const real c = 2.43;
-    const real d = 0.59;
-    const real e = 0.14;
-    return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
+// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
+float3 TonemapAcesFilmic(const float3 inputColor)
+{
+    // Apply filmic curve. 
+    float a = 2.51f;
+    float b = 0.03f;
+    float c = 2.43f;
+    float d = 0.59f;
+    float e = 0.14f;
+    return saturate((inputColor * (a * inputColor + b)) / (inputColor * (c * inputColor + d) + e));
 }
 }
 
 
-// NOTE: DEPRECATED! PLEASE USE ONE OF THE ABOVE FUNCTIONS!
-real3 ApplyFilmicTonemap(real3 color)
+// Applies the filmic curve from John Hable
+float3 TonemapFilmic(const float3 inputColor)
 {
 {
-    return TonemapAces(color);
+    // See: https://github.com/dmnsgn/glsl-tone-map/blob/main/filmic.glsl
+    float3 color = max(float3(0.0, 0.0, 0.0), inputColor - 0.004);
+    return (color * (6.2 * color + 0.5)) / (color * (6.2 * color + 1.7) + 0.06);
 }
 }
 
 
+float3 ApplyManualExposure(float3 color, float exposure)
+{
+    // Apply Manual exposure
+    return color * pow(2.0, exposure);
+}

+ 1 - 15
Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl

@@ -41,21 +41,7 @@ PSOutput MainPS(VSOutput IN)
 
 
     ShaperType shaperType = (ShaperType)PassSrg::m_shaperType;
     ShaperType shaperType = (ShaperType)PassSrg::m_shaperType;
 
 
-    // Convert from working color space to lut coordinates by applying the shaper function
-    float3 lutCoordinate = LinearToShaper(color, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale);
-
-    // Adjust coordinate to the domain excluding the outer half texel in all directions
-    uint3 outputDimensions;
-    PassSrg::m_lut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z);
-    float3 coordBias = 1.0 / (2.0 * outputDimensions);
-    float3 coordScale = (outputDimensions - 1.0) / outputDimensions;
-    lutCoordinate = (lutCoordinate * coordScale) + coordBias;
-
-    float3 lutColor = PassSrg::m_lut.Sample(PassSrg::LinearSampler, lutCoordinate).rgb;
-
-    // Apply the inverse of the shaper function to give the color in the working color space
-    float3 finalColor = ShaperToLinear(lutColor, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale);
-
+    float3 finalColor = ApplyShaperLookupTable(shaperType, color, PassSrg::m_shaperBias, PassSrg::m_shaperScale, PassSrg::m_lut, PassSrg::LinearSampler);
     OUT.m_color.rgb = finalColor;
     OUT.m_color.rgb = finalColor;
     OUT.m_color.a = 1.0;
     OUT.m_color.a = 1.0;
 
 

+ 38 - 12
Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransform.azsl

@@ -12,10 +12,14 @@
 #include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
 #include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
 #include <Atom/Features/PostProcessing/PostProcessUtil.azsli>
 #include <Atom/Features/PostProcessing/PostProcessUtil.azsli>
 #include <Atom/Features/PostProcessing/Aces.azsli>
 #include <Atom/Features/PostProcessing/Aces.azsli>
+#include <Atom/Features/PostProcessing/Tonemap.azsli>
+#include <Atom/Features/PostProcessing/Shapers.azsli>
+#include <Atom/Features/ColorManagement/TransformColor.azsli>
 
 
 ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
 ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
 {
 {
     Texture2D<float4> m_framebuffer;
     Texture2D<float4> m_framebuffer;
+    Texture3D<float4> m_lut;
     Sampler LinearSampler
     Sampler LinearSampler
     {
     {
         MinFilter = Linear;
         MinFilter = Linear;
@@ -28,19 +32,19 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
 
 
     // Reference white and black luminance values
     // Reference white and black luminance values
     float2 m_cinemaLimits;
     float2 m_cinemaLimits;
+
+    // LDR color grading
+    int m_shaperType;
+    float m_shaperBias;
+    float m_shaperScale;
 }
 }
 
 
 // Option shader variable to select tone mapper feature.
 // Option shader variable to select tone mapper feature.
-option enum class ToneMapperType {None, Reinhard} o_tonemapperType = ToneMapperType::None;
+option enum class ToneMapperType {None, Reinhard, AcesFitted, AcesFilmic, Filmic} o_tonemapperType = ToneMapperType::None;
 // Option shader variable to select transfer function.
 // Option shader variable to select transfer function.
 option enum class TransferFunctionType {None, Gamma22, PerceptualQuantizer} o_transferFunctionType = TransferFunctionType::None;
 option enum class TransferFunctionType {None, Gamma22, PerceptualQuantizer} o_transferFunctionType = TransferFunctionType::None;
-
-// Simple reinhard tone mapping algorithm based on below paper.
-// http://www.cmap.polytechnique.fr/~peyre/cours/x2005signal/hdr_photographic.pdf
-float3 TonemapReinhard(const float3 inputColor)
-{
-    return inputColor / (1.0 + inputColor);
-}
+// Option to enable color grading
+option bool o_colorGradingEnabled = false;
 
 
 float3 NormalizedToCinemaLimits(const float3 inputColor)
 float3 NormalizedToCinemaLimits(const float3 inputColor)
 {
 {
@@ -61,24 +65,41 @@ PSOutput MainPS(VSOutput IN)
 
 
     // Fetch the pixel color from the input texture
     // Fetch the pixel color from the input texture
     float3 color = PassSrg::m_framebuffer.Sample(PassSrg::LinearSampler, IN.m_texCoord).rgb;
     float3 color = PassSrg::m_framebuffer.Sample(PassSrg::LinearSampler, IN.m_texCoord).rgb;
-    
+
+#if MERGE_MANUAL_EXPOSURE
+    // Apply manual exposure compensation
+    color = ApplyManualExposure(color, float(ViewSrg::GetExposureValueCompensation()));
+#endif
+
     // Applying tone mapper.
     // Applying tone mapper.
     switch (o_tonemapperType)
     switch (o_tonemapperType)
     {
     {
         case ToneMapperType::Reinhard:
         case ToneMapperType::Reinhard:
-            color = TonemapReinhard(color);
+            color = TonemapReinhard(AcesCg_To_LinearSrgb(color));
+            break;
+        case ToneMapperType::AcesFitted:
+            color = AcesCg_To_LinearSrgb(TonemapAcesFitted(color));
+            break;
+        case ToneMapperType::AcesFilmic:
+            color = TonemapAcesFilmic(AcesCg_To_LinearSrgb(color));
+            break;
+        case ToneMapperType::Filmic:
+            color = TonemapFilmic(AcesCg_To_LinearSrgb(color));
             break;
             break;
         default:
         default:
             break;
             break;
-    }
+    } 
 
 
+    // Aplying transfer function to go from linear srgb to gamma srgb 
     switch (o_transferFunctionType)
     switch (o_transferFunctionType)
     {
     {
         case TransferFunctionType::Gamma22:
         case TransferFunctionType::Gamma22:
             color = pow(color, 1.0 / 2.2);
             color = pow(color, 1.0 / 2.2);
             break;
             break;
         case TransferFunctionType::PerceptualQuantizer:
         case TransferFunctionType::PerceptualQuantizer:
-            if (o_tonemapperType == ToneMapperType::Reinhard)
+            if (o_tonemapperType == ToneMapperType::Reinhard ||
+                o_tonemapperType == ToneMapperType::AcesFitted ||
+                o_tonemapperType == ToneMapperType::AcesFilmic)
             {
             {
                 color = NormalizedToCinemaLimits(color);
                 color = NormalizedToCinemaLimits(color);
             }
             }
@@ -89,6 +110,11 @@ PSOutput MainPS(VSOutput IN)
             break;
             break;
     }
     }
 
 
+    if(o_colorGradingEnabled)
+    {
+        color.rgb = ApplyShaperLookupTable(ShaperType::ShaperLinear, color, PassSrg::m_shaperBias, PassSrg::m_shaperScale, PassSrg::m_lut, PassSrg::LinearSampler);
+    }
+
     OUT.m_color.rgb = color;
     OUT.m_color.rgb = color;
     OUT.m_color.w = 1;
     OUT.m_color.w = 1;
 
 

+ 23 - 0
Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransformMobile.shader

@@ -0,0 +1,23 @@
+{ 
+    "Source" : "OutputTransform.azsl",
+    "Definitions": ["MERGE_MANUAL_EXPOSURE=1"],
+
+    "DepthStencilState" : {
+        "Depth" : { "Enable" : false }
+    },
+
+    "ProgramSettings":
+    {
+      "EntryPoints":
+      [
+        {
+          "name": "MainVS",
+          "type": "Vertex"
+        },
+        {
+          "name": "MainPS",
+          "type": "Fragment"
+        }
+      ]
+    }  
+}

+ 14 - 0
Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/OutputTransformMobile.shadervariantlist

@@ -0,0 +1,14 @@
+//Step 1 -> Use CreateShaderVariantListDocumentFromShader via SMC on OutputTransformMobile shader to get the shader options.
+//Step 2 -> Use ExpandOptionsFullCombinatorials via SMC and apply o_tonemapperType with value ToneMapperType::AcesFitted, ToneMapperType::AcesFilmic. o_transferFunctionType with TransferFunctionType::Gamma22. o_colorGradingEnabled with true and false. Generation Method 'Mulitply'.
+//Step 3 -> Use ExpandOptionsFullCombinatorials via SMC and apply o_tonemapperType with value ToneMapperType::Filmic. o_transferFunctionType with TransferFunctionType::None. o_colorGradingEnabled with true and false. Generation Method 'Mulitply'.
+{ 
+    "Shader" : "OutputTransformMobile.shader",
+	"Variants" : [ 
+        {"StableId": 1, "Options" : { "o_tonemapperType": "ToneMapperType::AcesFitted", "o_transferFunctionType": "TransferFunctionType::Gamma22", "o_colorGradingEnabled": "true" } },
+        {   "StableId": 2, "Options" : { "o_tonemapperType": "ToneMapperType::AcesFitted", "o_transferFunctionType": "TransferFunctionType::Gamma22", "o_colorGradingEnabled": "false" } },
+        {   "StableId": 3, "Options" : { "o_tonemapperType": "ToneMapperType::AcesFilmic", "o_transferFunctionType": "TransferFunctionType::Gamma22", "o_colorGradingEnabled": "true" } },
+        {   "StableId": 4, "Options" : { "o_tonemapperType": "ToneMapperType::AcesFilmic", "o_transferFunctionType": "TransferFunctionType::Gamma22", "o_colorGradingEnabled": "false" } },
+        {   "StableId": 5, "Options" : { "o_tonemapperType": "ToneMapperType::Filmic", "o_transferFunctionType": "TransferFunctionType::None", "o_colorGradingEnabled": "true" } },
+        {   "StableId": 6, "Options" : { "o_tonemapperType": "ToneMapperType::Filmic", "o_transferFunctionType": "TransferFunctionType::None", "o_colorGradingEnabled": "false" } }
+    ]
+}

+ 8 - 2
Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h

@@ -145,7 +145,10 @@ namespace AZ
             AcesLut,
             AcesLut,
             Passthrough,
             Passthrough,
             GammaSRGB,
             GammaSRGB,
-            Reinhard
+            Reinhard,
+            AcesFitted,
+            AcesFilmic,
+            Filmic
         );
         );
 
 
         enum class ShaperPresetType
         enum class ShaperPresetType
@@ -164,7 +167,10 @@ namespace AZ
         enum class ToneMapperType
         enum class ToneMapperType
         {
         {
             None = 0,
             None = 0,
-            Reinhard
+            Reinhard,
+            AcesFitted,
+            AcesFilmic,
+            Filmic
         };
         };
 
 
         enum class TransferFunctionType
         enum class TransferFunctionType

+ 3 - 2
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h

@@ -88,7 +88,8 @@ namespace AZ
             void GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) override;
             void GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) override;
             void GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId) override;
             void GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId) override;
             void RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) override;
             void RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) override;
-            DisplayMapperConfigurationDescriptor GetDisplayMapperConfiguration() override;
+            void UnregisterDisplayMapperConfiguration() override;
+            const DisplayMapperConfigurationDescriptor* GetDisplayMapperConfiguration() override;
 
 
         private:
         private:
             AcesDisplayMapperFeatureProcessor(const AcesDisplayMapperFeatureProcessor&) = delete;
             AcesDisplayMapperFeatureProcessor(const AcesDisplayMapperFeatureProcessor&) = delete;
@@ -111,7 +112,7 @@ namespace AZ
             // LUTs loaded from assets
             // LUTs loaded from assets
             AZStd::unordered_map<AZStd::string, DisplayMapperAssetLut>  m_assetLuts;
             AZStd::unordered_map<AZStd::string, DisplayMapperAssetLut>  m_assetLuts;
             // DisplayMapper configurations per scene
             // DisplayMapper configurations per scene
-            DisplayMapperConfigurationDescriptor m_displayMapperConfiguration;
+            AZStd::optional<DisplayMapperConfigurationDescriptor> m_displayMapperConfiguration;
 
 
             void InitializeImagePool();
             void InitializeImagePool();
             // Initialize a LUT image with the given name.
             // Initialize a LUT image with the given name.

+ 4 - 0
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h

@@ -38,6 +38,7 @@ namespace AZ
 
 
             void SetShaperParameters(const ShaperParams& shaperParams);
             void SetShaperParameters(const ShaperParams& shaperParams);
             void SetLutAssetId(const AZ::Data::AssetId& assetId);
             void SetLutAssetId(const AZ::Data::AssetId& assetId);
+            const AZ::Data::AssetId& GetLutAssetId() const;
 
 
         protected:
         protected:
             explicit ApplyShaperLookupTablePass(const RPI::PassDescriptor& descriptor);
             explicit ApplyShaperLookupTablePass(const RPI::PassDescriptor& descriptor);
@@ -51,6 +52,9 @@ namespace AZ
             RHI::ShaderInputConstantIndex m_shaderShaperBiasIndex;
             RHI::ShaderInputConstantIndex m_shaderShaperBiasIndex;
             RHI::ShaderInputConstantIndex m_shaderShaperScaleIndex;
             RHI::ShaderInputConstantIndex m_shaderShaperScaleIndex;
 
 
+            void SetupFrameGraphDependenciesCommon(RHI::FrameGraphInterface frameGraph);
+            void CompileResourcesCommon(const RHI::FrameGraphCompileContext& context);
+
         private:
         private:
 
 
             // Scope producer functions...
             // Scope producer functions...

+ 3 - 1
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h

@@ -74,7 +74,7 @@ namespace AZ
             //! Configuration name
             //! Configuration name
             AZStd::string m_name;
             AZStd::string m_name;
 
 
-            DisplayMapperOperationType m_operationType;
+            DisplayMapperOperationType m_operationType = DisplayMapperOperationType::Aces;
 
 
             bool m_ldrGradingLutEnabled = false;
             bool m_ldrGradingLutEnabled = false;
             Data::Asset<RPI::AnyAsset> m_ldrColorGradingLut;
             Data::Asset<RPI::AnyAsset> m_ldrColorGradingLut;
@@ -96,6 +96,8 @@ namespace AZ
             static void Reflect(ReflectContext* context);
             static void Reflect(ReflectContext* context);
 
 
             DisplayMapperConfigurationDescriptor m_config;
             DisplayMapperConfigurationDescriptor m_config;
+            bool m_mergeLdrGradingLut = false;
+            RPI::AssetReference m_outputTransformOverride;
         };
         };
     } // namespace Render
     } // namespace Render
 } // namespace AZ
 } // namespace AZ

+ 2 - 1
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h

@@ -45,7 +45,8 @@ namespace AZ
             virtual void GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) = 0;
             virtual void GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) = 0;
             virtual void GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId) = 0;
             virtual void GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId) = 0;
             virtual void RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) = 0;
             virtual void RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) = 0;
-            virtual DisplayMapperConfigurationDescriptor GetDisplayMapperConfiguration() = 0;
+            virtual void UnregisterDisplayMapperConfiguration() = 0;
+            virtual const DisplayMapperConfigurationDescriptor* GetDisplayMapperConfiguration() = 0;
         };
         };
     } // namespace Render
     } // namespace Render
 } // namespace AZ
 } // namespace AZ

+ 9 - 1
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h

@@ -83,10 +83,18 @@ namespace AZ
             void BuildGradingLutTemplate();
             void BuildGradingLutTemplate();
             void CreateGradingAndAcesPasses();
             void CreateGradingAndAcesPasses();
 
 
-            void GetDisplayMapperConfiguration();
+            void UpdateDisplayMapperConfiguration();
             void ClearChildren();
             void ClearChildren();
 
 
+            // Return true if it needs a separate ldr color grading pass
+            bool UsesLdrGradingLutPass() const;
+            // Return true if it needs an output transform pass (for doing certain tonemapping and gamma correction)
+            bool UsesOutputTransformPass() const;
+
             DisplayMapperConfigurationDescriptor m_displayMapperConfigurationDescriptor;
             DisplayMapperConfigurationDescriptor m_displayMapperConfigurationDescriptor;
+            DisplayMapperConfigurationDescriptor m_defaultDisplayMapperConfigurationDescriptor;
+            bool m_mergeLdrGradingLut = false;
+            RPI::AssetReference m_outputTransformOverrideShader;
             bool m_needToRebuildChildren = false;
             bool m_needToRebuildChildren = false;
 
 
             const RPI::PassAttachmentBinding* m_pipelineOutput = nullptr;
             const RPI::PassAttachmentBinding* m_pipelineOutput = nullptr;

+ 8 - 2
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h

@@ -11,7 +11,7 @@
 #include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
 #include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
 #include <ACES/Aces.h>
 #include <ACES/Aces.h>
 #include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
 #include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
-#include <Atom/Feature/DisplayMapper/DisplayMapperFullScreenPass.h>
+#include <Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h>
 #include <PostProcessing/PostProcessingShaderOptionBase.h>
 #include <PostProcessing/PostProcessingShaderOptionBase.h>
 
 
 namespace AZ
 namespace AZ
@@ -20,12 +20,13 @@ namespace AZ
     {
     {
         static const char* const ToneMapperShaderVariantOptionName{ "o_tonemapperType" };
         static const char* const ToneMapperShaderVariantOptionName{ "o_tonemapperType" };
         static const char* const TransferFunctionShaderVariantOptionName{ "o_transferFunctionType" };
         static const char* const TransferFunctionShaderVariantOptionName{ "o_transferFunctionType" };
+        static const char* const LdrGradingLutShaderVariantOptionName{ "o_colorGradingEnabled" };
 
 
         /**
         /**
          * This pass is used to apply tonemapping and output transforms other than ACES.
          * This pass is used to apply tonemapping and output transforms other than ACES.
          */
          */
         class OutputTransformPass
         class OutputTransformPass
-            : public DisplayMapperFullScreenPass
+            : public ApplyShaperLookupTablePass
             , public PostProcessingShaderOptionBase
             , public PostProcessingShaderOptionBase
         {
         {
             AZ_RPI_PASS(OutputTransformPass);
             AZ_RPI_PASS(OutputTransformPass);
@@ -55,17 +56,22 @@ namespace AZ
             void CompileResources(const RHI::FrameGraphCompileContext& context) override;
             void CompileResources(const RHI::FrameGraphCompileContext& context) override;
             void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
             void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
 
 
+            // Pass behavior overrides...
+            void FrameBeginInternal(FramePrepareParams params) override;
+
             void InitializeShaderVariant();
             void InitializeShaderVariant();
             void UpdateCurrentShaderVariant();
             void UpdateCurrentShaderVariant();
 
 
             const AZ::Name m_toneMapperShaderVariantOptionName;
             const AZ::Name m_toneMapperShaderVariantOptionName;
             const AZ::Name m_transferFunctionShaderVariantOptionName;
             const AZ::Name m_transferFunctionShaderVariantOptionName;
+            const AZ::Name m_ldrGradingLutShaderVariantOptionName;
 
 
             ToneMapperType m_toneMapperType = ToneMapperType::None;
             ToneMapperType m_toneMapperType = ToneMapperType::None;
             TransferFunctionType m_transferFunctionType = TransferFunctionType::None;
             TransferFunctionType m_transferFunctionType = TransferFunctionType::None;
 
 
             bool m_needToUpdateShaderVariant = false;
             bool m_needToUpdateShaderVariant = false;
             RHI::ShaderInputConstantIndex m_shaderInputCinemaLimitsIndex;
             RHI::ShaderInputConstantIndex m_shaderInputCinemaLimitsIndex;
+            RHI::ShaderInputNameIndex m_exposureControlBufferInputIndex = "m_exposureControl";
 
 
             AZ::Render::DisplayMapperParameters m_displayMapperParameters = {};
             AZ::Render::DisplayMapperParameters m_displayMapperParameters = {};
 
 

+ 7 - 3
Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp

@@ -70,7 +70,6 @@ namespace AZ::Render
 
 
     void AcesDisplayMapperFeatureProcessor::Activate()
     void AcesDisplayMapperFeatureProcessor::Activate()
     {
     {
-        GetDefaultDisplayMapperConfiguration(m_displayMapperConfiguration);
     }
     }
 
 
     void AcesDisplayMapperFeatureProcessor::Deactivate()
     void AcesDisplayMapperFeatureProcessor::Deactivate()
@@ -457,8 +456,13 @@ namespace AZ::Render
         m_displayMapperConfiguration = config;
         m_displayMapperConfiguration = config;
     }
     }
 
 
-    DisplayMapperConfigurationDescriptor AcesDisplayMapperFeatureProcessor::GetDisplayMapperConfiguration()
+    void AcesDisplayMapperFeatureProcessor::UnregisterDisplayMapperConfiguration()
     {
     {
-        return m_displayMapperConfiguration;
+        m_displayMapperConfiguration.reset();
+    }
+
+    const DisplayMapperConfigurationDescriptor* AcesDisplayMapperFeatureProcessor::GetDisplayMapperConfiguration()
+    {
+        return m_displayMapperConfiguration.has_value() ? &m_displayMapperConfiguration.value() : nullptr;
     }
     }
 } // namespace AZ::Render
 } // namespace AZ::Render

+ 17 - 4
Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp

@@ -51,11 +51,8 @@ namespace AZ
             UpdateShaperSrg();
             UpdateShaperSrg();
         }
         }
 
 
-        void ApplyShaperLookupTablePass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
+        void ApplyShaperLookupTablePass::SetupFrameGraphDependenciesCommon([[maybe_unused]] RHI::FrameGraphInterface frameGraph)
         {
         {
-            DeclareAttachmentsToFrameGraph(frameGraph);
-            DeclarePassDependenciesToFrameGraph(frameGraph);
-
             if (m_needToReloadLut)
             if (m_needToReloadLut)
             {
             {
                 ReleaseLutImage();
                 ReleaseLutImage();
@@ -64,7 +61,17 @@ namespace AZ
             }
             }
 
 
             AZ_Assert(m_lutResource.m_lutStreamingImage != nullptr, "ApplyShaperLookupTablePass unable to acquire LUT image");
             AZ_Assert(m_lutResource.m_lutStreamingImage != nullptr, "ApplyShaperLookupTablePass unable to acquire LUT image");
+        }
+
+        void ApplyShaperLookupTablePass::CompileResourcesCommon([[maybe_unused]] const RHI::FrameGraphCompileContext& context)
+        {
+        }
 
 
+        void ApplyShaperLookupTablePass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
+        {
+            DeclareAttachmentsToFrameGraph(frameGraph);
+            DeclarePassDependenciesToFrameGraph(frameGraph);
+            SetupFrameGraphDependenciesCommon(frameGraph);
             frameGraph.SetEstimatedItemCount(1);
             frameGraph.SetEstimatedItemCount(1);
         }
         }
 
 
@@ -72,6 +79,7 @@ namespace AZ
         {
         {
             AZ_Assert(m_shaderResourceGroup != nullptr, "ApplyShaperLookupTablePass %s has a null shader resource group when calling Compile.", GetPathName().GetCStr());
             AZ_Assert(m_shaderResourceGroup != nullptr, "ApplyShaperLookupTablePass %s has a null shader resource group when calling Compile.", GetPathName().GetCStr());
 
 
+            CompileResourcesCommon(context);
             BindPassSrg(context, m_shaderResourceGroup);
             BindPassSrg(context, m_shaderResourceGroup);
             m_shaderResourceGroup->Compile();
             m_shaderResourceGroup->Compile();
         }
         }
@@ -99,6 +107,11 @@ namespace AZ
             m_lutAssetId = assetId;
             m_lutAssetId = assetId;
         }
         }
 
 
+        const AZ::Data::AssetId& ApplyShaperLookupTablePass::GetLutAssetId() const
+        {
+            return m_lutAssetId;
+        }
+
         void ApplyShaperLookupTablePass::UpdateShaperSrg()
         void ApplyShaperLookupTablePass::UpdateShaperSrg()
         {
         {
             AZ_Assert(m_shaderResourceGroup != nullptr, "ApplyShaperLookupTablePass %s has a null shader resource group when calling UpdateShaperSrg.", GetPathName().GetCStr());
             AZ_Assert(m_shaderResourceGroup != nullptr, "ApplyShaperLookupTablePass %s has a null shader resource group when calling UpdateShaperSrg.", GetPathName().GetCStr());

+ 2 - 0
Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp

@@ -137,6 +137,8 @@ namespace AZ
                 serializeContext->Class<DisplayMapperPassData, Base>()
                 serializeContext->Class<DisplayMapperPassData, Base>()
                     ->Version(0)
                     ->Version(0)
                     ->Field("DisplayMapperConfig", &DisplayMapperPassData::m_config)
                     ->Field("DisplayMapperConfig", &DisplayMapperPassData::m_config)
+                    ->Field("MergeLdrGradingLut", &DisplayMapperPassData::m_mergeLdrGradingLut)
+                    ->Field("OutputTransformShaderOverride", &DisplayMapperPassData::m_outputTransformOverride)
                     ;
                     ;
             }
             }
         }
         }

+ 116 - 43
Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp

@@ -43,13 +43,19 @@ namespace AZ
         DisplayMapperPass::DisplayMapperPass(const RPI::PassDescriptor& descriptor)
         DisplayMapperPass::DisplayMapperPass(const RPI::PassDescriptor& descriptor)
             : RPI::ParentPass(descriptor)
             : RPI::ParentPass(descriptor)
         {
         {
-            GetDisplayMapperConfiguration();
-
             const DisplayMapperPassData* passData = RPI::PassUtils::GetPassData<DisplayMapperPassData>(descriptor);
             const DisplayMapperPassData* passData = RPI::PassUtils::GetPassData<DisplayMapperPassData>(descriptor);
             if (passData != nullptr)
             if (passData != nullptr)
             {
             {
-                m_displayMapperConfigurationDescriptor = passData->m_config;
+                m_defaultDisplayMapperConfigurationDescriptor = passData->m_config;
+                m_mergeLdrGradingLut = passData->m_mergeLdrGradingLut;
+                m_outputTransformOverrideShader = passData->m_outputTransformOverride;
+            }
+            else
+            {
+                AcesDisplayMapperFeatureProcessor::GetDefaultDisplayMapperConfiguration(m_defaultDisplayMapperConfigurationDescriptor);
             }
             }
+
+            UpdateDisplayMapperConfiguration();
         }
         }
 
 
         void DisplayMapperPass::ConfigureDisplayParameters()
         void DisplayMapperPass::ConfigureDisplayParameters()
@@ -76,7 +82,9 @@ namespace AZ
                 }
                 }
                 if (m_outputTransformPass)
                 if (m_outputTransformPass)
                 {
                 {
-                    if (m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Reinhard)
+                    if (m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Reinhard ||
+                        m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::AcesFitted ||
+                        m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::AcesFilmic)
                     {
                     {
                         // When using Reinhard tonemapper, a gamma of 2.2 for the transfer function is used for LDR display,
                         // When using Reinhard tonemapper, a gamma of 2.2 for the transfer function is used for LDR display,
                         // and PQ is used for HDR.
                         // and PQ is used for HDR.
@@ -90,6 +98,10 @@ namespace AZ
                             m_outputTransformPass->SetTransferFunctionType(TransferFunctionType::PerceptualQuantizer);
                             m_outputTransformPass->SetTransferFunctionType(TransferFunctionType::PerceptualQuantizer);
                         }
                         }
                     }
                     }
+                    else if (m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Filmic)
+                    {
+                        m_outputTransformPass->SetTransferFunctionType(TransferFunctionType::None);
+                    }
 
 
                     m_outputTransformPass->SetDisplayBufferFormat(m_displayBufferFormat);
                     m_outputTransformPass->SetDisplayBufferFormat(m_displayBufferFormat);
                 }
                 }
@@ -171,7 +183,7 @@ namespace AZ
 
 
         void DisplayMapperPass::FrameEndInternal()
         void DisplayMapperPass::FrameEndInternal()
         {
         {
-            GetDisplayMapperConfiguration();
+            UpdateDisplayMapperConfiguration();
             ParentPass::FrameEndInternal();
             ParentPass::FrameEndInternal();
         }
         }
 
 
@@ -229,10 +241,13 @@ namespace AZ
                 outConnection.m_attachmentRef.m_attachment = "Output";
                 outConnection.m_attachmentRef.m_attachment = "Output";
             }
             }
 
 
+            AZStd::string azshaderPath = shaderFilePath;
+            AZ::StringFunc::Path::ReplaceExtension(azshaderPath, "azshader");
+
             Data::AssetId shaderAssetId;
             Data::AssetId shaderAssetId;
             Data::AssetCatalogRequestBus::BroadcastResult(
             Data::AssetCatalogRequestBus::BroadcastResult(
                 shaderAssetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
                 shaderAssetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
-                shaderFilePath, azrtti_typeid<RPI::ShaderAsset>(), false);
+                azshaderPath.c_str(), azrtti_typeid<RPI::ShaderAsset>(), false);
             if (!shaderAssetId.IsValid())
             if (!shaderAssetId.IsValid())
             {
             {
                 AZ_Assert(false, "[DisplayMapperPass] Unable to obtain asset id for %s.", shaderFilePath);
                 AZ_Assert(false, "[DisplayMapperPass] Unable to obtain asset id for %s.", shaderFilePath);
@@ -316,40 +331,59 @@ namespace AZ
             const char* passthroughShaderPath = "Shaders/PostProcessing/FullscreenCopy.azshader";
             const char* passthroughShaderPath = "Shaders/PostProcessing/FullscreenCopy.azshader";
             const char* sRGBShaderPath = "Shaders/PostProcessing/DisplayMapperSRGB.azshader";
             const char* sRGBShaderPath = "Shaders/PostProcessing/DisplayMapperSRGB.azshader";
             const char* applyShaperLookupTableShaderFilePath = "Shaders/PostProcessing/ApplyShaperLookupTable.azshader";
             const char* applyShaperLookupTableShaderFilePath = "Shaders/PostProcessing/ApplyShaperLookupTable.azshader";
-            const char* outputTransformShaderPath = "Shaders/PostProcessing/OutputTransform.azshader";
+            const char* outputTransformShaderPath = m_outputTransformOverrideShader.m_assetId.IsValid()
+                ? m_outputTransformOverrideShader.m_filePath.c_str()
+                : "Shaders/PostProcessing/OutputTransform.azshader";
 
 
             // Output transform templates. If there is no LDR grading LUT pass, then the output transform pass is the final pass
             // Output transform templates. If there is no LDR grading LUT pass, then the output transform pass is the final pass
             // and will render to the DisplayMapper's output attachment. Otherwise, it renders to its own attachment image.
             // and will render to the DisplayMapper's output attachment. Otherwise, it renders to its own attachment image.
+            bool useSeparateLdrGradingLutPass = UsesLdrGradingLutPass();
             // ACES
             // ACES
             m_acesOutputTransformTemplate.reset();
             m_acesOutputTransformTemplate.reset();
             m_acesOutputTransformTemplate = CreatePassTemplateHelper(
             m_acesOutputTransformTemplate = CreatePassTemplateHelper(
-                acesOutputTransformTemplateName, acesOutputTransformPassClassName,
-                m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled, outputTransformImageName, acesOuputTransformShaderPath);
+                acesOutputTransformTemplateName,
+                acesOutputTransformPassClassName,
+                useSeparateLdrGradingLutPass,
+                outputTransformImageName,
+                acesOuputTransformShaderPath);
             // ACES LUT
             // ACES LUT
             m_acesOutputTransformLutTemplate.reset();
             m_acesOutputTransformLutTemplate.reset();
             m_acesOutputTransformLutTemplate = CreatePassTemplateHelper(
             m_acesOutputTransformLutTemplate = CreatePassTemplateHelper(
-                acesOutputTransformLutTemplateName, acesOutputTransformLutPassClassName,
-                m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled, outputTransformImageName, acesOuputTransformLutShaderPath);
+                acesOutputTransformLutTemplateName,
+                acesOutputTransformLutPassClassName,
+                useSeparateLdrGradingLutPass,
+                outputTransformImageName,
+                acesOuputTransformLutShaderPath);
             // Bake ACES LUT
             // Bake ACES LUT
             m_bakeAcesOutputTransformLutTemplate.reset();
             m_bakeAcesOutputTransformLutTemplate.reset();
             m_bakeAcesOutputTransformLutTemplate = CreateBakeAcesLutPassTemplateHelper(
             m_bakeAcesOutputTransformLutTemplate = CreateBakeAcesLutPassTemplateHelper(
-                bakeAcesOutputTransformLutTemplateName, bakeAcesOutputTransformLutPassClassName,
+                bakeAcesOutputTransformLutTemplateName,
+                bakeAcesOutputTransformLutPassClassName,
                 bakeAcesOuputTransformLutShaderPath);
                 bakeAcesOuputTransformLutShaderPath);
             // Passthrough
             // Passthrough
             m_passthroughTemplate.reset();
             m_passthroughTemplate.reset();
             m_passthroughTemplate = CreatePassTemplateHelper(
             m_passthroughTemplate = CreatePassTemplateHelper(
-                displayMapperPassthroughTemplateName, displayMapperFullScreenPassClassName,
-                m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled, outputTransformImageName, passthroughShaderPath);
+                displayMapperPassthroughTemplateName,
+                displayMapperFullScreenPassClassName,
+                useSeparateLdrGradingLutPass,
+                outputTransformImageName,
+                passthroughShaderPath);
             // sRGB
             // sRGB
             m_sRGBTemplate.reset();
             m_sRGBTemplate.reset();
             m_sRGBTemplate = CreatePassTemplateHelper(
             m_sRGBTemplate = CreatePassTemplateHelper(
-                displayMapperSRGBTemplateName, displayMapperFullScreenPassClassName,
-                m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled, outputTransformImageName, sRGBShaderPath);
+                displayMapperSRGBTemplateName,
+                displayMapperFullScreenPassClassName,
+                useSeparateLdrGradingLutPass,
+                outputTransformImageName,
+                sRGBShaderPath);
             // Output Transform
             // Output Transform
             m_outputTransformTemplate.reset();
             m_outputTransformTemplate.reset();
             m_outputTransformTemplate = CreatePassTemplateHelper(
             m_outputTransformTemplate = CreatePassTemplateHelper(
-                outputTransformTemplateName, outputTransformPassClassName,
-                m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled, outputTransformImageName, outputTransformShaderPath);
+                outputTransformTemplateName,
+                outputTransformPassClassName,
+                useSeparateLdrGradingLutPass,
+                outputTransformImageName,
+                outputTransformShaderPath);
 
 
             // LDR grading LUT pass, if enabled, is the final pass and so it will render into the DisplayMapper's output attachment.
             // LDR grading LUT pass, if enabled, is the final pass and so it will render into the DisplayMapper's output attachment.
             m_ldrGradingLookupTableTemplate.reset();
             m_ldrGradingLookupTableTemplate.reset();
@@ -392,7 +426,7 @@ namespace AZ
                 // Passthrough
                 // Passthrough
                 // [GFX TODO][ATOM-4189] Optimize the passthrough function in the DisplayMapper
                 // [GFX TODO][ATOM-4189] Optimize the passthrough function in the DisplayMapper
                 // Only need to create this if LDR grading LUT pass is not used.
                 // Only need to create this if LDR grading LUT pass is not used.
-                if (!m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled)
+                if (!UsesLdrGradingLutPass())
                 {
                 {
                     m_displayMapperPassthroughPass = CreatePassHelper<DisplayMapperFullScreenPass>(passSystem, m_passthroughTemplate, m_displayMapperPassthroughPassName);
                     m_displayMapperPassthroughPass = CreatePassHelper<DisplayMapperFullScreenPass>(passSystem, m_passthroughTemplate, m_displayMapperPassthroughPassName);
                 }
                 }
@@ -401,13 +435,38 @@ namespace AZ
             {
             {
                 m_displayMapperSRGBPass = CreatePassHelper<DisplayMapperFullScreenPass>(passSystem, m_sRGBTemplate, m_displayMapperSRGBPassName);
                 m_displayMapperSRGBPass = CreatePassHelper<DisplayMapperFullScreenPass>(passSystem, m_sRGBTemplate, m_displayMapperSRGBPassName);
             }
             }
-            else if (m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Reinhard)
+            else if (UsesOutputTransformPass())
             {
             {
                 m_outputTransformPass = CreatePassHelper<OutputTransformPass>(passSystem, m_outputTransformTemplate, m_outputTransformPassName);
                 m_outputTransformPass = CreatePassHelper<OutputTransformPass>(passSystem, m_outputTransformTemplate, m_outputTransformPassName);
-                m_outputTransformPass->SetToneMapperType(ToneMapperType::Reinhard);
+                ToneMapperType type = ToneMapperType::None;
+                switch (m_displayMapperConfigurationDescriptor.m_operationType)
+                {
+                case DisplayMapperOperationType::Reinhard:
+                    type = ToneMapperType::Reinhard;
+                    break;
+                case DisplayMapperOperationType::AcesFitted:
+                    type = ToneMapperType::AcesFitted;
+                    break;
+                case DisplayMapperOperationType::AcesFilmic:
+                    type = ToneMapperType::AcesFilmic;
+                    break;
+                case DisplayMapperOperationType::Filmic:
+                    type = ToneMapperType::Filmic;
+                    break;
+                default:
+                    AZ_Assert(false, "Invalid tonemapper type %d", m_displayMapperConfigurationDescriptor.m_operationType);
+                    break;
+                }
+                m_outputTransformPass->SetToneMapperType(type);
+                if (m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled &&
+                    m_mergeLdrGradingLut &&
+                    m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut.GetId().IsValid())
+                {
+                    m_outputTransformPass->SetLutAssetId(m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut.GetId());
+                }
             }
             }
 
 
-            if (m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled)
+            if (UsesLdrGradingLutPass())
             {
             {
                 // ID should be valid, maybe no need to check again here.
                 // ID should be valid, maybe no need to check again here.
                 if (m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut.GetId().IsValid())
                 if (m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut.GetId().IsValid())
@@ -449,42 +508,42 @@ namespace AZ
             }
             }
         }
         }
 
 
-        void DisplayMapperPass::GetDisplayMapperConfiguration()
+        void DisplayMapperPass::UpdateDisplayMapperConfiguration()
         {
         {
-            DisplayMapperConfigurationDescriptor desc;
+            // If no configuration is found, then use the default value that was set for the pass.
+            DisplayMapperConfigurationDescriptor desc = m_defaultDisplayMapperConfigurationDescriptor;
             AZ::RPI::Scene* scene = GetScene();
             AZ::RPI::Scene* scene = GetScene();
             if (scene)
             if (scene)
             {
             {
                 AcesDisplayMapperFeatureProcessor* fp = scene->GetFeatureProcessor<AcesDisplayMapperFeatureProcessor>();
                 AcesDisplayMapperFeatureProcessor* fp = scene->GetFeatureProcessor<AcesDisplayMapperFeatureProcessor>();
                 if (fp)
                 if (fp)
                 {
                 {
-                    desc = fp->GetDisplayMapperConfiguration();
-
-                    // Check to ensure that a valid LUT has been set
-                    if (desc.m_ldrGradingLutEnabled)
+                    auto fpDesc = fp->GetDisplayMapperConfiguration();
+                    if (fpDesc)
                     {
                     {
-                        if (!desc.m_ldrColorGradingLut.GetId().IsValid())
+                        desc = *fpDesc;
+                        // Check to ensure that a valid LUT has been set
+                        if (desc.m_ldrGradingLutEnabled)
                         {
                         {
-                            desc.m_ldrGradingLutEnabled = false;
+                            if (!desc.m_ldrColorGradingLut.GetId().IsValid())
+                            {
+                                desc.m_ldrGradingLutEnabled = false;
+                            }
                         }
                         }
                     }
                     }
-
-                    if (desc.m_operationType != m_displayMapperConfigurationDescriptor.m_operationType ||
-                        desc.m_ldrGradingLutEnabled != m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled ||
-                        desc.m_ldrColorGradingLut != m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut ||
-                        desc.m_acesParameterOverrides.m_overrideDefaults != m_displayMapperConfigurationDescriptor.m_acesParameterOverrides.m_overrideDefaults)
-                    {
-                        m_flags.m_createChildren = true;
-                        QueueForBuildAndInitialization();
-                    }
-                    m_displayMapperConfigurationDescriptor = desc;
                 }
                 }
             }
             }
-            else
+
+            if (desc.m_operationType != m_displayMapperConfigurationDescriptor.m_operationType ||
+                desc.m_ldrGradingLutEnabled != m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled ||
+                desc.m_ldrColorGradingLut != m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut ||
+                desc.m_acesParameterOverrides.m_overrideDefaults !=
+                    m_displayMapperConfigurationDescriptor.m_acesParameterOverrides.m_overrideDefaults)
             {
             {
-                // At the time the DisplayMapper is created, there is no scene, so just get the default settings 
-                AcesDisplayMapperFeatureProcessor::GetDefaultDisplayMapperConfiguration(m_displayMapperConfigurationDescriptor);
+                m_flags.m_createChildren = true;
+                QueueForBuildAndInitialization();
             }
             }
+            m_displayMapperConfigurationDescriptor = desc;
         }
         }
 
 
         void DisplayMapperPass::ClearChildren()
         void DisplayMapperPass::ClearChildren()
@@ -499,5 +558,19 @@ namespace AZ
             m_ldrGradingLookupTablePass = nullptr;
             m_ldrGradingLookupTablePass = nullptr;
             m_outputTransformPass = nullptr;
             m_outputTransformPass = nullptr;
         }
         }
+
+        bool DisplayMapperPass::UsesLdrGradingLutPass() const
+        {
+            // Merging the ldr color grading is only supported for the OutputTransform pass at the moment.
+            return m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled && !(m_mergeLdrGradingLut && UsesOutputTransformPass());
+        }
+
+        bool DisplayMapperPass::UsesOutputTransformPass() const
+        {
+            return m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Reinhard ||
+                m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::AcesFitted ||
+                m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::Filmic ||
+                m_displayMapperConfigurationDescriptor.m_operationType == DisplayMapperOperationType::AcesFilmic;
+        }
     }   // namespace Render
     }   // namespace Render
 }   // namespace AZ
 }   // namespace AZ

+ 75 - 12
Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp

@@ -14,6 +14,9 @@
 #include <Atom/RPI.Public/RenderPipeline.h>
 #include <Atom/RPI.Public/RenderPipeline.h>
 #include <Atom/RPI.Public/Scene.h>
 #include <Atom/RPI.Public/Scene.h>
 
 
+#include <PostProcess/PostProcessFeatureProcessor.h>
+#include <PostProcess/ExposureControl/ExposureControlSettings.h>
+
 namespace AZ
 namespace AZ
 {
 {
     namespace Render
     namespace Render
@@ -25,10 +28,12 @@ namespace AZ
         }
         }
 
 
         OutputTransformPass::OutputTransformPass(const RPI::PassDescriptor& descriptor)
         OutputTransformPass::OutputTransformPass(const RPI::PassDescriptor& descriptor)
-            : DisplayMapperFullScreenPass(descriptor)
+            : ApplyShaperLookupTablePass(descriptor)
             , m_toneMapperShaderVariantOptionName(ToneMapperShaderVariantOptionName)
             , m_toneMapperShaderVariantOptionName(ToneMapperShaderVariantOptionName)
             , m_transferFunctionShaderVariantOptionName(TransferFunctionShaderVariantOptionName)
             , m_transferFunctionShaderVariantOptionName(TransferFunctionShaderVariantOptionName)
+            , m_ldrGradingLutShaderVariantOptionName(LdrGradingLutShaderVariantOptionName)
         {
         {
+            m_flags.m_bindViewSrg = true;
         }
         }
 
 
         OutputTransformPass::~OutputTransformPass()
         OutputTransformPass::~OutputTransformPass()
@@ -37,7 +42,7 @@ namespace AZ
 
 
         void OutputTransformPass::InitializeInternal()
         void OutputTransformPass::InitializeInternal()
         {
         {
-            DisplayMapperFullScreenPass::InitializeInternal();
+            ApplyShaperLookupTablePass::InitializeInternal();
 
 
             AZ_Assert(m_shaderResourceGroup != nullptr, "OutputTransformPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr());
             AZ_Assert(m_shaderResourceGroup != nullptr, "OutputTransformPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr());
 
 
@@ -51,6 +56,10 @@ namespace AZ
 
 
         void OutputTransformPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
         void OutputTransformPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
         {
         {
+            if (GetLutAssetId().IsValid())
+            {
+                ApplyShaperLookupTablePass::SetupFrameGraphDependenciesCommon(frameGraph);
+            }
             DeclareAttachmentsToFrameGraph(frameGraph);
             DeclareAttachmentsToFrameGraph(frameGraph);
             DeclarePassDependenciesToFrameGraph(frameGraph);
             DeclarePassDependenciesToFrameGraph(frameGraph);
 
 
@@ -61,13 +70,19 @@ namespace AZ
         {
         {
             AZ_Assert(m_shaderResourceGroup != nullptr, "OutputTransformPass %s has a null shader resource group when calling Compile.", GetPathName().GetCStr());
             AZ_Assert(m_shaderResourceGroup != nullptr, "OutputTransformPass %s has a null shader resource group when calling Compile.", GetPathName().GetCStr());
 
 
+            if (GetLutAssetId().IsValid())
+            {
+                ApplyShaperLookupTablePass::CompileResourcesCommon(context);
+            }
+
             if (m_needToUpdateShaderVariant)
             if (m_needToUpdateShaderVariant)
             {
             {
                 UpdateCurrentShaderVariant();
                 UpdateCurrentShaderVariant();
             }
             }
 
 
-            m_shaderResourceGroup->SetConstant(m_shaderInputCinemaLimitsIndex, m_displayMapperParameters.m_cinemaLimits);
+            CompileShaderVariant(m_shaderResourceGroup);
 
 
+            m_shaderResourceGroup->SetConstant(m_shaderInputCinemaLimitsIndex, m_displayMapperParameters.m_cinemaLimits);
             BindPassSrg(context, m_shaderResourceGroup);
             BindPassSrg(context, m_shaderResourceGroup);
             m_shaderResourceGroup->Compile();
             m_shaderResourceGroup->Compile();
         }
         }
@@ -88,6 +103,30 @@ namespace AZ
             commandList->Submit(m_item);
             commandList->Submit(m_item);
         }
         }
 
 
+        void OutputTransformPass::FrameBeginInternal(FramePrepareParams params)
+        {
+            ApplyShaperLookupTablePass::FrameBeginInternal(params);
+            AZ::RPI::Scene* scene = GetScene();
+            if (scene)
+            {
+                PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor<PostProcessFeatureProcessor>();
+                if (fp)
+                {
+                    AZ::RPI::ViewPtr view = GetRenderPipeline()->GetFirstView(GetPipelineViewTag());
+                    PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view);
+                    if (postProcessSettings)
+                    {
+                        ExposureControlSettings* settings = postProcessSettings->GetExposureControlSettings();
+                        if (settings)
+                        {
+                            settings->UpdateBuffer();
+                            view->GetShaderResourceGroup()->SetBufferView(m_exposureControlBufferInputIndex, settings->GetBufferView());
+                        }
+                    }
+                }
+            }
+        }
+
         void OutputTransformPass::SetToneMapperType(const ToneMapperType& toneMapperType)
         void OutputTransformPass::SetToneMapperType(const ToneMapperType& toneMapperType)
         {
         {
             if (m_toneMapperType != toneMapperType)
             if (m_toneMapperType != toneMapperType)
@@ -110,23 +149,36 @@ namespace AZ
         {
         {
             AZ_Assert(m_shader != nullptr, "OutputTransformPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr());
             AZ_Assert(m_shader != nullptr, "OutputTransformPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr());
 
 
-            AZStd::vector<AZ::Name> toneMapperVariationTypes = { AZ::Name("ToneMapperType::None"), AZ::Name("ToneMapperType::Reinhard")};
+            AZStd::vector<AZ::Name> toneMapperVariationTypes = { AZ::Name("ToneMapperType::None"),
+                                                                 AZ::Name("ToneMapperType::Reinhard"),
+                                                                 AZ::Name("ToneMapperType::AcesFitted"),
+                                                                 AZ::Name("ToneMapperType::AcesFilmic"),
+                                                                 AZ::Name("ToneMapperType::Filmic") };
             AZStd::vector<AZ::Name> transferFunctionVariationTypes = {
             AZStd::vector<AZ::Name> transferFunctionVariationTypes = {
                 AZ::Name("TransferFunctionType::None"),
                 AZ::Name("TransferFunctionType::None"),
                 AZ::Name("TransferFunctionType::Gamma22"),
                 AZ::Name("TransferFunctionType::Gamma22"),
                 AZ::Name("TransferFunctionType::PerceptualQuantizer") };
                 AZ::Name("TransferFunctionType::PerceptualQuantizer") };
 
 
-            auto toneMapperVariationTypeCount = toneMapperVariationTypes.size();
-            auto totalVariationCount = toneMapperVariationTypes.size() * transferFunctionVariationTypes.size();
+            AZStd::vector<AZ::Name> doLdrGradingTypes = {AZ::Name("true"), AZ::Name("false")};
 
 
             // Caching all pipeline state for each shader variation for performance reason.
             // Caching all pipeline state for each shader variation for performance reason.
-            for (auto shaderVariantIndex = 0; shaderVariantIndex < totalVariationCount; ++shaderVariantIndex)
+            auto shaderOption = m_shader->CreateShaderOptionGroup();
+            for (uint32_t tonemapperIndex = 0; tonemapperIndex < toneMapperVariationTypes.size(); ++tonemapperIndex)
             {
             {
-                auto shaderOption = m_shader->CreateShaderOptionGroup();
-                shaderOption.SetValue(m_toneMapperShaderVariantOptionName, toneMapperVariationTypes[shaderVariantIndex % toneMapperVariationTypeCount]);
-                shaderOption.SetValue(m_transferFunctionShaderVariantOptionName, transferFunctionVariationTypes[shaderVariantIndex / toneMapperVariationTypeCount]);
-
-                PreloadShaderVariant(m_shader, shaderOption, GetRenderAttachmentConfiguration(), GetMultisampleState());
+                for (uint32_t transferFunctionIndex = 0; transferFunctionIndex < transferFunctionVariationTypes.size(); ++transferFunctionIndex)
+                {
+                    for (uint32_t colorGradingIndex = 0; colorGradingIndex < doLdrGradingTypes.size(); ++colorGradingIndex)
+                    {
+                        shaderOption.SetValue(
+                            m_toneMapperShaderVariantOptionName, toneMapperVariationTypes[tonemapperIndex]);
+                        shaderOption.SetValue(
+                            m_transferFunctionShaderVariantOptionName, transferFunctionVariationTypes[transferFunctionIndex]);
+                        shaderOption.SetValue(
+                            m_ldrGradingLutShaderVariantOptionName, doLdrGradingTypes[colorGradingIndex]);
+
+                        PreloadShaderVariant(m_shader, shaderOption, GetRenderAttachmentConfiguration(), GetMultisampleState());
+                    }
+                }
             }
             }
 
 
             m_needToUpdateShaderVariant = true;
             m_needToUpdateShaderVariant = true;
@@ -143,6 +195,15 @@ namespace AZ
             case ToneMapperType::Reinhard:
             case ToneMapperType::Reinhard:
                 shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::Reinhard"));
                 shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::Reinhard"));
                 break;
                 break;
+            case ToneMapperType::AcesFitted:
+                shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::AcesFitted"));
+                break;
+            case ToneMapperType::Filmic:
+                shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::Filmic"));
+                break;
+            case ToneMapperType::AcesFilmic:
+                shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::AcesFilmic"));
+                break;
             default:
             default:
                 shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::None"));
                 shaderOption.SetValue(m_toneMapperShaderVariantOptionName, AZ::Name("ToneMapperType::None"));
                 break;
                 break;
@@ -160,6 +221,8 @@ namespace AZ
                 break;
                 break;
             }
             }
 
 
+            shaderOption.SetValue(m_ldrGradingLutShaderVariantOptionName, GetLutAssetId().IsValid() ? AZ::Name("true") : AZ::Name("false")); 
+
             UpdateShaderVariant(shaderOption);
             UpdateShaderVariant(shaderOption);
             m_needToUpdateShaderVariant = false;
             m_needToUpdateShaderVariant = false;
         }
         }

+ 7 - 0
Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp

@@ -120,10 +120,17 @@ namespace AZ
             m_entityId = entityId;
             m_entityId = entityId;
 
 
             DisplayMapperComponentRequestBus::Handler::BusConnect(m_entityId);
             DisplayMapperComponentRequestBus::Handler::BusConnect(m_entityId);
+            OnConfigChanged();
         }
         }
 
 
         void DisplayMapperComponentController::Deactivate()
         void DisplayMapperComponentController::Deactivate()
         {
         {
+            DisplayMapperFeatureProcessorInterface* fp =
+                AZ::RPI::Scene::GetFeatureProcessorForEntity<DisplayMapperFeatureProcessorInterface>(m_entityId);
+            if (fp)
+            {
+                fp->UnregisterDisplayMapperConfiguration();
+            }
             DisplayMapperComponentRequestBus::Handler::BusDisconnect(m_entityId);
             DisplayMapperComponentRequestBus::Handler::BusDisconnect(m_entityId);
 
 
             m_postProcessInterface = nullptr;
             m_postProcessInterface = nullptr;