105 lines
3.3 KiB
GLSL
105 lines
3.3 KiB
GLSL
// Input, output blocks
|
|
VERTEX_EXPORT VertexData {
|
|
vec2 uv;
|
|
} VertexOut;
|
|
|
|
#if VERTEX_SHADER
|
|
|
|
layout(location = 0) in vec3 aPos;
|
|
|
|
void main() {
|
|
gl_Position = vec4(aPos, 1);
|
|
VertexOut.uv = aPos.xy * 0.5 + 0.5;
|
|
}
|
|
#endif // VERTEX_SHADER
|
|
|
|
#if FRAGMENT_SHADER
|
|
|
|
|
|
layout(binding = 0) uniform sampler2D src_sampler;
|
|
layout(location = 19) uniform int src_mip_level;
|
|
|
|
layout(location = 0) out vec3 downsample;
|
|
|
|
const float invGamma = 1.0 / 2.2;
|
|
vec3 toSRGB(vec3 v) { return pow(v, vec3(invGamma)); }
|
|
|
|
float RGBToLuminance(vec3 col)
|
|
{
|
|
return dot(col, vec3(0.2126f, 0.7152f, 0.0722f));
|
|
}
|
|
|
|
float karisAverage(vec3 col)
|
|
{
|
|
// Formula is 1 / (1 + luma)
|
|
float luma = RGBToLuminance(toSRGB(col)) * 0.25f;
|
|
return 1.0f / (1.0f + luma);
|
|
}
|
|
|
|
void main() {
|
|
vec2 uv = VertexOut.uv;
|
|
|
|
// 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 = textureLodOffset(src_sampler, uv, src_mip_level, ivec2(-2, 2)).rgb;
|
|
vec3 b = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 0, 2)).rgb;
|
|
vec3 c = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 2, 2)).rgb;
|
|
|
|
vec3 d = textureLodOffset(src_sampler, uv, src_mip_level, ivec2(-2, 0)).rgb;
|
|
vec3 e = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 0, 0)).rgb;
|
|
vec3 f = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 2, 0)).rgb;
|
|
|
|
vec3 g = textureLodOffset(src_sampler, uv, src_mip_level, ivec2(-2,-2)).rgb;
|
|
vec3 h = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 0,-2)).rgb;
|
|
vec3 i = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 2,-2)).rgb;
|
|
|
|
vec3 j = textureLodOffset(src_sampler, uv, src_mip_level, ivec2(-1, 1)).rgb;
|
|
vec3 k = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 1, 1)).rgb;
|
|
vec3 l = textureLodOffset(src_sampler, uv, src_mip_level, ivec2(-1,-1)).rgb;
|
|
vec3 m = textureLodOffset(src_sampler, uv, src_mip_level, ivec2( 1,-1)).rgb;
|
|
|
|
if (src_mip_level == 0) {
|
|
// Use Karis average to get rid of fireflies
|
|
|
|
vec3 group1 = (a + b + d + e) * (0.125 / 4);
|
|
vec3 group2 = (b + c + e + f) * (0.125 / 4);
|
|
vec3 group3 = (d + e + g + h) * (0.125 / 4);
|
|
vec3 group4 = (e + f + h + i) * (0.125 / 4);
|
|
vec3 group5 = (j + k + l + m) * (0.5 / 4);
|
|
group1 *= karisAverage(group1);
|
|
group2 *= karisAverage(group2);
|
|
group3 *= karisAverage(group3);
|
|
group4 *= karisAverage(group4);
|
|
group5 *= karisAverage(group5);
|
|
downsample = group1 + group2 + group3 + group4 + group5;
|
|
} else {
|
|
// 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
|
|
downsample = e*0.125;
|
|
downsample += (a+c+g+i)*0.03125;
|
|
downsample += (b+d+f+h)*0.0625;
|
|
downsample += (j+k+l+m)*0.125;
|
|
}
|
|
downsample = max(downsample, 0.001f);
|
|
}
|
|
|
|
|
|
#endif // FRAGMNET_SHADER
|