1
0
mirror of https://github.com/Swordfish90/cool-retro-term.git synced 2026-02-08 00:32:27 +00:00
Files
cool-retro-term/app/shaders/terminal_dynamic.frag
2025-12-24 19:17:32 +01:00

182 lines
5.7 KiB
GLSL

#version 440
#ifndef CRT_RASTER_MODE
#define CRT_RASTER_MODE 0
#endif
#ifndef CRT_BURN_IN
#define CRT_BURN_IN 1
#endif
#ifndef CRT_DISPLAY_FRAME
#define CRT_DISPLAY_FRAME 1
#endif
#ifndef CRT_CHROMA
#define CRT_CHROMA 1
#endif
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 1) in float vBrightness;
layout(location = 2) in float vDistortionScale;
layout(location = 3) in float vDistortionFreq;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform ubuf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
vec4 fontColor;
vec4 backgroundColor;
vec2 virtualResolution;
float rasterizationIntensity;
float burnInLastUpdate;
float burnInTime;
float burnIn;
float staticNoise;
float screenCurvature;
float glowingLine;
float chromaColor;
vec2 jitterDisplacement;
float ambientLight;
float jitter;
float horizontalSync;
float horizontalSyncStrength;
float flickering;
float displayTerminalFrame;
vec2 scaleNoiseSize;
float frameShininess;
float frameSize;
float bloom;
};
layout(binding = 0) uniform sampler2D noiseSource;
layout(binding = 1) uniform sampler2D screenBuffer;
layout(binding = 2) uniform sampler2D burnInSource;
layout(binding = 3) uniform sampler2D frameSource;
float min2(vec2 v) { return min(v.x, v.y); }
float prod2(vec2 v) { return v.x * v.y; }
float sum2(vec2 v) { return v.x + v.y; }
float rgb2grey(vec3 v) { return dot(v, vec3(0.21, 0.72, 0.04)); }
vec2 distortCoordinates(vec2 coords){
vec2 paddedCoords = coords * (vec2(1.0) + frameSize * 2.0) - frameSize;
vec2 cc = (paddedCoords - vec2(0.5));
float dist = dot(cc, cc) * screenCurvature;
return (paddedCoords + cc * (1.0 + dist) * dist);
}
vec3 applyRasterization(vec2 screenCoords, vec3 texel, vec2 virtualRes, float intensity) {
#if CRT_RASTER_MODE == 0
return texel;
#else
if (intensity <= 0.0) {
return texel;
}
const float INTENSITY = 0.30;
const float BRIGHTBOOST = 0.30;
#if CRT_RASTER_MODE == 1
vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * texel)) * texel;
vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * texel)) * texel;
vec2 coords = fract(screenCoords * virtualRes) * 2.0 - vec2(1.0);
float mask = 1.0 - abs(coords.y);
vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
#elif CRT_RASTER_MODE == 2
vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * texel)) * texel;
vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * texel)) * texel;
vec2 coords = fract(screenCoords * virtualRes) * 2.0 - vec2(1.0);
coords = coords * coords;
float mask = 1.0 - coords.x - coords.y;
vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
#elif CRT_RASTER_MODE == 3
const float SUBPIXELS = 3.0;
vec3 offsets = vec3(3.141592654) * vec3(0.5, 0.5 - 2.0 / 3.0, 0.5 - 4.0 / 3.0);
vec2 omega = vec2(3.141592654) * vec2(2.0) * virtualRes;
vec2 angle = screenCoords * omega;
vec3 xfactors = (SUBPIXELS + sin(angle.x + offsets)) / (SUBPIXELS + 1.0);
vec3 result = texel * xfactors;
vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * result)) * result;
vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * result)) * result;
vec2 coords = fract(screenCoords * virtualRes) * 2.0 - vec2(1.0);
float mask = 1.0 - abs(coords.y);
vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
#else
return texel;
#endif
#endif
}
float randomPass(vec2 coords){
return fract(smoothstep(-120.0, 0.0, coords.y - (virtualResolution.y + 120.0) * fract(time * 0.15)));
}
vec3 convertWithChroma(vec3 inColor) {
#if CRT_CHROMA == 1
float grey = rgb2grey(inColor);
float denom = max(grey, 0.0001);
vec3 foregroundColor = mix(fontColor.rgb, inColor * fontColor.rgb / denom, chromaColor);
return mix(backgroundColor.rgb, foregroundColor, grey);
#else
return mix(backgroundColor.rgb, fontColor.rgb, rgb2grey(inColor));
#endif
}
void main() {
vec2 cc = vec2(0.5) - qt_TexCoord0;
float distance = length(cc);
vec2 staticCoords = distortCoordinates(qt_TexCoord0);
vec2 coords = qt_TexCoord0;
float dst = sin((coords.y + time) * vDistortionFreq);
coords.x += dst * vDistortionScale;
vec4 noiseTexel = texture(noiseSource, scaleNoiseSize * coords + vec2(fract(time / 0.051), fract(time / 0.237)));
vec2 txt_coords = coords + (noiseTexel.ba - vec2(0.5)) * jitterDisplacement * jitter;
float color = 0.0001;
color += noiseTexel.a * staticNoise * (1.0 - distance * 1.3);
color += randomPass(coords * virtualResolution) * glowingLine;
vec3 txt_color = texture(screenBuffer, txt_coords).rgb;
float bloomScale = 1.0 + max(bloom, 0.0);
txt_color *= bloomScale;
#if CRT_BURN_IN == 1
vec4 txt_blur = texture(burnInSource, staticCoords);
float blurDecay = clamp((time - burnInLastUpdate) * burnInTime, 0.0, 1.0);
vec3 burnInColor = 0.65 * (txt_blur.rgb - vec3(blurDecay));
txt_color = max(txt_color, burnInColor);
#endif
txt_color += vec3(color);
txt_color = applyRasterization(staticCoords, txt_color, virtualResolution, rasterizationIntensity);
vec3 finalColor = convertWithChroma(txt_color);
float brightness = mix(1.0, vBrightness, step(0.0, flickering));
finalColor *= brightness;
finalColor += vec3(ambientLight) * (1.0 - distance) * (1.0 - distance);
#if CRT_DISPLAY_FRAME == 1
vec4 frameColor = texture(frameSource, qt_TexCoord0);
vec3 reflection = max(finalColor - backgroundColor.rgb, vec3(0.0)) * frameShininess;
finalColor = mix(finalColor, frameColor.rgb + reflection, frameColor.a);
#endif
fragColor = vec4(finalColor, qt_Opacity);
}