1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#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);
}
|