1+ #version 300 es
2+ precision highp float ;
3+
4+ uniform sampler2D u_image0;
5+ uniform float u_float0; // temperature (-100 to 100)
6+ uniform float u_float1; // tint (-100 to 100)
7+ uniform float u_float2; // vibrance (-100 to 100)
8+ uniform float u_float3; // saturation (-100 to 100)
9+
10+ in vec2 v_texCoord;
11+ out vec4 fragColor;
12+
13+ const float INPUT_SCALE = 0.01 ;
14+ const float TEMP_TINT_PRIMARY = 0.3 ;
15+ const float TEMP_TINT_SECONDARY = 0.15 ;
16+ const float VIBRANCE_BOOST = 2.0 ;
17+ const float SATURATION_BOOST = 2.0 ;
18+ const float SKIN_PROTECTION = 0.5 ;
19+ const float EPSILON = 0.001 ;
20+ const vec3 LUMA_WEIGHTS = vec3 (0.299 , 0.587 , 0.114 );
21+
22+ void main() {
23+ vec4 tex = texture(u_image0, v_texCoord);
24+ vec3 color = tex.rgb;
25+
26+ // Scale inputs: -100/100 → -1/1
27+ float temperature = u_float0 * INPUT_SCALE;
28+ float tint = u_float1 * INPUT_SCALE;
29+ float vibrance = u_float2 * INPUT_SCALE;
30+ float saturation = u_float3 * INPUT_SCALE;
31+
32+ // Temperature (warm/cool): positive = warm, negative = cool
33+ color.r += temperature * TEMP_TINT_PRIMARY;
34+ color.b -= temperature * TEMP_TINT_PRIMARY;
35+
36+ // Tint (green/magenta): positive = green, negative = magenta
37+ color.g += tint * TEMP_TINT_PRIMARY;
38+ color.r -= tint * TEMP_TINT_SECONDARY;
39+ color.b -= tint * TEMP_TINT_SECONDARY;
40+
41+ // Single clamp after temperature/tint
42+ color = clamp (color, 0.0 , 1.0 );
43+
44+ // Vibrance with skin protection
45+ if (vibrance != 0.0 ) {
46+ float maxC = max (color.r, max (color.g, color.b));
47+ float minC = min (color.r, min (color.g, color.b));
48+ float sat = maxC - minC;
49+ float gray = dot (color, LUMA_WEIGHTS);
50+
51+ if (vibrance < 0.0 ) {
52+ // Desaturate: -100 → gray
53+ color = mix (vec3 (gray), color, 1.0 + vibrance);
54+ } else {
55+ // Boost less saturated colors more
56+ float vibranceAmt = vibrance * (1.0 - sat);
57+
58+ // Branchless skin tone protection
59+ float isWarmTone = step (color.b, color.g) * step (color.g, color.r);
60+ float warmth = (color.r - color.b) / max (maxC, EPSILON);
61+ float skinTone = isWarmTone * warmth * sat * (1.0 - sat);
62+ vibranceAmt *= (1.0 - skinTone * SKIN_PROTECTION);
63+
64+ color = mix (vec3 (gray), color, 1.0 + vibranceAmt * VIBRANCE_BOOST);
65+ }
66+ }
67+
68+ // Saturation
69+ if (saturation != 0.0 ) {
70+ float gray = dot (color, LUMA_WEIGHTS);
71+ float satMix = saturation < 0.0
72+ ? 1.0 + saturation // -100 → gray
73+ : 1.0 + saturation * SATURATION_BOOST; // +100 → 3x boost
74+ color = mix (vec3 (gray), color, satMix);
75+ }
76+
77+ fragColor = vec4 (clamp (color, 0.0 , 1.0 ), tex.a);
78+ }
0 commit comments