dot/config/picom/shaders/OldCRT.glsl
2025-12-01 10:08:43 +00:00

190 lines
6.1 KiB
GLSL

#version 430
#define PI 3.1415926538
uniform float opacity;
uniform float time;
// Works best with fullscreen windows
// Made this to play retro games the way god intended
uniform float sc_freq = 0.2; // Frequency for the scanlines
uniform float sc_intensity = 0.6; // Intensity of the scanline effect
uniform bool grid = false; // Whether to also apply scanlines to x axis or not
uniform int distortion_offset = 2; // Pixel offset for red/blue distortion
uniform int downscale_factor = 2; // How many pixels of the window
// make an actual "pixel" (or block)
uniform float sph_distance = 500; // Distance from the theoretical sphere
// we use for our curvature transform
uniform float curvature = 1.5; // How much the window should "curve"
uniform float shadow_cutoff = 1; // How "early" the shadow starts affecting
// pixels close to the edges
// I'd keep this value very close to 1
uniform int shadow_intensity = 1; // Intensity level of the shadow effect (from 1 to 5)
vec4 outside_color = vec4(0 ,0 ,0, opacity); // Color for the outside of the window
float flash_speed = 0; // Speed of flashing effect, set to 0 to deactivate
float flash_intensity = 0.8; // Intensity of flashing effect
// You can play with different values for all the variables above
in vec2 texcoord; // texture coordinate of the fragment
uniform sampler2D tex; // texture of the window
ivec2 window_size = textureSize(tex, 0);
ivec2 window_center = ivec2(window_size.x/2, window_size.y/2);
float radius = (window_size.x/curvature);
int flash = int(round(flash_speed*time/(10000/window_size.y))) % window_size.y;
// Default window post-processing:
// 1) invert color
// 2) opacity / transparency
// 3) max-brightness clamping
// 4) rounded corners
vec4 default_post_processing(vec4 c);
// Darkens a pixels near the edges
vec4 darken_color(vec4 color, vec2 coords)
{
// If shadow intensity is 0, change nothing
if (shadow_intensity == 0)
{
return color;
}
// Get how far the coords are from the center
vec2 distances_from_center = abs(window_center - coords);
// Darken pixels close to the edges of the screen in a polynomial fashion
float brightness = 1;
brightness *= -pow((distances_from_center.y/window_center.y)*shadow_cutoff,
(5/shadow_intensity)*2)+1;
brightness *= -pow((distances_from_center.x/window_center.x)*shadow_cutoff,
(5/shadow_intensity)*2)+1;
color.xyz *= brightness;
return color;
}
// Applies a transformation to our window pixels to simulate
// a curved screen
ivec2 curve_coords_spheric(vec2 coords)
{
// Offset coords
coords -= window_center;
vec2 curved_coords;
// For this transform imagine a sphere in a 3d space with the
// window as a 2d plane tangent to that sphere
// For simplicity, we center the sphere at 0,0,0
// The coordinates of the projection share x and y with our window pixel
// We find Z using the formula for a sphere
vec3 projection_coords3d = vec3(coords.x, coords.y,
sqrt(pow(radius+sph_distance,2)-
pow(coords.x,2)-
pow(coords.y,2)));
// That vector goes from the center of the sphere to the projection of a pixel
// of our window onto the sphere's surface
// Let's scale it until it hits our window plane
projection_coords3d *= ((radius+sph_distance)/projection_coords3d.z);
curved_coords = projection_coords3d.xy;
// Compensate for starting coords offset
curved_coords += window_center;
return ivec2(curved_coords);
}
// Gets a color for a pixel with all the coordinate and
// downscale changes
vec4 get_pixel(vec2 coords)
{
// If pixel is at the edge of the window, return a completely black color
if (coords.x >=window_size.x-1 || coords.y >=window_size.y-1 ||
coords.x <=0 || coords.y <=0)
{
return outside_color;
}
vec4 color = texelFetch(tex, ivec2(coords), 0);
return default_post_processing(color);
}
// Gets the color from a downscaled block
vec4 get_block_color(vec2 coords)
{
// If downscale is set to 1, just return a pixel
if (downscale_factor < 2)
{
return get_pixel(coords);
}
// Relative position of pixel inside the block
ivec2 relative_position;
relative_position.xy = ivec2(coords).xy % downscale_factor;
// Average all colors from pixels inside the block
vec4 average = vec4(0, 0 , 0, 0);
for (int i = 0; i < downscale_factor; i++)
{
for (int j = 0; j < downscale_factor; j++)
{
average.xyzw += get_pixel(vec2(coords.x + i - relative_position.x,
coords.y + j - relative_position.y));
}
}
average /= pow(downscale_factor, 2);
return average;
}
// Main shader function
vec4 window_shader() {
// Apply curvature transform to coords
vec2 curved_coords = curve_coords_spheric(texcoord);
// Fetch the color
vec4 c = get_block_color(curved_coords);
// Fetch colors from close pixels to apply color distortion
vec4 c_right = get_block_color(vec2(curved_coords.x+2, curved_coords.y));
vec4 c_left = get_block_color(vec2(curved_coords.x-2, curved_coords.y));
// Mix red and blue colors
c = vec4(c_left.x, c.y, c_right.z, c.w);
// Apply scanlines
c.xyz *= sin(2*PI*sc_freq*(texcoord).y)/(2/sc_intensity) +
1 - sc_intensity/2;
// Also apply scanlines to x axis if grid is enabled
if (grid == true)
{
c.xyz *= sin(2*PI*sc_freq*(texcoord).x)/(2/sc_intensity) +
1 - sc_intensity/2;
}
// Apply flash
if (curved_coords.y >=flash-(window_size.y/10) && curved_coords.y <=flash)
{
c.xyz *= flash_intensity*(pow(((flash-curved_coords.y)/(window_size.y/10))-1,2)
+ 1/flash_intensity);
}
// Darken pixel
c = darken_color(c, curved_coords);
return (c);
}