downsampling.frag 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // This shader performs downsampling on a texture,
  2. // as taken from Call Of Duty method, presented at ACM Siggraph 2014.
  3. // This particular method was customly designed to eliminate
  4. // "pulsating artifacts and temporal stability issues".
  5. #version 330 core
  6. /* === Varyings === */
  7. noperspective in vec2 vTexCoord;
  8. /* === Uniforms === */
  9. uniform sampler2D uTexture;
  10. uniform vec2 uTexelSize; //< Reciprocal of the resolution of the source being sampled
  11. uniform vec4 uPrefilter;
  12. uniform int uMipLevel; //< Which mip we are writing to, used for Karis average
  13. /* === Fragments === */
  14. layout (location = 0) out vec3 FragColor;
  15. /* === Helper Functions === */
  16. vec3 LinearToSRGB(vec3 color)
  17. {
  18. // color = clamp(color, vec3(0.0), vec3(1.0));
  19. // const vec3 a = vec3(0.055f);
  20. // return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
  21. // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
  22. return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
  23. }
  24. float sRGBToLuma(vec3 col)
  25. {
  26. //return dot(col, vec3(0.2126, 0.7152, 0.0722));
  27. return dot(col, vec3(0.299, 0.587, 0.114));
  28. }
  29. float KarisAverage(vec3 col)
  30. {
  31. // Formula is 1 / (1 + luma)
  32. float luma = sRGBToLuma(LinearToSRGB(col)) * 0.25f;
  33. return 1.0f / (1.0f + luma);
  34. }
  35. vec3 Prefilter (vec3 col)
  36. {
  37. float brightness = max(col.r, max(col.g, col.b));
  38. float soft = brightness - uPrefilter.y;
  39. soft = clamp(soft, 0, uPrefilter.z);
  40. soft = soft * soft * uPrefilter.w;
  41. float contribution = max(soft, brightness - uPrefilter.x);
  42. contribution /= max(brightness, 0.00001);
  43. return col * contribution;
  44. }
  45. /* === Main Function === */
  46. void main()
  47. {
  48. // NOTE: This is the readable version of this shader. It will be optimized!
  49. float x = uTexelSize.x;
  50. float y = uTexelSize.y;
  51. // Take 13 samples around current texel:
  52. // a - b - c
  53. // - j - k -
  54. // d - e - f
  55. // - l - m -
  56. // g - h - i
  57. // === ('e' is the current texel) ===
  58. vec3 a = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y + 2*y)).rgb;
  59. vec3 b = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y + 2*y)).rgb;
  60. vec3 c = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y + 2*y)).rgb;
  61. vec3 d = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y)).rgb;
  62. vec3 e = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y)).rgb;
  63. vec3 f = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y)).rgb;
  64. vec3 g = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y - 2*y)).rgb;
  65. vec3 h = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y - 2*y)).rgb;
  66. vec3 i = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y - 2*y)).rgb;
  67. vec3 j = texture(uTexture, vec2(vTexCoord.x - x, vTexCoord.y + y)).rgb;
  68. vec3 k = texture(uTexture, vec2(vTexCoord.x + x, vTexCoord.y + y)).rgb;
  69. vec3 l = texture(uTexture, vec2(vTexCoord.x - x, vTexCoord.y - y)).rgb;
  70. vec3 m = texture(uTexture, vec2(vTexCoord.x + x, vTexCoord.y - y)).rgb;
  71. // Apply weighted distribution:
  72. // 0.5 + 0.125 + 0.125 + 0.125 + 0.125 = 1
  73. // a,b,d,e * 0.125
  74. // b,c,e,f * 0.125
  75. // d,e,g,h * 0.125
  76. // e,f,h,i * 0.125
  77. // j,k,l,m * 0.5
  78. // This shows 5 square areas that are being sampled. But some of them overlap,
  79. // so to have an energy preserving downsample we need to make some adjustments.
  80. // The weights are the distributed, so that the sum of j,k,l,m (e.g.)
  81. // contribute 0.5 to the final color output. The code below is written
  82. // to effectively yield this sum. We get:
  83. // 0.125*5 + 0.03125*4 + 0.0625*4 = 1
  84. // Check if we need to perform Karis average on each block of 4 samples
  85. vec3 groups[5];
  86. if (uMipLevel == 0)
  87. {
  88. // We are writing to mip 0, so we need to apply Karis average to each block
  89. // of 4 samples to prevent fireflies (very bright subpixels, leads to pulsating
  90. // artifacts).
  91. groups[0] = (a+b+d+e) * (0.125/4.0);
  92. groups[1] = (b+c+e+f) * (0.125/4.0);
  93. groups[2] = (d+e+g+h) * (0.125/4.0);
  94. groups[3] = (e+f+h+i) * (0.125/4.0);
  95. groups[4] = (j+k+l+m) * (0.5/4.0);
  96. groups[0] *= KarisAverage(groups[0]);
  97. groups[1] *= KarisAverage(groups[1]);
  98. groups[2] *= KarisAverage(groups[2]);
  99. groups[3] *= KarisAverage(groups[3]);
  100. groups[4] *= KarisAverage(groups[4]);
  101. FragColor = groups[0]+groups[1]+groups[2]+groups[3]+groups[4];
  102. FragColor = max(FragColor, 0.0001);
  103. FragColor = Prefilter(FragColor);
  104. }
  105. else
  106. {
  107. FragColor = e*0.125; // ok
  108. FragColor += (a+c+g+i)*0.03125; // ok
  109. FragColor += (b+d+f+h)*0.0625; // ok
  110. FragColor += (j+k+l+m)*0.125; // ok
  111. }
  112. }