Skip to content

Commit 96d6bd1

Browse files
pythongosssssKosinkadinkhuntcsg
authored
Add GLSL shader node using PyOpenGL (Comfy-Org#12148)
* adds support for executing simple glsl shaders using moderngl package * tidy * Support multiple outputs * Try fix build * fix casing * fix line endings * convert to using PyOpenGL and glfw * remove cpu support * tidy * add additional support for egl & osmesa backends * fix ci perf: only read required outputs * add diagnostics, update mac initialization * GLSL glueprints + node fixes (Comfy-Org#12492) * Add image operation blueprints * Add channels * Add glow * brightness/contrast * hsb * add glsl shader update system * shader nit iteration * add multipass for faster blur * more fixes * rebuild blueprints * print -> logger * Add edge preserving blur * fix: move _initialized flag to end of GLContext.__init__ Prevents '_vao' attribute error when init fails partway through and subsequent calls skip initialization due to early _initialized flag. * update valid ranges - threshold 0-100 - step 0+ * fix value ranges * rebuild node to remove extra inputs * Fix gamma step * clamp saturation in colorize instead of wrapping * Fix crash on 1x1 px images * rework description * remove unnecessary f Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com> Co-authored-by: Hunter Senft-Grupp <hunter@comfy.org>
1 parent 5f21175 commit 96d6bd1

29 files changed

+2155
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#version 300 es
2+
precision highp float;
3+
4+
uniform sampler2D u_image0;
5+
uniform float u_float0; // Brightness slider -100..100
6+
uniform float u_float1; // Contrast slider -100..100
7+
8+
in vec2 v_texCoord;
9+
out vec4 fragColor;
10+
11+
const float MID_GRAY = 0.18; // 18% reflectance
12+
13+
// sRGB gamma 2.2 approximation
14+
vec3 srgbToLinear(vec3 c) {
15+
return pow(max(c, 0.0), vec3(2.2));
16+
}
17+
18+
vec3 linearToSrgb(vec3 c) {
19+
return pow(max(c, 0.0), vec3(1.0/2.2));
20+
}
21+
22+
float mapBrightness(float b) {
23+
return clamp(b / 100.0, -1.0, 1.0);
24+
}
25+
26+
float mapContrast(float c) {
27+
return clamp(c / 100.0 + 1.0, 0.0, 2.0);
28+
}
29+
30+
void main() {
31+
vec4 orig = texture(u_image0, v_texCoord);
32+
33+
float brightness = mapBrightness(u_float0);
34+
float contrast = mapContrast(u_float1);
35+
36+
vec3 lin = srgbToLinear(orig.rgb);
37+
38+
lin = (lin - MID_GRAY) * contrast + brightness + MID_GRAY;
39+
40+
// Convert back to sRGB
41+
vec3 result = linearToSrgb(clamp(lin, 0.0, 1.0));
42+
43+
fragColor = vec4(result, orig.a);
44+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#version 300 es
2+
precision highp float;
3+
4+
uniform sampler2D u_image0;
5+
uniform vec2 u_resolution;
6+
uniform int u_int0; // Mode
7+
uniform float u_float0; // Amount (0 to 100)
8+
9+
in vec2 v_texCoord;
10+
out vec4 fragColor;
11+
12+
const int MODE_LINEAR = 0;
13+
const int MODE_RADIAL = 1;
14+
const int MODE_BARREL = 2;
15+
const int MODE_SWIRL = 3;
16+
const int MODE_DIAGONAL = 4;
17+
18+
const float AMOUNT_SCALE = 0.0005;
19+
const float RADIAL_MULT = 4.0;
20+
const float BARREL_MULT = 8.0;
21+
const float INV_SQRT2 = 0.70710678118;
22+
23+
void main() {
24+
vec2 uv = v_texCoord;
25+
vec4 original = texture(u_image0, uv);
26+
27+
float amount = u_float0 * AMOUNT_SCALE;
28+
29+
if (amount < 0.000001) {
30+
fragColor = original;
31+
return;
32+
}
33+
34+
// Aspect-corrected coordinates for circular effects
35+
float aspect = u_resolution.x / u_resolution.y;
36+
vec2 centered = uv - 0.5;
37+
vec2 corrected = vec2(centered.x * aspect, centered.y);
38+
float r = length(corrected);
39+
vec2 dir = r > 0.0001 ? corrected / r : vec2(0.0);
40+
vec2 offset = vec2(0.0);
41+
42+
if (u_int0 == MODE_LINEAR) {
43+
// Horizontal shift (no aspect correction needed)
44+
offset = vec2(amount, 0.0);
45+
}
46+
else if (u_int0 == MODE_RADIAL) {
47+
// Outward from center, stronger at edges
48+
offset = dir * r * amount * RADIAL_MULT;
49+
offset.x /= aspect; // Convert back to UV space
50+
}
51+
else if (u_int0 == MODE_BARREL) {
52+
// Lens distortion simulation (r² falloff)
53+
offset = dir * r * r * amount * BARREL_MULT;
54+
offset.x /= aspect; // Convert back to UV space
55+
}
56+
else if (u_int0 == MODE_SWIRL) {
57+
// Perpendicular to radial (rotational aberration)
58+
vec2 perp = vec2(-dir.y, dir.x);
59+
offset = perp * r * amount * RADIAL_MULT;
60+
offset.x /= aspect; // Convert back to UV space
61+
}
62+
else if (u_int0 == MODE_DIAGONAL) {
63+
// 45° offset (no aspect correction needed)
64+
offset = vec2(amount, amount) * INV_SQRT2;
65+
}
66+
67+
float red = texture(u_image0, uv + offset).r;
68+
float green = original.g;
69+
float blue = texture(u_image0, uv - offset).b;
70+
71+
fragColor = vec4(red, green, blue, original.a);
72+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#version 300 es
2+
precision highp float;
3+
4+
uniform sampler2D u_image0;
5+
uniform float u_float0; // Blur radius (0–20, default ~5)
6+
uniform float u_float1; // Edge threshold (0–100, default ~30)
7+
uniform int u_int0; // Step size (0/1 = every pixel, 2+ = skip pixels)
8+
9+
in vec2 v_texCoord;
10+
out vec4 fragColor;
11+
12+
const int MAX_RADIUS = 20;
13+
const float EPSILON = 0.0001;
14+
15+
// Perceptual luminance
16+
float getLuminance(vec3 rgb) {
17+
return dot(rgb, vec3(0.299, 0.587, 0.114));
18+
}
19+
20+
vec4 bilateralFilter(vec2 uv, vec2 texelSize, int radius,
21+
float sigmaSpatial, float sigmaColor)
22+
{
23+
vec4 center = texture(u_image0, uv);
24+
vec3 centerRGB = center.rgb;
25+
26+
float invSpatial2 = -0.5 / (sigmaSpatial * sigmaSpatial);
27+
float invColor2 = -0.5 / (sigmaColor * sigmaColor + EPSILON);
28+
29+
vec3 sumRGB = vec3(0.0);
30+
float sumWeight = 0.0;
31+
32+
int step = max(u_int0, 1);
33+
float radius2 = float(radius * radius);
34+
35+
for (int dy = -MAX_RADIUS; dy <= MAX_RADIUS; dy++) {
36+
if (dy < -radius || dy > radius) continue;
37+
if (abs(dy) % step != 0) continue;
38+
39+
for (int dx = -MAX_RADIUS; dx <= MAX_RADIUS; dx++) {
40+
if (dx < -radius || dx > radius) continue;
41+
if (abs(dx) % step != 0) continue;
42+
43+
vec2 offset = vec2(float(dx), float(dy));
44+
float dist2 = dot(offset, offset);
45+
if (dist2 > radius2) continue;
46+
47+
vec3 sampleRGB = texture(u_image0, uv + offset * texelSize).rgb;
48+
49+
// Spatial Gaussian
50+
float spatialWeight = exp(dist2 * invSpatial2);
51+
52+
// Perceptual color distance (weighted RGB)
53+
vec3 diff = sampleRGB - centerRGB;
54+
float colorDist = dot(diff * diff, vec3(0.299, 0.587, 0.114));
55+
float colorWeight = exp(colorDist * invColor2);
56+
57+
float w = spatialWeight * colorWeight;
58+
sumRGB += sampleRGB * w;
59+
sumWeight += w;
60+
}
61+
}
62+
63+
vec3 resultRGB = sumRGB / max(sumWeight, EPSILON);
64+
return vec4(resultRGB, center.a); // preserve center alpha
65+
}
66+
67+
void main() {
68+
vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0));
69+
70+
float radiusF = clamp(u_float0, 0.0, float(MAX_RADIUS));
71+
int radius = int(radiusF + 0.5);
72+
73+
if (radius == 0) {
74+
fragColor = texture(u_image0, v_texCoord);
75+
return;
76+
}
77+
78+
// Edge threshold → color sigma
79+
// Squared curve for better low-end control
80+
float t = clamp(u_float1, 0.0, 100.0) / 100.0;
81+
t *= t;
82+
float sigmaColor = mix(0.01, 0.5, t);
83+
84+
// Spatial sigma tied to radius
85+
float sigmaSpatial = max(radiusF * 0.75, 0.5);
86+
87+
fragColor = bilateralFilter(
88+
v_texCoord,
89+
texelSize,
90+
radius,
91+
sigmaSpatial,
92+
sigmaColor
93+
);
94+
}

0 commit comments

Comments
 (0)