Back to shaders
Shader test bench
Gn XORmear.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
precision mediump float;
/*{
"CATEGORIES": [
"Glitch",
"Distortion",
"Gnomalab"
],
"CREDIT": "Gnomalab",
"DESCRIPTION": "\"Procesador de estelas digitales con corrupción bitwise XOR. Permite aislar rangos de luminancia específicos y alternar entre texturas de barrido rugosas o sólidas mediante su ruteo de doble etapa.\"",
"INPUTS": [
{
"NAME": "inputImage",
"TYPE": "image"
},
{
"DEFAULT": 0,
"LABEL": "Umbral MIN",
"MAX": 1,
"MIN": 0,
"NAME": "threshold_min",
"TYPE": "float"
},
{
"DEFAULT": 1,
"LABEL": "Umbral MAX",
"MAX": 1,
"MIN": 0,
"NAME": "threshold_max",
"TYPE": "float"
},
{
"DEFAULT": 0.1,
"LABEL": "Suavizado (Softness)",
"MAX": 0.5,
"MIN": 0,
"NAME": "softness",
"TYPE": "float"
},
{
"DEFAULT": false,
"LABEL": "Invertir Mascara",
"NAME": "mask_invert",
"TYPE": "bool"
},
{
"DEFAULT": 0,
"LABEL": "ORDEN",
"LABELS": [
"1: XOR > Smear",
"2: Smear > XOR"
],
"NAME": "processing_order",
"TYPE": "long",
"VALUES": [
0,
1
]
},
{
"DEFAULT": 10,
"LABEL": "XOR Amount",
"MAX": 255,
"MIN": 0,
"NAME": "xor_val",
"TYPE": "float"
},
{
"DEFAULT": 0.4,
"LABEL": "Smear Length",
"MAX": 1,
"MIN": 0,
"NAME": "smear_len",
"TYPE": "float"
},
{
"DEFAULT": 0,
"LABEL": "Smear Angle",
"MAX": 1,
"MIN": 0,
"NAME": "smear_angle",
"TYPE": "float"
}
],
"ISFVSN": "2",
"VSN": "1.0.0"
}
*/
// --- FUNCION XOR MANUAL ---
float manual_xor(float a, float b) {
float result = 0.0;
for(int i = 0; i < 8; ++i) {
float p = pow(2.0, float(i));
float a_bit = mod(floor(a / p), 2.0);
float b_bit = mod(floor(b / p), 2.0);
if (abs(a_bit - b_bit) > 0.5) result += p;
}
return result;
}
vec4 applyXOR(vec4 col, float xVal) {
float r = manual_xor(floor(col.r * 255.0), xVal) / 255.0;
float g = manual_xor(floor(col.g * 255.0), xVal) / 255.0;
float b = manual_xor(floor(col.b * 255.0), xVal) / 255.0;
return vec4(r, g, b, col.a);
}
float getLuma(vec4 col) {
return dot(col.rgb, vec3(0.299, 0.587, 0.114));
}
void main() {
vec2 uv = isf_FragNormCoord;
vec4 baseCol = IMG_NORM_PIXEL(inputImage, uv);
// 1. Calcular Mascara de Banda (Min/Max)
float luma = getLuma(baseCol);
// Suavizado en el minimo y en el maximo
float mask_low = smoothstep(threshold_min - softness, threshold_min + softness, luma);
float mask_high = 1.0 - smoothstep(threshold_max - softness, threshold_max + softness, luma);
float mask = mask_low * mask_high;
if (mask_invert) mask = 1.0 - mask;
vec4 finalCol;
vec2 dir = vec2(cos(smear_angle * 6.2831), sin(smear_angle * 6.2831));
float samples = 16.0;
float distMult = 0.8;
if (processing_order == 0) {
// --- MODO 1: XOR > SMEAR ---
vec4 acc = vec4(0.0);
for(float i = 0.0; i < 16.0; i++) {
vec2 offset = dir * (i / samples) * smear_len * distMult;
vec4 pix = IMG_NORM_PIXEL(inputImage, fract(uv - offset));
acc += applyXOR(pix, xor_val);
}
finalCol = mix(baseCol, acc / samples, mask);
} else {
// --- MODO 2: SMEAR > XOR ---
vec4 acc = vec4(0.0);
for(float i = 0.0; i < 16.0; i++) {
vec2 offset = dir * (i / samples) * smear_len * distMult;
acc += IMG_NORM_PIXEL(inputImage, fract(uv - offset));
}
vec4 smearClean = acc / samples;
vec4 xorResult = applyXOR(smearClean, xor_val);
finalCol = mix(baseCol, xorResult, mask);
}
gl_FragColor = finalCol;
}