#version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D InputTexture; uniform float exposureValue; // Expecting value in stops, e.g., -5.0 to 5.0 // Calculate perceptual luminance float luminance(vec3 color) { return dot(color, vec3(0.2126, 0.7152, 0.0722)); } // Apply exposure by adjusting luminance while preserving color relationships vec3 applyExposure(vec3 color, float exposureStops) { // Get original luminance float lum = luminance(color); // Skip processing for very dark pixels to avoid division by zero if (lum < 0.0001) return color; // Calculate exposure factor float exposureFactor = pow(2.0, exposureStops); // Apply highlight compression when increasing exposure float newLum = lum * exposureFactor; if (exposureStops > 0.0 && newLum > 0.8) { // Soft highlight roll-off to prevent harsh clipping float excess = newLum - 0.8; newLum = 0.8 + 0.2 * (1.0 - exp(-excess * 5.0)); } // Scale RGB proportionally to maintain color relationships return color * (newLum / lum); } void main() { vec4 color = texture(InputTexture, TexCoord); // Apply exposure adjustment color.rgb = applyExposure(color.rgb, exposureValue); // Ensure output is in valid range FragColor = vec4(clamp(color.rgb, vec3(0.0), vec3(1.0)), color.a); }