17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 10:59:20 +02:00
Files
maszyna/shaders/exposure_average.comp

80 lines
2.6 KiB
Plaintext

// exposure from the luminance histogram (exposure_histogram.comp): the average log2 luminance of the pixels between the
// low and high percentile (the darkest and brightest parts of the image do not move the exposure), adapted over time to,
// faster towards the brighter. the exposure brings the adapted luminance to the key, within the limits. one work group,
// one invocation per bin; the histogram is cleared for the next frame
#include <common>
layout(local_size_x = EXPOSURE_HISTOGRAM_BINS) in;
layout(std430, binding = EXPOSURE_SSBO_BINDING) buffer exposure_ssbo
{
uint histogram[EXPOSURE_HISTOGRAM_BINS];
float adapted_luminance;
float exposure;
float average_luminance;
uint valid;
};
// x: log2 of the lowest luminance binned, y: log2 luminance per bin
layout(location = 0) uniform vec2 log_range;
// x: time since the last frame (s), y: rate of adaptation to a darker image, z: to a brighter one (1/s), w: key
layout(location = 1) uniform vec4 adaptation;
// lowest and highest exposure (multipliers)
layout(location = 2) uniform vec2 exposure_range;
const float LOW_PERCENTILE = 0.4;
const float HIGH_PERCENTILE = 0.95;
shared float shared_counts[EXPOSURE_HISTOGRAM_BINS];
void main()
{
uint index = gl_LocalInvocationIndex;
shared_counts[index] = float(histogram[index]);
histogram[index] = 0U;
barrier();
if (index != 0U)
return;
float total = 0.0;
for (uint bin = 1U; bin < EXPOSURE_HISTOGRAM_BINS; ++bin)
total += shared_counts[bin];
// an all black image (or none) leaves the exposure as it was
if (total < 1.0)
return;
float low = total * LOW_PERCENTILE;
float high = total * HIGH_PERCENTILE;
float counted = 0.0;
float logsum = 0.0;
float weight = 0.0;
for (uint bin = 1U; bin < EXPOSURE_HISTOGRAM_BINS; ++bin)
{
float count = shared_counts[bin];
// the part of the bin between the percentiles
float inside = max(min(counted + count, high) - max(counted, low), 0.0);
counted += count;
float binluminance = log_range.x + (float(bin - 1U) + 0.5) * log_range.y;
logsum += inside * binluminance;
weight += inside;
}
float averagelog = (weight > 0.0) ? logsum / weight : log_range.x;
average_luminance = exp2(averagelog);
if (valid == 0U || !(adapted_luminance > 0.0))
{
adapted_luminance = average_luminance;
valid = 1U;
}
else
{
float adaptedlog = log2(adapted_luminance);
float rate = (averagelog < adaptedlog) ? adaptation.y : adaptation.z;
adaptedlog = mix(adaptedlog, averagelog, 1.0 - exp(-adaptation.x * rate));
adapted_luminance = exp2(adaptedlog);
}
exposure = clamp(adaptation.w / adapted_luminance, exposure_range.x, exposure_range.y);
}