shader nit iteration

This commit is contained in:
pythongosssss 2026-01-31 16:03:47 -08:00
parent 0050b66a0b
commit 292a5918f4
16 changed files with 113 additions and 127 deletions

View File

@ -8,14 +8,15 @@ uniform float u_float1; // Contrast slider -100..100
in vec2 v_texCoord; in vec2 v_texCoord;
out vec4 fragColor; out vec4 fragColor;
const float EPSILON = 1e-5; const float MID_GRAY = 0.18; // 18% reflectance
// sRGB gamma 2.2 approximation
vec3 srgbToLinear(vec3 c) { vec3 srgbToLinear(vec3 c) {
return pow(c, vec3(2.2)); return pow(max(c, 0.0), vec3(2.2));
} }
vec3 linearToSrgb(vec3 c) { vec3 linearToSrgb(vec3 c) {
return pow(c, vec3(1.0/2.2)); return pow(max(c, 0.0), vec3(1.0/2.2));
} }
float mapBrightness(float b) { float mapBrightness(float b) {
@ -32,15 +33,9 @@ void main() {
float brightness = mapBrightness(u_float0); float brightness = mapBrightness(u_float0);
float contrast = mapContrast(u_float1); float contrast = mapContrast(u_float1);
// Early exit if no adjustment
if (abs(brightness) < EPSILON && abs(contrast - 1.0) < EPSILON) {
fragColor = orig;
return;
}
vec3 lin = srgbToLinear(orig.rgb); vec3 lin = srgbToLinear(orig.rgb);
lin = (lin - 0.5) * contrast + brightness + 0.5; lin = (lin - MID_GRAY) * contrast + brightness + MID_GRAY;
// Convert back to sRGB // Convert back to sRGB
vec3 result = linearToSrgb(clamp(lin, 0.0, 1.0)); vec3 result = linearToSrgb(clamp(lin, 0.0, 1.0));

View File

@ -14,21 +14,26 @@ const int MODE_BARREL = 2;
const int MODE_SWIRL = 3; const int MODE_SWIRL = 3;
const int MODE_DIAGONAL = 4; const int MODE_DIAGONAL = 4;
const float AMOUNT_SCALE = 0.0005;
const float RADIAL_MULT = 4.0;
const float BARREL_MULT = 8.0;
const float INV_SQRT2 = 0.70710678118;
void main() { void main() {
vec2 uv = v_texCoord; vec2 uv = v_texCoord;
vec4 original = texture(u_image0, uv); vec4 original = texture(u_image0, uv);
float amount = u_float0 * 0.0005; float amount = u_float0 * AMOUNT_SCALE;
if (amount == 0.0) { if (amount < 0.000001) {
fragColor = original; fragColor = original;
return; return;
} }
vec2 centered = uv - 0.5; vec2 centered = uv - 0.5;
float r = length(centered); float r = length(centered);
vec2 dir = normalize(centered + 0.001); vec2 dir = r > 0.0001 ? centered / r : vec2(0.0);
vec2 offset; vec2 offset = vec2(0.0);
if (u_int0 == MODE_LINEAR) { if (u_int0 == MODE_LINEAR) {
// Horizontal shift // Horizontal shift
@ -36,20 +41,20 @@ void main() {
} }
else if (u_int0 == MODE_RADIAL) { else if (u_int0 == MODE_RADIAL) {
// Outward from center, stronger at edges // Outward from center, stronger at edges
offset = dir * r * amount * 4.0; offset = dir * r * amount * RADIAL_MULT;
} }
else if (u_int0 == MODE_BARREL) { else if (u_int0 == MODE_BARREL) {
// Lens distortion simulation (r² falloff) // Lens distortion simulation (r² falloff)
offset = dir * r * r * amount * 8.0; offset = dir * r * r * amount * BARREL_MULT;
} }
else if (u_int0 == MODE_SWIRL) { else if (u_int0 == MODE_SWIRL) {
// Perpendicular to radial (rotational aberration) // Perpendicular to radial (rotational aberration)
vec2 perp = vec2(-dir.y, dir.x); vec2 perp = vec2(-dir.y, dir.x);
offset = perp * r * amount * 4.0; offset = perp * r * amount * RADIAL_MULT;
} }
else if (u_int0 == MODE_DIAGONAL) { else if (u_int0 == MODE_DIAGONAL) {
// 45° offset // 45° offset
offset = vec2(amount, amount) * 0.707; offset = vec2(amount, amount) * INV_SQRT2;
} }
float red = texture(u_image0, uv + offset).r; float red = texture(u_image0, uv + offset).r;

View File

@ -10,31 +10,43 @@ uniform float u_float3; // saturation (-100 to 100)
in vec2 v_texCoord; in vec2 v_texCoord;
out vec4 fragColor; out vec4 fragColor;
const float INPUT_SCALE = 0.01;
const float TEMP_TINT_PRIMARY = 0.3;
const float TEMP_TINT_SECONDARY = 0.15;
const float VIBRANCE_BOOST = 2.0;
const float SATURATION_BOOST = 2.0;
const float SKIN_PROTECTION = 0.5;
const float EPSILON = 0.001;
const vec3 LUMA_WEIGHTS = vec3(0.299, 0.587, 0.114);
void main() { void main() {
vec4 tex = texture(u_image0, v_texCoord); vec4 tex = texture(u_image0, v_texCoord);
vec3 color = tex.rgb; vec3 color = tex.rgb;
// Scale inputs: -100/100 → -1/1 // Scale inputs: -100/100 → -1/1
float temperature = u_float0 * 0.01; float temperature = u_float0 * INPUT_SCALE;
float tint = u_float1 * 0.01; float tint = u_float1 * INPUT_SCALE;
float vibrance = u_float2 * 0.01; float vibrance = u_float2 * INPUT_SCALE;
float saturation = u_float3 * 0.01; float saturation = u_float3 * INPUT_SCALE;
// Temperature (warm/cool): positive = warm, negative = cool // Temperature (warm/cool): positive = warm, negative = cool
color.r = clamp(color.r + temperature * 0.3, 0.0, 1.0); color.r += temperature * TEMP_TINT_PRIMARY;
color.b = clamp(color.b - temperature * 0.3, 0.0, 1.0); color.b -= temperature * TEMP_TINT_PRIMARY;
// Tint (green/magenta): positive = green, negative = magenta // Tint (green/magenta): positive = green, negative = magenta
color.g = clamp(color.g + tint * 0.3, 0.0, 1.0); color.g += tint * TEMP_TINT_PRIMARY;
color.r = clamp(color.r - tint * 0.15, 0.0, 1.0); color.r -= tint * TEMP_TINT_SECONDARY;
color.b = clamp(color.b - tint * 0.15, 0.0, 1.0); color.b -= tint * TEMP_TINT_SECONDARY;
// Vibrance Pro with skin protection // Single clamp after temperature/tint
color = clamp(color, 0.0, 1.0);
// Vibrance with skin protection
if (vibrance != 0.0) { if (vibrance != 0.0) {
float maxC = max(color.r, max(color.g, color.b)); float maxC = max(color.r, max(color.g, color.b));
float minC = min(color.r, min(color.g, color.b)); float minC = min(color.r, min(color.g, color.b));
float sat = maxC - minC; float sat = maxC - minC;
float gray = dot(color, vec3(0.299, 0.587, 0.114)); float gray = dot(color, LUMA_WEIGHTS);
if (vibrance < 0.0) { if (vibrance < 0.0) {
// Desaturate: -100 → gray // Desaturate: -100 → gray
@ -43,24 +55,22 @@ void main() {
// Boost less saturated colors more // Boost less saturated colors more
float vibranceAmt = vibrance * (1.0 - sat); float vibranceAmt = vibrance * (1.0 - sat);
// Skin tone protection (hardcoded 0.5) // Branchless skin tone protection
float skinTone = 0.0; float isWarmTone = step(color.b, color.g) * step(color.g, color.r);
if (color.r > color.g && color.g > color.b) { float warmth = (color.r - color.b) / max(maxC, EPSILON);
float warmth = (color.r - color.b) / max(maxC, 0.001); float skinTone = isWarmTone * warmth * sat * (1.0 - sat);
skinTone = warmth * sat * (1.0 - sat); vibranceAmt *= (1.0 - skinTone * SKIN_PROTECTION);
}
vibranceAmt *= (1.0 - skinTone * 0.5);
color = mix(vec3(gray), color, 1.0 + vibranceAmt * 2.0); color = mix(vec3(gray), color, 1.0 + vibranceAmt * VIBRANCE_BOOST);
} }
} }
// Saturation // Saturation
if (saturation != 0.0) { if (saturation != 0.0) {
float gray = dot(color, vec3(0.299, 0.587, 0.114)); float gray = dot(color, LUMA_WEIGHTS);
float satMix = saturation < 0.0 float satMix = saturation < 0.0
? 1.0 + saturation // -100 → gray ? 1.0 + saturation // -100 → gray
: 1.0 + saturation * 2.0; // +100 → 3x boost : 1.0 + saturation * SATURATION_BOOST; // +100 → 3x boost
color = mix(vec3(gray), color, satMix); color = mix(vec3(gray), color, satMix);
} }

View File

@ -86,7 +86,7 @@ void main() {
float luma = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); float luma = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
// Grain UV (resolution-independent) // Grain UV (resolution-independent)
vec2 grainUV = v_texCoord * u_resolution / max(u_float1, 0.001); vec2 grainUV = v_texCoord * u_resolution / max(u_float1, 0.01);
uvec2 grainPixel = uvec2(grainUV); uvec2 grainPixel = uvec2(grainUV);
float g; float g;
@ -120,5 +120,5 @@ void main() {
vec3 grainColor = mix(vec3(g), grainRGB, clamp(u_float2, 0.0, 1.0)); vec3 grainColor = mix(vec3(g), grainRGB, clamp(u_float2, 0.0, 1.0));
color.rgb += grainColor * strength * lumWeight; color.rgb += grainColor * strength * lumWeight;
fragColor0 = vec4(color.rgb, color.a); fragColor0 = vec4(clamp(color.rgb, 0.0, 1.0), color.a);
} }

View File

@ -76,6 +76,7 @@ void main() {
float t1 = threshold + 0.15; float t1 = threshold + 0.15;
vec2 texelSize = 1.0 / u_resolution; vec2 texelSize = 1.0 / u_resolution;
float aspect = u_resolution.x / u_resolution.y;
float radius2 = radius * radius; float radius2 = radius * radius;
float sampleScale = clamp(radius * 0.75, 0.35, 1.0); float sampleScale = clamp(radius * 0.75, 0.35, 1.0);
@ -103,7 +104,7 @@ void main() {
float fi = float(i); float fi = float(i);
float dist = sqrt(fi / float(samples)) * radius * radiusJitter; float dist = sqrt(fi / float(samples)) * radius * radiusJitter;
vec2 offset = dir * dist * texelSize; vec2 offset = dir * dist * texelSize * vec2(1.0, aspect);
vec3 c = texture(u_image0, v_texCoord + offset).rgb; vec3 c = texture(u_image0, v_texCoord + offset).rgb;
float mask = smoothstep(t0, t1, dot(c, LUMA)); float mask = smoothstep(t0, t1, dot(c, LUMA));
@ -120,7 +121,7 @@ void main() {
); );
} }
glow *= intensity / totalWeight; glow *= intensity / max(totalWeight, 0.001);
if (u_int1 > 0) { if (u_int1 > 0) {
glow *= hexToRgb(u_int1); glow *= hexToRgb(u_int1);

View File

@ -106,23 +106,8 @@ vec3 rgb2hsb(vec3 c) {
} }
vec3 hsb2rgb(vec3 hsb) { vec3 hsb2rgb(vec3 hsb) {
float h = hsb.x * 6.0; vec3 rgb = clamp(abs(mod(hsb.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
float s = hsb.y; return hsb.z * mix(vec3(1.0), rgb, hsb.y);
float b = hsb.z;
float c = b * s;
float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));
float m = b - c;
vec3 rgb;
if (h < 1.0) rgb = vec3(c, x, 0.0);
else if (h < 2.0) rgb = vec3(x, c, 0.0);
else if (h < 3.0) rgb = vec3(0.0, c, x);
else if (h < 4.0) rgb = vec3(0.0, x, c);
else if (h < 5.0) rgb = vec3(x, 0.0, c);
else rgb = vec3(c, 0.0, x);
return rgb + m;
} }
//============================================================================= //=============================================================================
@ -149,16 +134,6 @@ float getHueWeight(float hue, float center, float overlap) {
float getModeWeight(float hue, int mode, float overlap) { float getModeWeight(float hue, int mode, float overlap) {
if (mode == MODE_MASTER || mode == MODE_COLORIZE) return 1.0; if (mode == MODE_MASTER || mode == MODE_COLORIZE) return 1.0;
float centers[6];
centers[0] = 0.0;
centers[1] = 1.0/6.0;
centers[2] = 2.0/6.0;
centers[3] = 3.0/6.0;
centers[4] = 4.0/6.0;
centers[5] = 5.0/6.0;
int idx = mode - 1;
if (mode == MODE_RED) { if (mode == MODE_RED) {
return max( return max(
getHueWeight(hue, 0.0, overlap), getHueWeight(hue, 0.0, overlap),
@ -166,7 +141,8 @@ float getModeWeight(float hue, int mode, float overlap) {
); );
} }
return getHueWeight(hue, centers[idx], overlap); float center = float(mode - 1) / 6.0;
return getHueWeight(hue, center, overlap);
} }
//============================================================================= //=============================================================================

View File

@ -25,60 +25,66 @@ float gaussian(float x, float sigma) {
void main() { void main() {
vec2 texelSize = 1.0 / u_resolution; vec2 texelSize = 1.0 / u_resolution;
float radius = max(u_float0, 0.0); float radius = max(u_float0, 0.0);
// Radial (angular) blur // Radial (angular) blur with incremental rotation
if (u_int0 == BLUR_RADIAL) { if (u_int0 == BLUR_RADIAL) {
vec2 center = vec2(0.5); vec2 center = vec2(0.5);
vec2 dir = v_texCoord - center; vec2 dir = v_texCoord - center;
float dist = length(dir); float dist = length(dir);
// Avoid division by zero
if (dist < 1e-4) { if (dist < 1e-4) {
fragColor0 = texture(u_image0, v_texCoord); fragColor0 = texture(u_image0, v_texCoord);
return; return;
} }
vec4 sum = vec4(0.0); vec4 sum = vec4(0.0);
float totalWeight = 0.0; float totalWeight = 0.0;
float angleStep = radius * RADIAL_STRENGTH; float angleStep = radius * RADIAL_STRENGTH;
dir /= dist; dir /= dist;
float cosStep = cos(angleStep);
float sinStep = sin(angleStep);
float negAngle = -float(RADIAL_SAMPLES) * angleStep;
vec2 rotDir = vec2(
dir.x * cos(negAngle) - dir.y * sin(negAngle),
dir.x * sin(negAngle) + dir.y * cos(negAngle)
);
for (int i = -RADIAL_SAMPLES; i <= RADIAL_SAMPLES; i++) { for (int i = -RADIAL_SAMPLES; i <= RADIAL_SAMPLES; i++) {
float a = float(i) * angleStep; vec2 uv = center + rotDir * dist;
float s = sin(a);
float c = cos(a);
vec2 rotatedDir = vec2(
dir.x * c - dir.y * s,
dir.x * s + dir.y * c
);
vec2 uv = center + rotatedDir * dist;
float w = 1.0 - abs(float(i)) / float(RADIAL_SAMPLES); float w = 1.0 - abs(float(i)) / float(RADIAL_SAMPLES);
sum += texture(u_image0, uv) * w; sum += texture(u_image0, uv) * w;
totalWeight += w; totalWeight += w;
rotDir = vec2(
rotDir.x * cosStep - rotDir.y * sinStep,
rotDir.x * sinStep + rotDir.y * cosStep
);
} }
fragColor0 = sum / totalWeight; fragColor0 = sum / max(totalWeight, 0.001);
return; return;
} }
// Gaussian / Box blur // Gaussian / Box blur (grid sampling)
int samples = int(ceil(radius)); int samples = int(ceil(radius));
if (samples == 0) { if (samples == 0) {
fragColor0 = texture(u_image0, v_texCoord); fragColor0 = texture(u_image0, v_texCoord);
return; return;
} }
vec4 color = vec4(0.0); vec4 color = vec4(0.0);
float totalWeight = 0.0; float totalWeight = 0.0;
float sigma = radius / 2.0; float sigma = radius / 2.0;
for (int x = -samples; x <= samples; x++) { for (int x = -samples; x <= samples; x++) {
for (int y = -samples; y <= samples; y++) { for (int y = -samples; y <= samples; y++) {
vec2 offset = vec2(float(x), float(y)) * texelSize; vec2 offset = vec2(float(x), float(y)) * texelSize;
vec4 sample_color = texture(u_image0, v_texCoord + offset); vec4 sample_color = texture(u_image0, v_texCoord + offset);
float weight; float weight;
if (u_int0 == BLUR_GAUSSIAN) { if (u_int0 == BLUR_GAUSSIAN) {
float dist = length(vec2(float(x), float(y))); float dist = length(vec2(float(x), float(y)));
@ -87,11 +93,11 @@ void main() {
// BLUR_BOX // BLUR_BOX
weight = 1.0; weight = 1.0;
} }
color += sample_color * weight; color += sample_color * weight;
totalWeight += weight; totalWeight += weight;
} }
} }
fragColor0 = color / totalWeight; fragColor0 = color / totalWeight;
} }

View File

@ -3,9 +3,9 @@ precision highp float;
uniform sampler2D u_image0; uniform sampler2D u_image0;
uniform vec2 u_resolution; uniform vec2 u_resolution;
uniform float u_float0; // amount [0.0 3.0] typical: 0.51.5 uniform float u_float0; // amount [0.0 - 3.0] typical: 0.5-1.5
uniform float u_float1; // radius [0.5 10.0] blur radius in pixels uniform float u_float1; // radius [0.5 - 10.0] blur radius in pixels
uniform float u_float2; // threshold [0.0 0.1] min difference to sharpen uniform float u_float2; // threshold [0.0 - 0.1] min difference to sharpen
in vec2 v_texCoord; in vec2 v_texCoord;
layout(location = 0) out vec4 fragColor0; layout(location = 0) out vec4 fragColor0;
@ -23,27 +23,21 @@ void main() {
float radius = max(u_float1, 0.5); float radius = max(u_float1, 0.5);
float amount = u_float0; float amount = u_float0;
float threshold = u_float2; float threshold = u_float2;
vec4 original = texture(u_image0, v_texCoord); vec4 original = texture(u_image0, v_texCoord);
// Gaussian blur for the "unsharp" mask // Gaussian blur for the "unsharp" mask
int samples = int(ceil(radius)); int samples = int(ceil(radius));
float sigma = radius / 2.0; float sigma = radius / 2.0;
vec4 blurred = vec4(0.0); vec4 blurred = vec4(0.0);
float totalWeight = 0.0; float totalWeight = 0.0;
for (int x = -samples; x <= samples; x++) { for (int x = -samples; x <= samples; x++) {
for (int y = -samples; y <= samples; y++) { for (int y = -samples; y <= samples; y++) {
vec2 offset = vec2(float(x), float(y)) * texel; vec2 offset = vec2(float(x), float(y)) * texel;
vec2 sampleCoord = v_texCoord + offset; vec2 sampleCoord = v_texCoord + offset;
// Boundary check - skip out-of-bounds samples
if (sampleCoord.x < 0.0 || sampleCoord.x > 1.0 ||
sampleCoord.y < 0.0 || sampleCoord.y > 1.0) {
continue;
}
float dist = length(vec2(float(x), float(y))); float dist = length(vec2(float(x), float(y)));
float weight = gaussian(dist, sigma); float weight = gaussian(dist, sigma);
blurred += texture(u_image0, sampleCoord) * weight; blurred += texture(u_image0, sampleCoord) * weight;
@ -51,18 +45,17 @@ void main() {
} }
} }
blurred /= totalWeight; blurred /= totalWeight;
// Unsharp mask = original - blurred // Unsharp mask = original - blurred
vec3 mask = original.rgb - blurred.rgb; vec3 mask = original.rgb - blurred.rgb;
// Luminance-based threshold (Photoshop-style) // Luminance-based threshold with smooth falloff
float lumaDelta = abs(getLuminance(original.rgb) - getLuminance(blurred.rgb)); float lumaDelta = abs(getLuminance(original.rgb) - getLuminance(blurred.rgb));
if (lumaDelta < threshold) { float thresholdScale = smoothstep(0.0, threshold, lumaDelta);
mask = vec3(0.0); mask *= thresholdScale;
}
// Sharpen: original + mask * amount // Sharpen: original + mask * amount
vec3 sharpened = original.rgb + mask * amount; vec3 sharpened = original.rgb + mask * amount;
fragColor0 = vec4(clamp(sharpened, 0.0, 1.0), original.a); fragColor0 = vec4(clamp(sharpened, 0.0, 1.0), original.a);
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long