PPEyeAdaptation.bsl 5.6 KB

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