#version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D InputTexture; uniform float vibranceValue; // -100 to 100 const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722); // Check if a color is potentially a skin tone (approximate) float skinToneLikelihood(vec3 color) { // Simple skin tone detection - check if in common skin tone range // Based on normalized r/g ratio in RGB space float total = color.r + color.g + color.b; if (total < 0.001) return 0.0; vec3 normalized = color / total; // Detect skin tones based on red-green ratio and absolute red value bool redEnough = normalized.r > 0.35; bool redGreenRatio = normalized.r / normalized.g > 1.1 && normalized.r / normalized.g < 2.0; bool notTooBlue = normalized.b < 0.4; return (redEnough && redGreenRatio && notTooBlue) ? 1.0 - pow(normalized.b * 1.5, 2.0) : 0.0; } vec3 applyVibrance(vec3 color, float vibrance) { float vibAmount = vibrance / 100.0; // Map to -1..1 // Calculate better saturation float luma = dot(color, luminanceWeight); vec3 chroma = max(color - luma, 0.0); float sat = length(chroma) / (luma + 0.001); // Get skin tone protection factor float skinFactor = skinToneLikelihood(color); // Calculate adjustment strength based on current saturation // Less effect on already highly saturated colors float satWeight = 1.0 - smoothstep(0.2, 0.8, sat); // Apply less vibrance to skin tones float adjustmentFactor = satWeight * (1.0 - skinFactor * 0.7); // Create non-linear response curve for natural-looking adjustment float strength = vibAmount > 0.0 ? vibAmount * (1.0 - pow(sat, 2.0)) // Positive vibrance : vibAmount; // Negative vibrance (desaturation) // Fine-tune the saturation component-wise to preserve color relationships vec3 satColor = color; if (abs(vibAmount) > 0.001) { // Get distance from gray for each channel vec3 dist = color - luma; // Adjust distance based on vibrance dist *= (1.0 + strength * adjustmentFactor); // Rebuild color from luma + adjusted chroma satColor = luma + dist; // Preserve color ratios for extreme adjustments if (vibAmount > 0.5) { float maxComponent = max(satColor.r, max(satColor.g, satColor.b)); if (maxComponent > 1.0) { float scale = min(1.0 / maxComponent, 2.0); // Limit scaling satColor = luma + (satColor - luma) * scale; } } } return satColor; } void main() { vec4 color = texture(InputTexture, TexCoord); color.rgb = applyVibrance(color.rgb, vibranceValue); FragColor = vec4(max(color.rgb, vec3(0.0)), color.a); }