44 lines
1.4 KiB
GLSL
44 lines
1.4 KiB
GLSL
#version 330 core
|
|
out vec4 FragColor;
|
|
in vec2 TexCoord;
|
|
|
|
uniform sampler2D InputTexture;
|
|
uniform float saturationValue; // -100 (grayscale) to 100 (double sat)
|
|
|
|
// Perceptual luminance weights (Rec. 709)
|
|
const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722);
|
|
|
|
vec3 applySaturation(vec3 color, float saturation) {
|
|
// Get original luminance
|
|
float lum = dot(color, luminanceWeight);
|
|
|
|
// Skip processing for very dark or very bright pixels
|
|
if (lum < 0.001 || lum > 0.999) return color;
|
|
|
|
// Non-linear saturation response curve (more natural-looking)
|
|
float factor;
|
|
if (saturation >= 0.0) {
|
|
// Positive saturation with highlight protection
|
|
factor = 1.0 + (saturation / 100.0) * (1.0 - 0.3 * smoothstep(0.7, 1.0, lum));
|
|
} else {
|
|
// Negative saturation with shadow protection
|
|
factor = 1.0 + (saturation / 100.0) * (1.0 - 0.1 * smoothstep(0.0, 0.2, lum));
|
|
}
|
|
|
|
// Apply saturation while preserving luminance
|
|
vec3 adjusted = mix(vec3(lum), color, factor);
|
|
|
|
// Ensure we maintain original luminance exactly
|
|
float newLum = dot(adjusted, luminanceWeight);
|
|
return adjusted * (lum / max(newLum, 0.001));
|
|
}
|
|
|
|
void main() {
|
|
vec4 color = texture(InputTexture, TexCoord);
|
|
|
|
// Apply saturation
|
|
color.rgb = applySaturation(color.rgb, saturationValue);
|
|
|
|
// Proper clamping to valid range
|
|
FragColor = vec4(clamp(color.rgb, 0.0, 1.0), color.a);
|
|
} |