1+ // Geargrafx port of crt-consumer from libretro slang-shaders.
2+ // Original shader licensed GPL v2 or later.
3+
4+ in vec2 vTexCoord;
5+ out vec4 FragColor;
6+
7+ uniform sampler2D Source;
8+ uniform vec4 SourceSize;
9+ uniform vec4 OutputSize;
10+ uniform vec4 BackgroundColor;
11+ uniform int FrameCount;
12+ uniform float PRE_SCALE;
13+ uniform float blurx;
14+ uniform float blury;
15+ uniform float warpx;
16+ uniform float warpy;
17+ uniform float corner;
18+ uniform float smoothness;
19+ uniform float inter = 1.0 ;
20+ uniform float Downscale = 2.0 ;
21+ uniform float scanlow;
22+ uniform float scanhigh;
23+ uniform float beamlow;
24+ uniform float beamhigh;
25+ uniform float preserve;
26+ uniform float brightboost1;
27+ uniform float brightboost2;
28+ uniform float glow;
29+ uniform float quality;
30+ uniform float glow_str;
31+ uniform float nois;
32+ uniform float postbr;
33+ uniform float GAMMA_OUT;
34+ uniform float sat;
35+ uniform float contrast;
36+ uniform float WP;
37+ uniform float vignette;
38+ uniform float vpower;
39+ uniform float vstr;
40+
41+ const float MaskDark = 0.2 ;
42+ const float masksize = 1.0 ;
43+
44+ const mat3 D65_to_XYZ = mat3 (
45+ 0.4306190 , 0.2220379 , 0.0201853 ,
46+ 0.3415419 , 0.7066384 , 0.1295504 ,
47+ 0.1783091 , 0.0713236 , 0.9390944 );
48+
49+ const mat3 XYZ_to_D65 = mat3 (
50+ 3.0628971 , - 0.9692660 , 0.0678775 ,
51+ - 1.3931791 , 1.8760108 , - 0.2288548 ,
52+ - 0.4757517 , 0.0415560 , 1.0693490 );
53+
54+ const mat3 D50_to_XYZ = mat3 (
55+ 0.4552773 , 0.2323025 , 0.0145457 ,
56+ 0.3675500 , 0.7077956 , 0.1049154 ,
57+ 0.1413926 , 0.0599019 , 0.7057489 );
58+
59+ const mat3 XYZ_to_D50 = mat3 (
60+ 2.9603944 , - 0.9787684 , 0.0844874 ,
61+ - 1.4678519 , 1.9161415 , - 0.2545973 ,
62+ - 0.4685105 , 0.0334540 , 1.4216174 );
63+
64+ vec2 warp_position(vec2 pos)
65+ {
66+ pos = pos * 2.0 - 1.0 ;
67+ pos *= vec2 (1.0 + pos.y * pos.y * warpx, 1.0 + pos.x * pos.x * warpy);
68+ return pos * 0.5 + 0.5 ;
69+ }
70+
71+ bool outside_source(vec2 pos)
72+ {
73+ return pos.x < 0.0 || pos.x > 1.0 || pos.y < 0.0 || pos.y > 1.0 ;
74+ }
75+
76+ vec3 sample_source(vec2 pos)
77+ {
78+ if (outside_source(pos))
79+ return BackgroundColor.rgb;
80+
81+ return texture(Source, pos).rgb;
82+ }
83+
84+ float scanline_weight(float y, float luminance)
85+ {
86+ float beam = mix (scanlow, scanhigh, y);
87+ float scan = mix (beamlow, beamhigh, luminance);
88+ float ex = y * scan;
89+ return exp2 (- beam * ex * ex);
90+ }
91+
92+ vec3 default_mask(vec2 pos, float luminance)
93+ {
94+ pos = floor (pos / masksize);
95+
96+ float phase = fract (pos.x * 0.4999 );
97+ vec3 mask = phase < 0.4999 ? vec3 (1.0 , MaskDark, 1.0 ) : vec3 (MaskDark, 1.0 , MaskDark);
98+ return mix (mask, vec3 (1.0 ), luminance * preserve);
99+ }
100+
101+ mat4 contrast_matrix(void )
102+ {
103+ float t = (1.0 - contrast) * 0.5 ;
104+
105+ return mat4 (contrast, 0.0 , 0.0 , 0.0 ,
106+ 0.0 , contrast, 0.0 , 0.0 ,
107+ 0.0 , 0.0 , contrast, 0.0 ,
108+ t, t, t, 1.0 );
109+ }
110+
111+ float vignette_amount(void )
112+ {
113+ if (vignette < 0.5 )
114+ return 1.0 ;
115+
116+ vec2 pos = vTexCoord * (1.0 - vTexCoord);
117+ float value = pos.x * pos.y * vstr;
118+ return min (pow (value, vpower), 1.0 );
119+ }
120+
121+ vec3 apply_saturation(vec3 color)
122+ {
123+ float luminance = length (color) * 0.5775 ;
124+ vec3 weights = vec3 (0.4 , 0.5 , 0.1 );
125+ if (luminance < 0.5 )
126+ weights = weights * weights + weights * weights;
127+
128+ luminance = dot (color, weights);
129+ return mix (vec3 (luminance), color, sat);
130+ }
131+
132+ vec3 glow_sample(vec2 texcoord)
133+ {
134+ vec2 size = SourceSize.zw / quality;
135+ vec3 sum = vec3 (0.0 );
136+ float factor = 1.0 / glow;
137+
138+ for (float x = - glow; x <= glow; x += 1.0 )
139+ {
140+ for (float y = - glow; y <= glow; y += 1.0 )
141+ {
142+ vec3 color = sample_source(texcoord + vec2 (x, y) * size) * factor;
143+ sum += color * color;
144+ }
145+ }
146+
147+ return glow_str * sum / (glow * glow);
148+ }
149+
150+ float noise(vec2 coord)
151+ {
152+ float timer = float (FrameCount) / 60.0 ;
153+ return fract (sin (timer * dot (coord.xy, vec2 (12.9898 , 78.233 ))) * 43758.5453 );
154+ }
155+
156+ float corner_mask(vec2 coord)
157+ {
158+ coord = min (coord, vec2 (1.0 ) - coord) * vec2 (1.0 , SourceSize.y / SourceSize.x);
159+ vec2 distance = vec2 (corner) - min (coord, vec2 (corner));
160+ return clamp ((corner - sqrt (dot (distance , distance ))) * smoothness, 0.0 , 1.0 );
161+ }
162+
163+ vec3 apply_color_temperature(vec3 color)
164+ {
165+ if (WP == 0.0 )
166+ return color;
167+
168+ vec3 warmer = XYZ_to_D65 * (D50_to_XYZ * color);
169+ vec3 cooler = XYZ_to_D50 * (D65_to_XYZ * color);
170+ float amount = abs (WP) / 100.0 ;
171+ vec3 target = WP < 0.0 ? cooler : warmer;
172+ return mix (color, clamp (target, 0.0 , 1.0 ), amount);
173+ }
174+
175+ void main()
176+ {
177+ vec2 pos = warp_position(vTexCoord.xy);
178+ if (outside_source(pos))
179+ {
180+ FragColor = vec4 (BackgroundColor.rgb, 1.0 );
181+ return ;
182+ }
183+
184+ vec2 tex_size = SourceSize.xy;
185+ float interlace_downscale = Downscale > 0.0 ? Downscale : 2.0 ;
186+ vec2 pixel_phase = fract (pos * tex_size);
187+ if (inter < 0.5 && tex_size.y > 400.0 )
188+ pixel_phase.y = fract (pos.y * tex_size.y / interlace_downscale);
189+
190+ vec2 texel = pos * tex_size;
191+ vec2 texel_floored = floor (texel);
192+ float region_range = 0.5 - 0.5 / PRE_SCALE;
193+ vec2 center_dist = pixel_phase - 0.5 ;
194+ vec2 filtered_phase = (center_dist - clamp (center_dist, - region_range, region_range)) * PRE_SCALE + 0.5 ;
195+ vec2 coords = (texel_floored + filtered_phase) / SourceSize.xy;
196+
197+ vec3 sample1 = sample_source(vec2 (coords.x + blurx * SourceSize.z, coords.y - blury * SourceSize.w));
198+ vec3 sample2 = sample_source(coords);
199+ vec3 sample3 = sample_source(vec2 (coords.x - blurx * SourceSize.z, coords.y + blury * SourceSize.w));
200+
201+ vec3 color = vec3 (sample1.r * 0.5 + sample2.r * 0.5 ,
202+ sample1.g * 0.25 + sample2.g * 0.5 + sample3.g * 0.25 ,
203+ sample2.b * 0.5 + sample3.b * 0.5 );
204+
205+ color = apply_color_temperature(color);
206+ color = 2.0 * pow (color, vec3 (2.8 )) - pow (color, vec3 (3.6 ));
207+
208+ float luminance = color.r * 0.3 + color.g * 0.6 + color.b * 0.1 ;
209+ float scan_pos = fract (pixel_phase.y - 0.5 );
210+ if (inter <= 0.5 || tex_size.y <= 400.0 )
211+ color = color * scanline_weight(scan_pos, luminance) + color * scanline_weight(1.0 - scan_pos, luminance);
212+
213+ float masked_luminance = color.r * 0.3 + color.g * 0.6 + color.b * 0.1 ;
214+ color *= default_mask(vTexCoord * OutputSize.xy, masked_luminance);
215+ color *= mix (brightboost1, brightboost2, max (max (color.r, color.g), color.b));
216+ color = pow (max (color, vec3 (0.0 )), vec3 (1.0 / GAMMA_OUT));
217+
218+ if (glow_str != 0.0 )
219+ color += glow_sample(coords);
220+ if (sat != 1.0 )
221+ color = apply_saturation(color);
222+ if (corner != 0.0 )
223+ color = mix (BackgroundColor.rgb, color, corner_mask(pos + 0.5 / tex_size));
224+ if (nois != 0.0 )
225+ color *= 1.0 + noise(coords * 2.0 ) / nois;
226+
227+ color *= mix (1.0 , postbr, luminance);
228+ vec4 result = vec4 (color, 1.0 );
229+ if (contrast != 1.0 )
230+ result = contrast_matrix() * result;
231+ if (inter > 0.5 && SourceSize.y > 400.0 && fract (float (FrameCount) / 2.0 ) < 0.5 )
232+ result *= 0.95 ;
233+
234+ result.rgb *= vignette_amount();
235+ FragColor = result;
236+ }
0 commit comments