PPEyeAdaptation.bsl 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #include "$ENGINE$\PPBase.bslinc"
  2. technique PPEyeAdaptation
  3. {
  4. mixin PPBase;
  5. code
  6. {
  7. #define NUM_BUCKETS (THREADGROUP_SIZE_X * THREADGROUP_SIZE_Y)
  8. [internal]
  9. cbuffer Input
  10. {
  11. // [0]: x - histogram scale, y - histogram offset, z - histogram percent low, w - histogram percent high
  12. // [1]: x - min adaptation, y - max adaptation, z - adaptation speed up, w - adaptation speed down
  13. // [2]: x - exposure scale, y - frame time delta, zw - nothing
  14. float4 gEyeAdaptationParams[3];
  15. }
  16. Texture2D gHistogramTex;
  17. /**
  18. * Returns luminance of the histogram bucket.
  19. *
  20. * @param pos Position of the histogram bucket in range [0, 1].
  21. * @return Luminance of the bucket.
  22. */
  23. float calcHistogramLuminance(float pos)
  24. {
  25. return exp2((pos - gEyeAdaptationParams[0].y) / gEyeAdaptationParams[0].x);
  26. }
  27. /**
  28. * Returns value of the histogram bucket.
  29. *
  30. * @param histogram Texture containing the histogram buckets in the first row.
  31. * @param bucketIdx Index of the bucket. Caller must ensure it is in valid range.
  32. * @return Value of the needed histogram bucket.
  33. */
  34. float getHistogramValue(Texture2D histogram, uint bucketIdx)
  35. {
  36. uint texelIdx = bucketIdx / 4;
  37. float4 packedValue = histogram.Load(int3(texelIdx, 0, 0));
  38. float4 mask = float4(
  39. (bucketIdx % 4) == 0,
  40. (bucketIdx % 4) == 1,
  41. (bucketIdx % 4) == 2,
  42. (bucketIdx % 4) == 3);
  43. return dot(packedValue, mask);
  44. }
  45. /**
  46. * Calculates the sum of all values in the histogram.
  47. *
  48. * @param histogram Texture containing the histogram buckets in the first row.
  49. * @return Sum of all the values in the histogram.
  50. */
  51. float calcHistogramSum(Texture2D histogram)
  52. {
  53. float sum = 0;
  54. for(uint i = 0; i < NUM_BUCKETS; i++)
  55. sum += getHistogramValue(histogram, i);
  56. return sum;
  57. }
  58. /**
  59. * Calculates the average luminance in the histogram, while ignoring the outlier values that may skew the result.
  60. *
  61. * @param histogram Texture containing the histogram buckets in the first row.
  62. * @param low Sum below which to ignore values (removing lower end outliers), in range [0, histogramSum].
  63. * @param high Sum above which to ignore values (removing higher end outliers), in range [0, histogramSum].
  64. * Must be higher than @low.
  65. * @return Average luminance in the histogram.
  66. */
  67. float calcHistogramAverageLuminance(Texture2D histogram, float low, float high)
  68. {
  69. float2 sumAndWeight = float2(0.0f, 0.0f);
  70. for(uint i = 0; i < NUM_BUCKETS; i++)
  71. {
  72. float value = getHistogramValue(histogram, i);
  73. // Ignore any values below the @low parameter, and then shift the valid range
  74. // by the amount we ignored. Eventually the low end of the range reaches zero
  75. // and values are no longer ignored.
  76. float offset = min(value, low);
  77. value = value - offset;
  78. low -= offset;
  79. high -= offset;
  80. // Ignore any values above the @high parameter, and then shift the valid range.
  81. value = min(value, high);
  82. high -= value;
  83. float histogramPos = i / (float)NUM_BUCKETS;
  84. float luminance = calcHistogramLuminance(histogramPos);
  85. sumAndWeight += float2(luminance, 1) * value;
  86. }
  87. return sumAndWeight.x / max(0.0001f, sumAndWeight.y);
  88. }
  89. /**
  90. * Calculates the eye adaptation from the luminance in the provided histogram. Eye adaptation value will be
  91. * used for automatically scaling expsure based on scene brightness.
  92. *
  93. * @param histogram Texture containing the histogram buckets in the first row.
  94. * @return Ideal eye adaptation value for the provided luminance.
  95. */
  96. float calcEyeAdaptation(Texture2D histogram)
  97. {
  98. float sum = calcHistogramSum(histogram);
  99. float lowRange = gEyeAdaptationParams[0].z * sum;
  100. float highRange = gEyeAdaptationParams[0].w * sum;
  101. float avgLuminance = calcHistogramAverageLuminance(histogram, lowRange, highRange);
  102. avgLuminance = clamp(avgLuminance, gEyeAdaptationParams[1].x, gEyeAdaptationParams[1].y);
  103. return avgLuminance;
  104. }
  105. /**
  106. * Smooths out eye adaptation changes over multiple frames so they aren't as jarring.
  107. *
  108. * @param old Eye adaptation value from the previous frame.
  109. * @param target Ideal eye adaptation value for this frame.
  110. * @param frameDelta Time difference between this and last frame, in seconds.
  111. * @return Smoothed eye adaptation.
  112. */
  113. float smoothEyeAdaptation(float old, float target, float frameDelta)
  114. {
  115. float diff = target - old;
  116. float speedUp = gEyeAdaptationParams[1].z;
  117. float speedDown = gEyeAdaptationParams[1].w;
  118. float adaptionSpeed = (diff > 0) ? speedUp : speedDown;
  119. float scale = 1.0f - exp2(-frameDelta * adaptionSpeed);
  120. return clamp(old + diff * scale, gEyeAdaptationParams[1].x, gEyeAdaptationParams[1].y);
  121. }
  122. float4 fsmain(VStoFS input) : SV_Target0
  123. {
  124. float exposureScale = gEyeAdaptationParams[2].x;
  125. float targetAdaptation = calcEyeAdaptation(gHistogramTex);
  126. float oldExposure = gHistogramTex.Load(int3(0, 1, 0)).x;
  127. float oldAdaptation = exposureScale / oldExposure; // Assuming same exposure scale as last frame
  128. float frameDelta = gEyeAdaptationParams[2].y;
  129. float smoothAdaptation = smoothEyeAdaptation(oldAdaptation, targetAdaptation, frameDelta);
  130. return exposureScale / smoothAdaptation; // Returns exposure
  131. }
  132. };
  133. };