| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- #version 330 core
- // This shader performs downsampling on a texture,
- // as taken from Call Of Duty method, presented at ACM Siggraph 2014.
- // This particular method was customly designed to eliminate
- // "pulsating artifacts and temporal stability issues".
- /* === Varyings === */
- in vec2 vTexCoord;
- /* === Uniforms === */
- uniform sampler2D uTexture;
- uniform vec2 uResolution;
- uniform int uMipLevel; //< Which mip we are writing to, used for Karis average
- uniform vec4 uPrefilter;
- /* === Fragments === */
- layout (location = 0) out vec3 FragDownSample;
- /* === Helper Functions === */
- vec3 LinearToSRGB(vec3 color)
- {
- // color = clamp(color, vec3(0.0), vec3(1.0));
- // const vec3 a = vec3(0.055f);
- // 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)));
- // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
- return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
- }
- float sRGBToLuma(vec3 col)
- {
- //return dot(col, vec3(0.2126, 0.7152, 0.0722));
- return dot(col, vec3(0.299, 0.587, 0.114));
- }
- float KarisAverage(vec3 col)
- {
- // Formula is 1 / (1 + luma)
- float luma = sRGBToLuma(LinearToSRGB(col)) * 0.25f;
- return 1.0f / (1.0f + luma);
- }
- vec3 Prefilter (vec3 col)
- {
- float brightness = max(col.r, max(col.g, col.b));
- float soft = brightness - uPrefilter.y;
- soft = clamp(soft, 0, uPrefilter.z);
- soft = soft * soft * uPrefilter.w;
- float contribution = max(soft, brightness - uPrefilter.x);
- contribution /= max(brightness, 0.00001);
- return col * contribution;
- }
- /* === Main Function === */
- void main()
- {
- // NOTE: This is the readable version of this shader. It will be optimized!
- vec2 srcTexelSize = 1.0 / uResolution;
- float x = srcTexelSize.x;
- float y = srcTexelSize.y;
- // Take 13 samples around current texel:
- // a - b - c
- // - j - k -
- // d - e - f
- // - l - m -
- // g - h - i
- // === ('e' is the current texel) ===
- vec3 a = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y + 2*y)).rgb;
- vec3 b = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y + 2*y)).rgb;
- vec3 c = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y + 2*y)).rgb;
- vec3 d = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y)).rgb;
- vec3 e = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y)).rgb;
- vec3 f = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y)).rgb;
- vec3 g = texture(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y - 2*y)).rgb;
- vec3 h = texture(uTexture, vec2(vTexCoord.x, vTexCoord.y - 2*y)).rgb;
- vec3 i = texture(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y - 2*y)).rgb;
- vec3 j = texture(uTexture, vec2(vTexCoord.x - x, vTexCoord.y + y)).rgb;
- vec3 k = texture(uTexture, vec2(vTexCoord.x + x, vTexCoord.y + y)).rgb;
- vec3 l = texture(uTexture, vec2(vTexCoord.x - x, vTexCoord.y - y)).rgb;
- vec3 m = texture(uTexture, vec2(vTexCoord.x + x, vTexCoord.y - y)).rgb;
- // Apply weighted distribution:
- // 0.5 + 0.125 + 0.125 + 0.125 + 0.125 = 1
- // a,b,d,e * 0.125
- // b,c,e,f * 0.125
- // d,e,g,h * 0.125
- // e,f,h,i * 0.125
- // j,k,l,m * 0.5
- // This shows 5 square areas that are being sampled. But some of them overlap,
- // so to have an energy preserving downsample we need to make some adjustments.
- // The weights are the distributed, so that the sum of j,k,l,m (e.g.)
- // contribute 0.5 to the final color output. The code below is written
- // to effectively yield this sum. We get:
- // 0.125*5 + 0.03125*4 + 0.0625*4 = 1
- // Check if we need to perform Karis average on each block of 4 samples
- vec3 groups[5];
- if (uMipLevel == 0)
- {
- // We are writing to mip 0, so we need to apply Karis average to each block
- // of 4 samples to prevent fireflies (very bright subpixels, leads to pulsating
- // artifacts).
- groups[0] = (a+b+d+e) * (0.125/4.0);
- groups[1] = (b+c+e+f) * (0.125/4.0);
- groups[2] = (d+e+g+h) * (0.125/4.0);
- groups[3] = (e+f+h+i) * (0.125/4.0);
- groups[4] = (j+k+l+m) * (0.5/4.0);
- groups[0] *= KarisAverage(groups[0]);
- groups[1] *= KarisAverage(groups[1]);
- groups[2] *= KarisAverage(groups[2]);
- groups[3] *= KarisAverage(groups[3]);
- groups[4] *= KarisAverage(groups[4]);
- FragDownSample = groups[0]+groups[1]+groups[2]+groups[3]+groups[4];
- FragDownSample = max(FragDownSample, 0.0001);
- FragDownSample = Prefilter(FragDownSample);
- }
- else
- {
- FragDownSample = e*0.125; // ok
- FragDownSample += (a+c+g+i)*0.03125; // ok
- FragDownSample += (b+d+f+h)*0.0625; // ok
- FragDownSample += (j+k+l+m)*0.125; // ok
- }
- }
|