Files
mtd/pixeldither.gdshader
2025-06-23 16:55:07 +10:00

76 lines
1.9 KiB
Plaintext

shader_type canvas_item;
uniform bool shader_enabled = true;
uniform sampler2D palette;
uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap;
uniform bool dithering = false;
uniform int dithering_size : hint_range(1, 16) = 2;
uniform int resolution_scale : hint_range(1, 64) = 2;
uniform int quantization_level : hint_range(2, 64) = 64;
int dithering_pattern(ivec2 fragcoord) {
const int pattern[] = {
-4, +0, -3, +1,
+2, -2, +3, -1,
-3, +1, -4, +0,
+3, -1, +2, -2
};
int x = fragcoord.x % 4;
int y = fragcoord.y % 4;
return pattern[y * 4 + x] * dithering_size;
}
float color_distance(vec3 a, vec3 b) {
vec3 diff = a - b;
return dot(diff, diff);
}
vec3 quantize_color(vec3 color, int levels) {
if (levels <= 1) return vec3(0.0);
float step = 1.0 / float(levels - 1);
return round(color / step) * step;
}
void fragment() {
vec4 final_color;
if (!shader_enabled) {
final_color = texture(SCREEN_TEXTURE, UV);
} else {
vec2 tex_size = vec2(textureSize(SCREEN_TEXTURE, 0));
if (tex_size.x <= 0.0 || tex_size.y <= 0.0) {
final_color = vec4(0.0);
} else {
ivec2 texel_coord = ivec2(floor(UV * tex_size / float(resolution_scale)));
vec3 color = texelFetch(SCREEN_TEXTURE, texel_coord * resolution_scale, 0).rgb;
if (dithering) {
int dither = dithering_pattern(texel_coord);
color += vec3(float(dither) / 255.0);
}
color = clamp(color, 0.0, 1.0);
color = quantize_color(color, quantization_level);
int palette_size = 64;
float closest_distance = 9999.0;
vec4 closest_color = vec4(0.0);
for (int i = 0; i < palette_size; i++) {
float u = float(i) / float(palette_size - 1);
vec3 palette_color = texture(palette, vec2(u, 0.0)).rgb;
float dist = color_distance(color, palette_color);
if (dist < closest_distance) {
closest_distance = dist;
closest_color = vec4(palette_color, 1.0);
}
}
final_color = closest_color;
}
}
COLOR = final_color;
}