You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
3.3 KiB

2 years ago
  1. // GLSL debanding shader, use as: source-shader=path/to/deband.glsl
  2. // (Loosely based on flash3kyuu_deband, but expanded to multiple iterations)
  3. //------------ Configuration section ------------
  4. // The threshold of difference below which a pixel is considered to be part of
  5. // a gradient. Higher = more debanding, but setting it too high diminishes image
  6. // details.
  7. #define THRESHOLD 64
  8. // The range (in source pixels) at which to sample for neighbours. Higher values
  9. // will find more gradients, but lower values will deband more aggressively.
  10. #define RANGE 8
  11. // The number of debanding iterations to perform. Each iteration samples from
  12. // random positions, so increasing the number of iterations is likely to
  13. // increase the debanding quality. Conversely, it slows the shader down.
  14. // (Each iteration will use a multiple of the configured RANGE, and a
  15. // successively lower THRESHOLD - so setting it much higher has little effect)
  16. #define ITERATIONS 4
  17. // (Optional) Add some extra noise to the image. This significantly helps cover
  18. // up remaining banding and blocking artifacts, at comparatively little visual
  19. // quality. Higher = more grain. Setting it to 0 disables the effect.
  20. #define GRAIN 48
  21. // Note: If performance is too slow, try eg. RANGE=16 ITERATIONS=2. In general,
  22. // an increase in the number of ITERATIONS should roughly correspond to a
  23. // decrease in RANGE and perhaps an increase in THRESHOLD.
  24. //------------ End of configuration ------------
  25. // Wide usage friendly PRNG, shamelessly stolen from a GLSL tricks forum post
  26. float mod289(float x) { return x - floor(x / 289.0) * 289.0; }
  27. float permute(float x) { return mod289((34.0*x + 1.0) * x); }
  28. float rand(float x) { return fract(x / 41.0); }
  29. // Helper: Calculate a stochastic approximation of the avg color around a pixel
  30. vec4 average(sampler2D tex, vec2 pos, float range, inout float h)
  31. {
  32. // Compute a random rangle and distance
  33. float dist = rand(h) * range; h = permute(h);
  34. float dir = rand(h) * 6.2831853; h = permute(h);
  35. vec2 pt = dist / image_size;
  36. vec2 o = vec2(cos(dir), sin(dir));
  37. // Sample at quarter-turn intervals around the source pixel
  38. vec4 ref[4];
  39. ref[0] = texture(tex, pos + pt * vec2( o.x, o.y));
  40. ref[1] = texture(tex, pos + pt * vec2(-o.y, o.x));
  41. ref[2] = texture(tex, pos + pt * vec2(-o.x, -o.y));
  42. ref[3] = texture(tex, pos + pt * vec2( o.y, -o.x));
  43. // Return the (normalized) average
  44. return cmul*(ref[0] + ref[1] + ref[2] + ref[3])/4.0;
  45. }
  46. vec4 sample(sampler2D tex, vec2 pos, vec2 tex_size)
  47. {
  48. float h;
  49. // Initialize the PRNG by hashing the position + a random uniform
  50. vec3 m = vec3(pos, random) + vec3(1.0);
  51. h = permute(permute(permute(m.x)+m.y)+m.z);
  52. // Sample the source pixel
  53. vec4 col = cmul*texture(tex, pos);
  54. for (int i = 1; i <= ITERATIONS; i++) {
  55. // Use the average instead if the difference is below the threshold
  56. vec4 avg = average(tex, pos, i*RANGE, h);
  57. vec4 diff = abs(col - avg);
  58. col = mix(avg, col, greaterThan(diff, vec4(THRESHOLD/(i*16384.0))));
  59. }
  60. // Add some random noise to the output
  61. vec3 noise;
  62. noise.x = rand(h); h = permute(h);
  63. noise.y = rand(h); h = permute(h);
  64. noise.z = rand(h); h = permute(h);
  65. col.rgb += (GRAIN/8192.0) * (noise - vec3(0.5));
  66. return col;
  67. }
  68. // vim: set ft=glsl: