Files
tbd-station-14/Resources/Textures/Shaders/cataracts.swsl
deathride58 6901e930b5 Blindness hotfix (content side) (#23465)
* fixes major blindness issues like blindness not scaling with render res

* HEY. get outta there
2024-01-04 11:17:06 +11:00

78 lines
7.3 KiB
Plaintext

// This is a shader simulation of cataracts. Not exactly a realistic/faithful one (the faithful route would be bloom and blur strong enough to make anyone's eyes bleed), but rather, a stylized one!
// This description and explainer here is mostly for the sake of future reference, and to make sure the artistic intent doesn't end up lost throughout the years to come.
// The effects this goes for are based largely off reports from blind friends and colleagues alike. Mostly reported experiences of cataracts, but also other forms of blindness, such as neuro-opthalmic injuries.
// The distortion here does the bulk of the work. This simulates the most common thing that those with the condition report: of everything being an amorphous blur, of seeing double even out of one eye, and more.
// Of course, it'd be incredibly straight-forward to go for blur. However, this runs into a notable issue: making a blur strong enough to actually be debilitating would be pretty intense on GPU performance.
// There's additional issues with blur as well, but the main sour point is that it *really* doesn't work well in 2D, where everything has the same effective focal point.
// However, distortion is surprisingly suitable for the purpose of making everything amorphous, even if it's less a blur and more a haze.
// This distortion also moves a bit. This definitely leans far more towards simulating neuro-opthalmic injuries than cataracts specifically (this is effectively oscillopsia), but it does make sure you can't negate the effect by memorizing what part of the screen correlates to exactly where.
// As a bonus: this also simulates less-talked-about experiences, such as always seeing multiple, and attempts to rely on far-sighted vision being immensely disorienting.
// But being unable to make out what's happening in your vision is far from the only pain that blind folks have to put up with.
// Notably: another very common report is that it can be hard (but not impossible) to make out color, and that every light source is incredibly *bright* to the point of causing pain.
// This, too, would be far more straight-forward to implement by slapping a heavy bloom filter on the effect.
// ... However, this is a bad idea for a pretty obvious reason: heavy bloom almost always causes eyestrain. The idea here is to simulate blindness, not *cause* blindness!
// So instead, this takes a slightly more creative route: aggressively tonemapping the distorted output, and pulling in a distorted lightmap to blend on top.
// These two steps combined create a foggy and surprisingly blurry image. The result is deliberately muted a bit to reduce eyestrain, but it does lead to any amount of light overpowering everything else, which lines up with reported experiences.
// All of this in combination manages to achieve an experience that hits the same notes of what's common for blind folks to report, but in a *very* stylized manner.
// And of course, more importantly: the combination ensures that if your character is blind, then you simply can't see shit. You can certainly *try*, just as blind folks IRL are able to, but that'll be fruitless!
// Oh. God. We're already at 18 lines of header comments. We rambled, huh? Anyway, thank you for coming to our TED talk. - Bhijn & Myr
// Boilerplate vars
uniform sampler2D SCREEN_TEXTURE;
uniform sampler2D LIGHT_TEXTURE;
uniform highp float Zoom;
// Base distortion
uniform highp float DistortionScalar; // Scales the total distortion applied to the final image.
const highp float DistortionCoordScalar = 0.1125; //Scales the coordinates for the distortion texture
const highp float DistortionCenterRadius = 200.0; // radius of the gradient used to tone down the distortion effect
const highp float DistortionCenterMinDist = 0.25; // minimum distance from the center of the screen for the distortion to appear at all
const highp float DistortionCenterPow = 1.1; // the exponent used for the distortion center
const highp float DistortionGradientMax = 8.0; // Maximum value for the gradient used for the distortion
const highp float DistortionZoomPow = 0.5; // exponent for the zoom uniform when applied to distortion. The math is funky
const highp float NoiseScalar = 10.0; // Multiplies the size of the FBM noise
// Base cloudiness
uniform highp float CloudinessScalar;
const highp float CloudinessCenterRadius = 125.0; // radius of the gradient used to tone down the cloudiness
const highp float CloudinessCenterMinDist = 0.2; // minimum distance from the center of the screen for cloudiness to appear at all
const highp float CloudinessCenterPow = 2.0; // the exponent used for the distortion center
const highp float CloudinessGradientMax = 8.0; // Maximum value for the gradient used for cloudiness
const highp float CloudinessGrayscaleMax = 0.8; // maximum desaturation for the cloudiness effect
const highp float CloudinessGrayscaleMult = 0.15; // multiplier applied to the gradient for the desaturation scalar
const highp float CloudinessZoomPow = 0.85; // exponent for the zoom uniform when applied to cloudiness
// Additional lightmap pass for the cloudiness
const highp float CloudyLightDistortionMult = 0.5; // multiplier applied to the total amount of distortion added to the lightmap during the lightmap pass
const highp float CloudyLightMult = 0.05; // multiplier for scaling the lightmap's brightness
const highp float CloudyLightGrayscaleMax = 0.25; // maximum amount of grayscale effect for the lightmap
const highp float CloudyLightGrayscaleMult = 0.15; // multiplier used to scale the grayscale effect
const highp float TimeScale = 0.25;
void fragment() {
highp vec2 aspect = vec2(1.0/SCREEN_PIXEL_SIZE.x, 1.0/SCREEN_PIXEL_SIZE.y);
highp float timesin = (sin(TIME * TimeScale) + 0.5) * 0.2;
highp float timecos = (cos(TIME * TimeScale) + 0.5) * 0.2;
highp float distortionZoom = pow(Zoom, DistortionZoomPow);
highp float centergradient = zCircleGradient(aspect, FRAGCOORD.xy, DistortionGradientMax, DistortionCenterRadius / distortionZoom, DistortionCenterMinDist / distortionZoom, DistortionCenterPow);
highp vec2 coord = (FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy) * centergradient * DistortionCoordScalar;
highp vec2 offset = vec2((zFBM(coord * NoiseScalar + timesin) - 0.5) * centergradient, (zFBM(coord * NoiseScalar + timecos) - 0.5) * centergradient);
COLOR = zTextureSpec(SCREEN_TEXTURE, Pos + (offset * DistortionScalar));
highp float cloudyZoom = pow(Zoom, CloudinessZoomPow);
highp float cloudygradient = zCircleGradient(aspect, FRAGCOORD.xy, CloudinessGradientMax, CloudinessCenterRadius / cloudyZoom, CloudinessCenterMinDist / cloudyZoom, CloudinessCenterPow) * CloudinessScalar;
COLOR.rgb = mix(COLOR.rgb, vec3(zGrayscale(COLOR.rgb)), min(cloudygradient * CloudinessGrayscaleMult, CloudinessGrayscaleMax));
COLOR.rgb = pow(COLOR.rgb, vec3((cloudygradient * CloudinessScalar + 1.0)));
//technically the highp makes this higher-quality than the actual default frag shader's lightmap sampling but shushhhhhh it looks prettier without as much banding
highp vec3 lightsample = (texture2D(LIGHT_TEXTURE, Pos + (offset * DistortionScalar * CloudyLightDistortionMult)).rgb * pow(cloudygradient, 1.0 / CloudinessCenterPow) * CloudyLightMult);
lightsample = mix(lightsample, vec3(zGrayscale(lightsample)), min(cloudygradient * CloudyLightGrayscaleMult, CloudyLightGrayscaleMax));
COLOR.rgb = COLOR.rgb + lightsample;
}