PPEyeAdaptation.bsl 5.5 KB

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