Add color blind accessibility filter

This commit is contained in:
Your Name 2026-08-11 14:08:36 -04:00
parent b42569eb9c
commit fa0bada313
2 changed files with 132 additions and 2 deletions

View file

@ -57,4 +57,75 @@ glow = love.graphics.newShader[[
}
return pixel_me;
}
]]
]]
-- Accessibility post-process. The puzzle's RGB channels are normally also
-- its visual language, so this runs only after the entire scene has been
-- composed. It leaves neutral UI/text alone and remaps saturated colours to
-- a palette whose red, green, and yellow accents remain distinct for people
-- with deuteranopia.
accessibilityColorShader = love.graphics.newShader[[
extern number accessibilityTime;
vec3 rgbToHsv(vec3 c) {
float maximum = max(c.r, max(c.g, c.b));
float minimum = min(c.r, min(c.g, c.b));
float delta = maximum - minimum;
float hue = 0.0;
if (delta > 0.0001) {
if (maximum == c.r) {
hue = mod((c.g - c.b) / delta, 6.0);
} else if (maximum == c.g) {
hue = ((c.b - c.r) / delta) + 2.0;
} else {
hue = ((c.r - c.g) / delta) + 4.0;
}
hue = hue / 6.0;
if (hue < 0.0) hue += 1.0;
}
float saturation = maximum <= 0.0 ? 0.0 : delta / maximum;
return vec3(hue, saturation, maximum);
}
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
vec4 pixel = Texel(texture, texture_coords) * color;
vec3 hsv = rgbToHsv(pixel.rgb);
// Preserve greys, white (including additive RGB overlap), and near-black
// background detail. Only colourful puzzle elements are recoloured.
if (hsv.y < 0.22 || hsv.z < 0.06) return pixel;
// Keep every additive pair in its own bucket. In particular, magenta
// (red + blue) must not become the same colour as red, and cyan
// (green + blue) must not become the same colour as green.
vec3 replacement;
float pattern = 0.0;
// Advance in whole pixels so the texture stays crisp instead of swimming.
float ditherStep = floor(accessibilityTime * 1.5);
if (hsv.x < 0.055 || hsv.x >= 0.94) {
replacement = vec3(0.93, 0.18, 0.50); // red: pink
} else if (hsv.x < 0.19) {
replacement = vec3(1.00, 0.78, 0.08); // red + green: gold
vec2 cell = mod(floor(screen_coords + vec2(ditherStep, 0.0)), vec2(9.0));
pattern = length(cell - vec2(4.0)) < 1.6 ? 1.0 : 0.0; // dithered dots
} else if (hsv.x < 0.43) {
replacement = vec3(0.00, 0.72, 0.62); // green: turquoise
} else if (hsv.x < 0.58) {
replacement = vec3(0.12, 0.55, 0.96); // green + blue: sky blue
pattern = mod(floor(screen_coords.x + screen_coords.y + ditherStep), 10.0) < 2.0 ? 1.0 : 0.0; // dithered slashes
} else if (hsv.x < 0.77) {
replacement = vec3(0.38, 0.32, 0.96); // blue: indigo
} else {
replacement = vec3(0.72, 0.25, 0.92); // red + blue: purple
pattern = mod(floor((screen_coords.x + ditherStep) / 4.0) + floor(screen_coords.y / 4.0), 2.0); // dithered checker
}
// Secondary colours get a gently animated dither cue, so they are
// identifiable even when their hue is hard to perceive. Lighting and
// fades remain.
replacement = mix(replacement, vec3(1.0), pattern * 0.55);
return vec4(replacement * hsv.z, pixel.a);
}
]]