diff --git a/examples/multistep_pixelsort.gdshader b/examples/multistep_pixelsort.gdshader new file mode 100644 index 0000000..21ba1b6 --- /dev/null +++ b/examples/multistep_pixelsort.gdshader @@ -0,0 +1,20 @@ +shader_type canvas_item; + +#include "./shaderlib/pixelsort.gdshaderinc" + +//!steps 1500 +uniform int STEP; + +//!load ./images/mountain.jpg + +void fragment() { + // pixel sorting works in multiple steps + COLOR = pixelsort_step( + TEXTURE, UV, + DIRECTION_BOTTOM_TO_TOP, + COLOR_MODE_OKLCH, + {true, false, false}, + {-INF, .007, -INF}, + {INF, INF, INF}, + STEP); +} diff --git a/examples/multistep_pixelsort.gdshader.uid b/examples/multistep_pixelsort.gdshader.uid new file mode 100644 index 0000000..47eaaf5 --- /dev/null +++ b/examples/multistep_pixelsort.gdshader.uid @@ -0,0 +1 @@ +uid://csk0fg4by651b diff --git a/shaderlib/pixelsort.gdshaderinc b/shaderlib/pixelsort.gdshaderinc new file mode 100644 index 0000000..73ed5da --- /dev/null +++ b/shaderlib/pixelsort.gdshaderinc @@ -0,0 +1,126 @@ + +/* + Pixelsorting using odd-even sort + + I roughly followed https://ciphrd.com/2020/04/08/pixel-sorting-on-shader-using-well-crafted-sorting-filters-glsl/ + - vector fields aren't implemented, diagonal sorting is not supported! +*/ + +#include "./hsv.gdshaderinc" +#include "./oklab.gdshaderinc" + +#define INF (1.0/0.0) + +#define DIRECTION_LEFT_TO_RIGHT vec2(1, 0) +#define DIRECTION_RIGHT_TO_LEFT vec2(-1, 0) +#define DIRECTION_TOP_TO_BOTTOM vec2(0, 1) +#define DIRECTION_BOTTOM_TO_TOP vec2(0, -1) + +#define COLOR_MODE_RGB 0 +#define COLOR_MODE_OKLAB 1 +#define COLOR_MODE_OKLCH 2 +#define COLOR_MODE_HSV 3 + +vec4 pixelsort_step( + sampler2D tex, vec2 uv, + vec2 direction, // e.g. (1, 0) for left-to-right or (0, -1) for bottom-to-top + // see DIRECTION_LEFT_TO_RIGHT, etc. + // note: vertical sorting doesn't work, so using e.g. (1, 1) won't work + int color_mode, // 0 = RGB, 1 = OKLAB, 2 = OKLCH, 3 = HSV + // see COLOR_MODE_RGB, etc. + bool color_channel_mask[3], // which color channel(s) to take into account + float lower_threshold[3], // lower threshold for pixels to be considered sorted + // when in doubt, use {-INF, -INF, -INF} + float upper_threshold[3], // upper threshold; {INF, INF, INF} + int step_ // from STEP +) { + // sanitize inputs + direction = clamp(direction, vec2(-1, -1), vec2(1, 1)); + color_mode = clamp(color_mode, 0, 3); + // get neighbour + vec2 texture_size = vec2(textureSize(tex, 0)); + vec2 a = (mod(floor(uv * texture_size), 2.0) * 2.0 - 1.0) * (mod(float(step_), 2.0) * 2.0 - 1.0); + vec2 neighbour_uv = uv + (direction * a / texture_size); + // + vec4 x = texture(tex, uv); + vec4 y = texture(tex, neighbour_uv); + if ( // stop at borders + neighbour_uv.x > 1.0 || + neighbour_uv.x < 0.0 || + neighbour_uv.y > 1.0 || + neighbour_uv.y < 0.0 + ) { + return x; + } else { + // convert color if necessary + // get value to compare + float vx = 0.0; + float vy = 0.0; + vec3 color_x; + vec3 color_y; + if (color_mode == COLOR_MODE_RGB) { + color_x = x.rgb; + color_y = y.rgb; + } else if (color_mode == COLOR_MODE_OKLAB) { + color_x = rgb2oklab(x).rgb; + color_y = rgb2oklab(y).rgb; + } else if (color_mode == COLOR_MODE_OKLCH) { + color_x = oklab2oklch(rgb2oklab(x)).rgb; + color_y = oklab2oklch(rgb2oklab(y)).rgb; + } else if (color_mode == COLOR_MODE_HSV) { + color_x = rgb2hsv(x).rgb; + color_y = rgb2hsv(y).rgb; + } + float divisor = 0.0; + if (color_channel_mask[0]) { + vx += color_x.r; + vy += color_y.r; + divisor += 1.0; + } + if (color_channel_mask[1]) { + vx += color_x.g; + vy += color_y.g; + divisor += 1.0; + } + if (color_channel_mask[2]) { + vx += color_x.b; + vy += color_y.b; + divisor += 1.0; + } + divisor = max(divisor, 1.0); + vx /= divisor; + vy /= divisor; + // + if ( + (a.x < .0 && abs(direction).y == .0) || + (a.y < .0 && abs(direction).x == .0) + ) { + if ( + vy > vx && + // threshold + color_x.r < upper_threshold[0] && + color_x.g < upper_threshold[1] && + color_x.b < upper_threshold[2] && + color_x.r > lower_threshold[0] && + color_x.g > lower_threshold[1] && + color_x.b > lower_threshold[2] + ) { return y; } + else { return x; } + } else if ( + (a.x > .0 && abs(direction).y == .0) || + (a.y > .0 && abs(direction).x == .0) + ) { + if ( + vx >= vy && + // threshold + color_y.r < upper_threshold[0] && + color_y.g < upper_threshold[1] && + color_y.b < upper_threshold[2] && + color_y.r > lower_threshold[0] && + color_y.g > lower_threshold[1] && + color_y.b > lower_threshold[2] + ) { return y; } + else { return x; } + } + } +} diff --git a/shaderlib/pixelsort.gdshaderinc.uid b/shaderlib/pixelsort.gdshaderinc.uid new file mode 100644 index 0000000..e02f48e --- /dev/null +++ b/shaderlib/pixelsort.gdshaderinc.uid @@ -0,0 +1 @@ +uid://doefnwk3vyr0o