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
|
// Specify default precision for fragment shaders
out vec4 outcolor;
in vec2 frag_texture_coord;
uniform vec2 resolution;
uniform vec2 src_image_size;
uniform float brightness;
uniform vec4 tone_data;
uniform bool crt_emulation;
uniform sampler2D iChannel0;
vec3 CrtsFetch(vec2 uv) {
const float bias = 0.003333333;
return max(texture(iChannel0, uv, -16.0).rgb, vec3(bias));
}
#define CrtsRcpF1(x) (1.0 / (x))
#define CrtsSatF1(x) clamp((x), 0.0, 1.0)
const float PI2 = 6.28318530717958;
const float HALF = 0.5;
float CrtsMax3F1(float a, float b, float c) {
return max(a, max(b, c));
}
vec3 CrtsMask(vec2 pos, float dark) {
#ifdef CRTS_MASK_GRILLE
vec3 m = vec3(dark);
float x = fract(pos.x * (1.0 / 3.0));
m.r = (x < (1.0 / 3.0)) ? 1.0 : dark;
m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? 1.0 : dark;
m.b = (x >= (2.0 / 3.0)) ? 1.0 : dark;
return m;
#endif
#ifdef CRTS_MASK_GRILLE_LITE
vec3 m = vec3(1.0);
float x = fract(pos.x * (1.0 / 3.0));
m.r = (x < (1.0 / 3.0)) ? dark : 1.0;
m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? dark : 1.0;
m.b = (x >= (2.0 / 3.0)) ? dark : 1.0;
return m;
#endif
#ifdef CRTS_MASK_NONE
return vec3(1.0);
#endif
#ifdef CRTS_MASK_SHADOW
pos.x += pos.y * 3.0;
vec3 m = vec3(dark);
float x = fract(pos.x * (1.0 / 6.0));
m.r = (x < (1.0 / 3.0)) ? 1.0 : dark;
m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? 1.0 : dark;
m.b = (x >= (2.0 / 3.0)) ? 1.0 : dark;
return m;
#endif
}
vec3 CrtsFilter(vec2 ipos, vec2 inputSizeDivOutputSize, vec2 halfInputSize, vec2 rcpInputSize, vec2 rcpOutputSize, vec2 twoDivOutputSize, float inputHeight, vec2 warp, float thin, float blur, float mask, vec4 tone) {
vec2 pos = ipos * twoDivOutputSize - vec2(1.0);
pos *= vec2(1.0 + (pos.y * pos.y) * warp.x, 1.0 + (pos.x * pos.x) * warp.y);
float vin = 1.0 - ((1.0 - CrtsSatF1(pos.x * pos.x)) * (1.0 - CrtsSatF1(pos.y * pos.y)));
vin = CrtsSatF1((-vin) * inputHeight + inputHeight);
pos = pos * halfInputSize + halfInputSize;
float y0 = floor(pos.y - 0.5) + 0.5;
float x0 = floor(pos.x - 1.5) + 0.5;
vec2 p = vec2(x0 * rcpInputSize.x, y0 * rcpInputSize.y);
vec3 colA[4], colB[4];
for (int i = 0; i < 4; i++) {
colA[i] = CrtsFetch(p);
p.x += rcpInputSize.x;
}
p.y += rcpInputSize.y;
for (int i = 3; i >= 0; i--) {
p.x -= rcpInputSize.x;
colB[i] = CrtsFetch(p);
}
float off = pos.y - y0;
float scanA = cos(min(HALF, off * thin) * PI2) * HALF + HALF;
float scanB = cos(min(HALF, (-off) * thin + thin) * PI2) * HALF + HALF;
float off0 = pos.x - x0;
float pix[4];
for (int i = 0; i < 4; i++) {
float diff = off0 - float(i);
pix[i] = exp2(blur * diff * diff);
}
float pixT = CrtsRcpF1(pix[0] + pix[1] + pix[2] + pix[3]);
#ifdef CRTS_WARP
pixT *= vin;
#endif
scanA *= pixT;
scanB *= pixT;
vec3 color = (colA[0] * pix[0] + colA[1] * pix[1] + colA[2] * pix[2] + colA[3] * pix[3]) * scanA + (colB[0] * pix[0] + colB[1] * pix[1] + colB[2] * pix[2] + colB[3] * pix[3]) * scanB;
color *= CrtsMask(ipos, mask);
#ifdef CRTS_TONE
float peak = max(1.0 / (256.0 * 65536.0), CrtsMax3F1(color.r, color.g, color.b));
vec3 ratio = color * CrtsRcpF1(peak);
#ifdef CRTS_CONTRAST
peak = pow(peak, tone.x);
#endif
peak = peak * CrtsRcpF1(peak * tone.y + tone.z);
#ifdef CRTS_SATURATION
ratio = pow(ratio, vec3(tone.w));
#endif
return ratio * peak;
#else
return color;
#endif
}
vec3 linearToSRGB(vec3 color) {
return pow(color, vec3(1.0 / 2.2));
}
void main() {
vec2 fragCoord = vec2(frag_texture_coord.x, 1.0 - frag_texture_coord.y);
if (crt_emulation) {
outcolor.rgb = CrtsFilter(
fragCoord.xy * resolution,
src_image_size / resolution,
src_image_size * vec2(0.5),
1.0 / src_image_size,
1.0 / resolution,
2.0 / resolution,
src_image_size.y,
vec2(1.0 / 24.0, 1.0 / 16.0), // warp value
INPUT_THIN,
INPUT_BLUR,
INPUT_MASK,
tone_data
);
outcolor.rgb *= brightness;
outcolor = vec4(outcolor.rgb, 1.0); // Keep original color with alpha set to 1.0
} else {
outcolor = texture(iChannel0, fragCoord);
}
}
|