EyeAdaptation.azsl 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <scenesrg_all.srgi>
  9. #include <viewsrg_all.srgi>
  10. #include <Atom/RPI/Math.azsli>
  11. #include "EyeAdaptationUtil.azsli"
  12. ShaderResourceGroup PassSrg : SRG_PerPass
  13. {
  14. // This is a mip chain containing the scene luminance info
  15. // x = min luminance
  16. // y = average luminance
  17. // z = max luminance
  18. Texture2D<float4> m_sceneLuminance;
  19. Sampler LinearSampler
  20. {
  21. MinFilter = Linear;
  22. MagFilter = Linear;
  23. MipFilter = Linear;
  24. AddressU = Clamp;
  25. AddressV = Clamp;
  26. AddressW = Clamp;
  27. };
  28. // Contains the eye-adaptation feedback settings. This buffer will be updated in this pass.
  29. RWStructuredBuffer<EyeAdaptation> m_eyeAdaptationData;
  30. }
  31. [numthreads(1, 1, 1)]
  32. void MainCS(uint3 dispatch_id : SV_DispatchThreadID)
  33. {
  34. // Determine the goal exposure value in logarithmic(perceptual) space.
  35. float goalExposureLog2 = 0.0; // default goal is no adjustment
  36. if (ViewSrg::m_exposureControl.m_eyeAdaptationEnabled)
  37. {
  38. // Get the dimensions and mip levels from the luminance mip chain
  39. uint2 inputDimensions;
  40. uint numMipLevels = 1;
  41. PassSrg::m_sceneLuminance.GetDimensions(0, inputDimensions.x, inputDimensions.y, numMipLevels);
  42. // Use smallest 1x1 mip to get scene average luminance.
  43. float luminanceAvg = PassSrg::m_sceneLuminance.SampleLevel(PassSrg::LinearSampler, float2(0.5f, 0.5f), numMipLevels - 1).y;
  44. // Middle gray value is the half-tone between black and white in exposure control.
  45. // Defining Middle Grey value to 0.18f as the typical value used in photography. Cf. https://en.wikipedia.org/wiki/Middle_gray
  46. const float middleGray = 0.18f;
  47. // Protection from NaNs getting in the frame buffer leading to NaN luminance.
  48. if (isnan(luminanceAvg))
  49. {
  50. luminanceAvg = middleGray;
  51. }
  52. // This finds the exposure at which the lumininaceAvg would be equal to middleGray, ie perfectly neutral.
  53. goalExposureLog2 = log2(luminanceAvg / middleGray);
  54. // Clamp goal exposure by min / max.
  55. float minExposure = ViewSrg::m_exposureControl.m_exposureMinLog2;
  56. float maxExposure = ViewSrg::m_exposureControl.m_exposureMaxLog2;
  57. goalExposureLog2 = clamp(goalExposureLog2, minExposure, maxExposure);
  58. }
  59. // Convert the previous frame linear exposure to a stop adjustment.
  60. float previousFrameExposureLog2 = -log2(max(0.00000001, PassSrg::m_eyeAdaptationData[0].m_exposureValue));
  61. float exposureDifference = goalExposureLog2 - previousFrameExposureLog2;
  62. // Speed depends on if the exposure should be increased or decreased.
  63. const float speed = exposureDifference > 0.0 ? ViewSrg::m_exposureControl.m_speedUp : ViewSrg::m_exposureControl.m_speedDown;
  64. // Update the adjustment for this frame based on the frame deltaTime and speed
  65. float deltaTime = clamp(SceneSrg::m_time - PassSrg::m_eyeAdaptationData[0].m_setValueTime, 0.0f, 1.0f);
  66. float exposureAdjustment = exposureDifference * deltaTime * speed;
  67. float newExposureLog2 = previousFrameExposureLog2 + exposureAdjustment;
  68. // Store the linear exposure so it can be used by the look modification transform later.
  69. // newExposureLog2 is negated because m_exposureValue is used to correct for a given exposure.
  70. PassSrg::m_eyeAdaptationData[0].m_exposureValue = pow(2.0f, -newExposureLog2);
  71. PassSrg::m_eyeAdaptationData[0].m_setValueTime = SceneSrg::m_time;
  72. }