@thenick775/mgba-wasm 2.4.0 → 2.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mgba.data ADDED
@@ -0,0 +1,3480 @@
1
+ varying vec2 texCoord;
2
+ uniform sampler2D tex;
3
+ uniform vec2 texSize;
4
+ uniform vec2 outputSize;
5
+
6
+ #ifdef GL_FRAGMENT_PRECISION_HIGH
7
+ precision highp float;
8
+ #else
9
+ precision mediump float;
10
+ #endif
11
+
12
+ void main() {
13
+ vec3 c = texture2D(tex, texCoord).rgb;
14
+
15
+ // gamma-ish shaping the original shader intended
16
+ c = pow(c * vec3(0.8), vec3(1.8)) + vec3(0.16);
17
+
18
+ // Stripe mask aligned to OUTPUT pixels (stable across scale factors)
19
+ float x = mod(floor(gl_FragCoord.x), 3.0);
20
+ vec3 maskX =
21
+ (x < 0.5) ? vec3(0.2, 0.2, 1.0) :
22
+ (x < 1.5) ? vec3(0.2, 1.0, 0.2) :
23
+ vec3(1.0, 0.2, 0.2);
24
+
25
+ float y = mod(floor(gl_FragCoord.y), 4.0);
26
+ vec3 maskY = (y < 3.5) ? vec3(1.0) : vec3(0.8);
27
+
28
+ c *= (maskX * maskY);
29
+
30
+ gl_FragColor = vec4(c, 1.0);
31
+ }
32
+ [shader]
33
+ name=AGB-001
34
+ author=endrift
35
+ description=A glorious recreation of the original Game Boy Advance
36
+ passes=1
37
+
38
+ [pass.0]
39
+ fragmentShader=agb001.fs
40
+ blend=1
41
+ width=-3
42
+ height=-4
43
+ varying vec2 texCoord;
44
+ uniform sampler2D tex;
45
+ uniform float reflectionBrightness;
46
+ uniform vec2 reflectionDistance;
47
+ uniform float lightBrightness;
48
+
49
+ const float speed = 2.0;
50
+ const float decay = 2.0;
51
+ const float coeff = 2.5;
52
+
53
+ void main() {
54
+ float sp = pow(speed, lightBrightness);
55
+ float dc = pow(decay, -lightBrightness);
56
+ float s = (sp - dc) / (sp + dc);
57
+ vec2 radius = (texCoord.st - vec2(0.5, 0.5)) * vec2(coeff * s);
58
+ radius = pow(abs(radius), vec2(4.0));
59
+ vec3 bleed = vec3(0.12, 0.14, 0.19);
60
+ bleed += (dot(radius, radius) + vec3(0.02, 0.03, 0.05)) * vec3(0.14, 0.18, 0.2);
61
+
62
+ vec4 color = texture2D(tex, texCoord);
63
+ color.rgb += pow(bleed, pow(vec3(lightBrightness), vec3(-0.5)));
64
+
65
+ vec4 reflection = texture2D(tex, texCoord - reflectionDistance);
66
+ color.rgb += reflection.rgb * reflectionBrightness;
67
+ color.a = 1.0;
68
+ gl_FragColor = color;
69
+ }
70
+ varying vec2 texCoord;
71
+ uniform sampler2D tex;
72
+ uniform vec2 texSize;
73
+
74
+ const vec3 kArrayX0 = vec3(0.2, 0.2, 1.0);
75
+ const vec3 kArrayX1 = vec3(0.2, 1.0, 0.2);
76
+ const vec3 kArrayX2 = vec3(1.0, 0.2, 0.2);
77
+ const vec3 kArrayY0 = vec3(1.0, 1.0, 1.0);
78
+ const vec3 kArrayY1 = vec3(1.0, 1.0, 1.0);
79
+ const vec3 kArrayY2 = vec3(1.0, 1.0, 1.0);
80
+ const vec3 kArrayY3 = vec3(0.9, 0.9, 0.9);
81
+
82
+
83
+ void main() {
84
+ vec4 color = texture2D(tex, texCoord);
85
+ // WebGL1-safe: precomputed constant LUTs (no runtime array writes)
86
+ color.rgb = pow(color.rgb, vec3(1.6, 1.6, 1.6));
87
+ int xi = int(mod(texCoord.s * texSize.x * 3.0, 3.0));
88
+ vec3 ax = (xi == 0) ? kArrayX0 : ((xi == 1) ? kArrayX1 : kArrayX2);
89
+ int yi = int(mod(texCoord.t * texSize.y * 4.0, 4.0));
90
+ vec3 ay = (yi == 0) ? kArrayY0 : ((yi == 1) ? kArrayY1 : ((yi == 2) ? kArrayY2 : kArrayY3));
91
+ color.rgb *= ax;
92
+ color.rgb *= ay;
93
+ color.a = 0.8;
94
+ gl_FragColor = color;
95
+ }
96
+ [shader]
97
+ name=AGS-001
98
+ author=endrift
99
+ description=A pristine recreation of the illuminated Game Boy Advance SP
100
+ passes=2
101
+
102
+ [pass.0]
103
+ fragmentShader=ags001.fs
104
+ blend=1
105
+ width=-3
106
+ height=-4
107
+
108
+ [pass.1]
109
+ fragmentShader=ags001-light.fs
110
+ width=-3
111
+ height=-4
112
+
113
+ [pass.1.uniform.lightBrightness]
114
+ type=float
115
+ default=1
116
+ readableName=Light brightness
117
+
118
+ [pass.1.uniform.reflectionBrightness]
119
+ type=float
120
+ default=0.07
121
+ readableName=Reflection brightness
122
+
123
+ [pass.1.uniform.reflectionDistance]
124
+ type=float2
125
+ default[0]=0
126
+ default[1]=0.025
127
+ readableName=Reflection distance
128
+ // Shader that replicates the LCD Colorspace from a Nintendo DS Lite --
129
+ varying vec2 texCoord;
130
+ varying mat4 profile;
131
+ uniform sampler2D tex;
132
+ uniform vec2 texSize;
133
+
134
+ const float target_gamma = 2.2;
135
+ const float display_gamma = 2.2;
136
+
137
+ void main() {
138
+ // bring out our stored luminance value
139
+ float lum = profile[3].w;
140
+
141
+ // our adjustments need to happen in linear gamma
142
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma)).rgba;
143
+
144
+ screen = clamp(screen * lum, 0.0, 1.0);
145
+ screen = profile * screen;
146
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
147
+ }
148
+ uniform int color_mode;
149
+ attribute vec4 position;
150
+ varying vec2 texCoord;
151
+ varying mat4 profile;
152
+
153
+ const mat4 DSL_sRGB = mat4(
154
+ 0.93, 0.025, 0.008, 0.0, //red channel
155
+ 0.14, 0.90, -0.03, 0.0, //green channel
156
+ -0.07, 0.075, 1.022, 0.0, //blue channel
157
+ 0.0, 0.0, 0.0, 0.935 //alpha channel
158
+ );
159
+
160
+ const mat4 DSL_DCI = mat4(
161
+ 0.76, 0.055, 0.0225, 0.0, //red channel
162
+ 0.27, 0.875, 0.0225, 0.0, //green channel
163
+ -0.03, 0.07, 0.955, 0.0, //blue channel
164
+ 0.0, 0.0, 0.0, 0.97 //alpha channel
165
+ );
166
+
167
+ const mat4 DSL_Rec2020 = mat4(
168
+ 0.585, 0.09, 0.0225, 0.0, //red channel
169
+ 0.3725, 0.825, 0.035, 0.0, //green channel
170
+ 0.0425, 0.085, 0.9425, 0.0, //blue channel
171
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
172
+ );
173
+
174
+ void main() {
175
+ if (color_mode == 1) profile = DSL_sRGB;
176
+ else if (color_mode == 2) profile = DSL_DCI;
177
+ else if (color_mode == 3) profile = DSL_Rec2020;
178
+
179
+ gl_Position = position;
180
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
181
+ }
182
+ [shader]
183
+ name=Nintendo DS Lite Color
184
+ author=Pokefan531 and hunterk
185
+ description=Shader that replicates the LCD Colorspace from a Nintendo DS Lite.
186
+ passes=1
187
+
188
+ [pass.0]
189
+ fragmentShader=dslite-color.fs
190
+ vertexShader=dslite-color.vs
191
+ blend=1
192
+ width=-1
193
+ height=-1
194
+
195
+ [pass.0.uniform.color_mode]
196
+ type=int
197
+ default=1
198
+ min=1
199
+ max=3
200
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
201
+ /*
202
+ fish shader
203
+
204
+ algorithm and original implementation by Miloslav "drummyfish" Ciz
205
+ (tastyfish@seznam.cz)
206
+
207
+ Permission is hereby granted, free of charge, to any person obtaining a copy
208
+ of this software and associated documentation files (the "Software"), to deal
209
+ in the Software without restriction, including without limitation the rights
210
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
211
+ copies of the Software, and to permit persons to whom the Software is
212
+ furnished to do so, subject to the following conditions:
213
+
214
+ The above copyright notice and this permission notice shall be included in
215
+ all copies or substantial portions of the Software.
216
+
217
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
218
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
219
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
220
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
221
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
222
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
223
+ THE SOFTWARE.
224
+ */
225
+
226
+ uniform sampler2D tex;
227
+ uniform vec2 texSize;
228
+ varying vec2 texCoord;
229
+
230
+ uniform float similarity_threshold;
231
+
232
+ vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported
233
+ {
234
+ return texture2D(t, (2.0 * vec2(c) + vec2(1.0,1.0)) / (2.0 * texSize));
235
+ }
236
+
237
+ float pixel_brightness(vec4 pixel)
238
+ {
239
+ return 0.21 * pixel.x + 0.72 * pixel.y + 0.07 * pixel.z;
240
+ }
241
+
242
+ bool pixel_is_brighter(vec4 pixel1, vec4 pixel2)
243
+ {
244
+ return pixel_brightness(pixel1) > pixel_brightness(pixel2);
245
+ }
246
+
247
+ vec3 pixel_to_yuv(vec4 pixel)
248
+ {
249
+ float y = 0.299 * pixel.x + 0.587 * pixel.y + 0.114 * pixel.z;
250
+ return vec3(y, 0.492 * (pixel.z - y), 0.877 * (pixel.x - y));
251
+ }
252
+
253
+ bool yuvs_are_similar(vec3 yuv1, vec3 yuv2)
254
+ {
255
+ vec3 yuv_difference = abs(yuv1 - yuv2);
256
+ return yuv_difference.x <= similarity_threshold && yuv_difference.y <= similarity_threshold && yuv_difference.z <= similarity_threshold;
257
+ }
258
+
259
+ bool pixels_are_similar(vec4 pixel1, vec4 pixel2)
260
+ {
261
+ vec3 yuv1 = pixel_to_yuv(pixel1);
262
+ vec3 yuv2 = pixel_to_yuv(pixel2);
263
+
264
+ return yuvs_are_similar(yuv1, yuv2);
265
+ }
266
+
267
+ vec4 interpolate_nondiagonal(vec4 neighbour1, vec4 neighbour2)
268
+ {
269
+ if (pixels_are_similar(neighbour1,neighbour2))
270
+ return mix(neighbour1,neighbour2,0.5);
271
+ else
272
+ return pixel_is_brighter(neighbour1, neighbour2) ? neighbour1 : neighbour2;
273
+ }
274
+
275
+ vec4 mix3(vec4 value1, vec4 value2, vec4 value3)
276
+ {
277
+ return (value1 + value2 + value3) / 3.0;
278
+ }
279
+
280
+ vec4 straight_line(vec4 p0, vec4 p1, vec4 p2, vec4 p3)
281
+ {
282
+ return pixel_is_brighter(p2,p0) ? mix(p2,p3,0.5) : mix(p0,p1,0.5);
283
+ }
284
+
285
+ vec4 corner(vec4 p0, vec4 p1, vec4 p2, vec4 p3)
286
+ {
287
+ return pixel_is_brighter(p1,p0) ? mix3(p1,p2,p3) : mix3(p0,p1,p2);
288
+ }
289
+
290
+ vec4 interpolate_diagonal(vec4 a, vec4 b, vec4 c, vec4 d)
291
+ {
292
+ // a b
293
+ // c d
294
+
295
+ vec3 a_yuv = pixel_to_yuv(a);
296
+ vec3 b_yuv = pixel_to_yuv(b);
297
+ vec3 c_yuv = pixel_to_yuv(c);
298
+ vec3 d_yuv = pixel_to_yuv(d);
299
+
300
+ bool ad = yuvs_are_similar(a_yuv,d_yuv);
301
+ bool bc = yuvs_are_similar(b_yuv,c_yuv);
302
+ bool ab = yuvs_are_similar(a_yuv,b_yuv);
303
+ bool cd = yuvs_are_similar(c_yuv,d_yuv);
304
+ bool ac = yuvs_are_similar(a_yuv,c_yuv);
305
+ bool bd = yuvs_are_similar(b_yuv,d_yuv);
306
+
307
+ if (ad && cd && ab) // all pixels are equal?
308
+ return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) );
309
+
310
+ else if (ac && cd && ! ab) // corner 1?
311
+ return corner(b,a,d,c);
312
+ else if (bd && cd && ! ab) // corner 2?
313
+ return corner(a,b,c,d);
314
+ else if (ac && ab && ! bd) // corner 3?
315
+ return corner(d,c,b,a);
316
+ else if (ab && bd && ! ac) // corner 4?
317
+ return corner(c,a,d,b);
318
+
319
+ else if (ad && (!bc || pixel_is_brighter(b,a))) // diagonal line 1?
320
+ return mix(a,d,0.5);
321
+ else if (bc && (!ad || pixel_is_brighter(a,b))) // diagonal line 2?
322
+ return mix(b,c,0.5);
323
+
324
+ else if (ab) // horizontal line 1?
325
+ return straight_line(a,b,c,d);
326
+ else if (cd) // horizontal line 2?
327
+ return straight_line(c,d,a,b);
328
+
329
+ else if (ac) // vertical line 1?
330
+ return straight_line(a,c,b,d);
331
+ else if (bd) // vertical line 2?
332
+ return straight_line(b,d,a,c);
333
+
334
+ return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) );
335
+ }
336
+
337
+ void main()
338
+ {
339
+ ivec2 pixel_coords2 = ivec2(vec2(texCoord) * vec2(texSize) * 2.0);
340
+ ivec2 pixel_coords = pixel_coords2 / 2;
341
+
342
+ bool x_even = (pixel_coords2.x - (pixel_coords2.x / 2) * 2) == 0;
343
+ bool y_even = (pixel_coords2.y - (pixel_coords2.y / 2) * 2) == 0;
344
+
345
+ if (x_even)
346
+ {
347
+ if (y_even)
348
+ {
349
+
350
+ gl_FragColor = interpolate_diagonal(
351
+ texel_fetch(tex, pixel_coords + ivec2(-1,-1)),
352
+ texel_fetch(tex, pixel_coords + ivec2(0,-1)),
353
+ texel_fetch(tex, pixel_coords + ivec2(-1,0)),
354
+ texel_fetch(tex, pixel_coords + ivec2(0,0))
355
+ );
356
+
357
+ }
358
+ else
359
+ {
360
+ gl_FragColor = interpolate_nondiagonal
361
+ (
362
+ texel_fetch(tex, pixel_coords + ivec2(-1,0)),
363
+ texel_fetch(tex, pixel_coords)
364
+ );
365
+ }
366
+ }
367
+ else if (y_even)
368
+ {
369
+ gl_FragColor = interpolate_nondiagonal
370
+ (
371
+ texel_fetch(tex, pixel_coords + ivec2(0,-1)),
372
+ texel_fetch(tex, pixel_coords)
373
+ );
374
+ }
375
+ else
376
+ gl_FragColor = texel_fetch(tex, pixel_coords);
377
+ }
378
+ [shader]
379
+ name=fish
380
+ author=Drummyfish
381
+ description=Attempts to keep thin lines thin.
382
+ passes=1
383
+
384
+ [pass.0]
385
+ fragmentShader=fish.fs
386
+ integerScaling=1
387
+
388
+ [pass.0.uniform.similarity_threshold]
389
+ type=float
390
+ default=0.2
391
+ readableName=Similarity Threshold
392
+ min=0
393
+ max=1
394
+ // Shader that replicates the LCD Colorspace from a Gameboy Micro (OXY-001) --
395
+ varying vec2 texCoord;
396
+ varying mat4 profile;
397
+ uniform sampler2D tex;
398
+ uniform vec2 texSize;
399
+
400
+ const float target_gamma = 2.2;
401
+ const float display_gamma = 2.2;
402
+
403
+ void main() {
404
+ // bring out our stored luminance value
405
+ float lum = profile[3].w;
406
+
407
+ // our adjustments need to happen in linear gamma
408
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma)).rgba;
409
+
410
+ screen = clamp(screen * lum, 0.0, 1.0);
411
+ screen = profile * screen;
412
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
413
+ }
414
+ uniform int color_mode;
415
+ attribute vec4 position;
416
+ varying vec2 texCoord;
417
+ varying mat4 profile;
418
+
419
+ const mat4 GBM_sRGB = mat4(
420
+ 0.8025, 0.10, 0.1225, 0.0, //red channel
421
+ 0.31, 0.6875, 0.1125, 0.0, //green channel
422
+ -0.1125, 0.2125, 0.765, 0.0, //blue channel
423
+ 0.0, 0.0, 0.0, 0.9 //alpha channel
424
+ );
425
+
426
+ const mat4 GBM_DCI = mat4(
427
+ 0.6675, 0.125, 0.13, 0.0, //red channel
428
+ 0.3825, 0.675, 0.1475, 0.0, //green channel
429
+ -0.05, 0.20, 0.7225, 0.0, //blue channel
430
+ 0.0, 0.0, 0.0, 0.96 //alpha channel
431
+ );
432
+
433
+ const mat4 GBM_Rec2020 = mat4(
434
+ 0.525, 0.15, 0.13, 0.0, //red channel
435
+ 0.43, 0.65, 0.155, 0.0, //green channel
436
+ 0.045, 0.20, 0.715, 0.0, //blue channel
437
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
438
+ );
439
+
440
+ void main() {
441
+ if (color_mode == 1) profile = GBM_sRGB;
442
+ else if (color_mode == 2) profile = GBM_DCI;
443
+ else if (color_mode == 3) profile = GBM_Rec2020;
444
+
445
+ gl_Position = position;
446
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
447
+ }
448
+ [shader]
449
+ name=GB Micro Color
450
+ author=Pokefan531 and hunterk
451
+ description=Shader that replicates the LCD Colorspace from a Gameboy Micro (OXY-001).
452
+ passes=1
453
+
454
+ [pass.0]
455
+ fragmentShader=gb-micro-color.fs
456
+ vertexShader=gb-micro-color.vs
457
+ blend=1
458
+ width=-1
459
+ height=-1
460
+
461
+ [pass.0.uniform.color_mode]
462
+ type=int
463
+ default=1
464
+ min=1
465
+ max=3
466
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
467
+ // Shader that replicates the LCD Colorspace from Gameboy Advance --
468
+ varying vec2 texCoord;
469
+ varying mat4 profile;
470
+ uniform sampler2D tex;
471
+ uniform vec2 texSize;
472
+
473
+ uniform float darken_screen;
474
+ const float target_gamma = 2.0;
475
+ const float display_gamma = 2.0;
476
+
477
+ void main() {
478
+ // bring out our stored luminance value
479
+ float lum = profile[3].w;
480
+
481
+ // our adjustments need to happen in linear gamma
482
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba;
483
+
484
+ screen = clamp(screen * lum, 0.0, 1.0);
485
+ screen = profile * screen;
486
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
487
+ }
488
+ uniform int color_mode;
489
+ attribute vec4 position;
490
+ varying vec2 texCoord;
491
+ varying mat4 profile;
492
+
493
+ const mat4 GBA_sRGB = mat4(
494
+ 0.80, 0.135, 0.195, 0.0, //red channel
495
+ 0.275, 0.64, 0.155, 0.0, //green channel
496
+ -0.075, 0.225, 0.65, 0.0, //blue channel
497
+ 0.0, 0.0, 0.0, 0.93 //alpha channel
498
+ );
499
+
500
+ const mat4 GBA_DCI = mat4(
501
+ 0.685, 0.16, 0.20, 0.0, //red channel
502
+ 0.34, 0.629, 0.19, 0.0, //green channel
503
+ -0.025, 0.211, 0.61, 0.0, //blue channel
504
+ 0.0, 0.0, 0.0, 0.975 //alpha channel
505
+ );
506
+
507
+ const mat4 GBA_Rec2020 = mat4(
508
+ 0.555, 0.1825, 0.20, 0.0, //red channel
509
+ 0.395, 0.61, 0.195, 0.0, //green channel
510
+ 0.05, 0.2075, 0.605, 0.0, //blue channel
511
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
512
+ );
513
+
514
+ void main() {
515
+ if (color_mode == 1) profile = GBA_sRGB;
516
+ else if (color_mode == 2) profile = GBA_DCI;
517
+ else if (color_mode == 3) profile = GBA_Rec2020;
518
+
519
+ gl_Position = position;
520
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
521
+ }
522
+ [shader]
523
+ name=GBA Color
524
+ author=Pokefan531 and hunterk
525
+ description=Modifies the color output to simulate the GBA LCD characteristics.
526
+ passes=1
527
+
528
+ [pass.0]
529
+ fragmentShader=gba-color.fs
530
+ vertexShader=gba-color.vs
531
+ blend=1
532
+ width=-1
533
+ height=-1
534
+
535
+ [pass.0.uniform.darken_screen]
536
+ type=float
537
+ default=0.5
538
+ min=0
539
+ max=1
540
+ readableName=Darken Screen
541
+
542
+ [pass.0.uniform.color_mode]
543
+ type=int
544
+ default=1
545
+ min=1
546
+ max=3
547
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
548
+ // Shader that replicates the LCD Colorspace from Gameboy Color --
549
+ varying vec2 texCoord;
550
+ varying mat4 profile;
551
+ uniform sampler2D tex;
552
+ uniform vec2 texSize;
553
+
554
+ uniform float lighten_screen;
555
+ const float target_gamma = 2.2;
556
+ const float display_gamma = 2.2;
557
+
558
+ void main() {
559
+ // bring out our stored luminance value
560
+ float lum = profile[3].w;
561
+
562
+ // our adjustments need to happen in linear gamma
563
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma - lighten_screen)).rgba;
564
+
565
+ screen = clamp(screen * lum, 0.0, 1.0);
566
+ screen = profile * screen;
567
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
568
+ }
569
+ uniform int color_mode;
570
+ attribute vec4 position;
571
+ varying vec2 texCoord;
572
+ varying mat4 profile;
573
+
574
+ const mat4 GBC_sRGB = mat4(
575
+ 0.905, 0.10, 0.1575, 0.0, //red channel
576
+ 0.195, 0.65, 0.1425, 0.0, //green channel
577
+ -0.10, 0.25, 0.70, 0.0, //blue channel
578
+ 0.0, 0.0, 0.0, 0.91 //alpha channel
579
+ );
580
+
581
+ const mat4 GBC_DCI = mat4(
582
+ 0.76, 0.125, 0.16, 0.0, //red channel
583
+ 0.27, 0.6375, 0.18, 0.0, //green channel
584
+ -0.03, 0.2375, 0.66, 0.0, //blue channel
585
+ 0.0, 0.0, 0.0, 0.97 //alpha channel
586
+ );
587
+
588
+ const mat4 GBC_Rec2020 = mat4(
589
+ 0.61, 0.155, 0.16, 0.0, //red channel
590
+ 0.345, 0.615, 0.1875, 0.0, //green channel
591
+ 0.045, 0.23, 0.6525, 0.0, //blue channel
592
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
593
+ );
594
+
595
+ void main() {
596
+ if (color_mode == 1) profile = GBC_sRGB;
597
+ else if (color_mode == 2) profile = GBC_DCI;
598
+ else if (color_mode == 3) profile = GBC_Rec2020;
599
+
600
+ gl_Position = position;
601
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
602
+ }
603
+ [shader]
604
+ name=GBC Color
605
+ author=Pokefan531 and hunterk
606
+ description=Modifies the color output to simulate the GBA LCD characteristics.
607
+ passes=1
608
+
609
+ [pass.0]
610
+ fragmentShader=gbc-color.fs
611
+ vertexShader=gbc-color.vs
612
+ blend=1
613
+ width=-1
614
+ height=-1
615
+
616
+ [pass.0.uniform.lighten_screen]
617
+ type=float
618
+ default=1.0
619
+ min=0
620
+ max=1
621
+ readableName=External Lighten Screen
622
+
623
+ [pass.0.uniform.color_mode]
624
+ type=int
625
+ default=1
626
+ min=1
627
+ max=3
628
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
629
+ /**
630
+ * This shader creates a backlight bleeding effect,
631
+ * and an internal reflection or ghosting effect.
632
+ */
633
+
634
+ varying vec2 texCoord;
635
+ uniform sampler2D tex;
636
+
637
+ /**
638
+ * Determines the color of the backlight bleed.
639
+ * Lower values produce less, dimmer light.
640
+ * Higher values produce brighter or more colorful light.
641
+ * You'll normally want each of these numbers to be close
642
+ * to 1, and not normally lower than 0.
643
+ */
644
+ uniform vec3 LightColor;
645
+
646
+ /**
647
+ * Affects the shape of the backlight bleed glow.
648
+ * Lower values cause the light bleed to fade out quickly
649
+ * from the edges.
650
+ * Higher values cause the light bleed to fade out more
651
+ * softly and gradually toward the center.
652
+ * You'll normally want this to be a number from 0 to 1.
653
+ */
654
+ uniform float LightSoftness;
655
+
656
+ /**
657
+ * Lower values result in a less visible or intense
658
+ * backlight bleed.
659
+ * Higher values make the backlight bleed more pronounced.
660
+ * You'll normally want this to be a number close to 0,
661
+ * and not normally higher than 1.
662
+ */
663
+ uniform float LightIntensity;
664
+
665
+ /**
666
+ * Lower values cause the internal reflection or ghosting
667
+ * effect to be less visible.
668
+ * Higher values cause the effect to be brighter and more
669
+ * visible.
670
+ * You'll normally want this to be a number close to 0,
671
+ * and not normally higher than 1.
672
+ */
673
+ uniform float ReflectionBrightness;
674
+
675
+ /**
676
+ * Lower values have the internal reflection or ghosting
677
+ * effect appear offset by a lesser distance.
678
+ * Higher values have the effect offset by a greater
679
+ * distance.
680
+ * You'll normally want each of these numbers to be close
681
+ * to 0, and not normally higher than 1.
682
+ */
683
+ uniform vec2 ReflectionDistance;
684
+
685
+ #define M_PI 3.1415926535897932384626433832795
686
+
687
+ /**
688
+ * Helper to compute backlight bleed intensity
689
+ * for a texCoord input.
690
+ */
691
+ float getLightIntensity(vec2 coord) {
692
+ vec2 coordCentered = coord - vec2(0.5, 0.5);
693
+ float coordDistCenter = (
694
+ length(coordCentered) / sqrt(0.5)
695
+ );
696
+ vec2 coordQuadrant = vec2(
697
+ 1.0 - (1.5 * min(coord.x, 1.0 - coord.x)),
698
+ 1.0 - (1.5 * min(coord.y, 1.0 - coord.y))
699
+ );
700
+ float lightIntensityEdges = (
701
+ pow(coordQuadrant.x, 5.0) +
702
+ pow(coordQuadrant.y, 5.0)
703
+ );
704
+ float lightIntensity = (
705
+ (1.0 - LightSoftness) * lightIntensityEdges +
706
+ LightSoftness * coordDistCenter
707
+ );
708
+ return clamp(lightIntensity, 0.0, 1.0);
709
+ }
710
+
711
+ /**
712
+ * Helper to convert an intensity value into a white
713
+ * gray color with that intensity. A radial distortion
714
+ * effect with subtle chromatic abberation is applied that
715
+ * makes it look a little more like a real old or cheap
716
+ * backlight, and also helps to reduce color banding.
717
+ */
718
+ vec3 getWhiteVector(float intensity) {
719
+ const float DeformAmount = 0.0025;
720
+ vec2 texCoordCentered = texCoord - vec2(0.5, 0.5);
721
+ float radians = atan(texCoordCentered.y, texCoordCentered.x);
722
+ float rot = pow(2.0, 4.0 + floor(6.0 * length(texCoordCentered)));
723
+ float deformRed = cos(rot * radians + (2.0 / 3.0 * M_PI));
724
+ float deformGreen = cos(rot * radians);
725
+ float deformBlue = cos(rot * radians + (4.0 / 3.0 * M_PI));
726
+ return clamp(vec3(
727
+ intensity + (deformRed * DeformAmount),
728
+ intensity + (deformGreen * DeformAmount),
729
+ intensity + (deformBlue * DeformAmount)
730
+ ), 0.0, 1.0);
731
+ }
732
+
733
+ void main() {
734
+ vec3 colorSource = texture2D(tex, texCoord).rgb;
735
+ vec3 lightWhiteVector = getWhiteVector(getLightIntensity(texCoord));
736
+ vec3 colorLight = LightColor * lightWhiteVector;
737
+ vec3 colorReflection = texture2D(tex, texCoord - ReflectionDistance).rgb;
738
+ vec3 colorResult = (
739
+ colorSource +
740
+ (colorLight * LightIntensity) +
741
+ (colorReflection * ReflectionBrightness)
742
+ );
743
+ gl_FragColor = vec4(
744
+ colorResult,
745
+ 1.0
746
+ );
747
+ }
748
+ /**
749
+ * This shader imitates the GameBoy Color subpixel
750
+ * arrangement.
751
+ */
752
+
753
+ varying vec2 texCoord;
754
+ uniform sampler2D tex;
755
+ uniform vec2 texSize;
756
+
757
+ /**
758
+ * Adds a base color to everything.
759
+ * Lower values make black colors darker.
760
+ * Higher values make black colors lighter.
761
+ * You'll normally want each of these numbers to be close
762
+ * to 0, and not normally higher than 1.
763
+ */
764
+ uniform vec3 BaseColor;
765
+
766
+ /**
767
+ * Modifies the contrast or saturation of the image.
768
+ * Lower values make the image more gray and higher values
769
+ * make it more colorful.
770
+ * A value of 1 represents a normal, baseline level of
771
+ * contrast.
772
+ * You'll normally want this to be somewhere around 1.
773
+ */
774
+ uniform float SourceContrast;
775
+
776
+ /**
777
+ * Modifies the luminosity of the image.
778
+ * Lower values make the image darker and higher values make
779
+ * it lighter.
780
+ * A value of 1 represents normal, baseline luminosity.
781
+ * You'll normally want this to be somewhere around 1.
782
+ */
783
+ uniform float SourceLuminosity;
784
+
785
+ /**
786
+ * Lower values look more like a sharp, unshaded image.
787
+ * Higher values look more like an LCD display with subpixels.
788
+ * You'll normally want this to be a number from 0 to 1.
789
+ */
790
+ uniform float SubpixelBlendAmount;
791
+
792
+ /**
793
+ * Lower values make subpixels darker.
794
+ * Higher values make them lighter and over-bright.
795
+ * A value of 1 represents a normal, baseline gamma value.
796
+ * You'll normally want this to be somewhere around 1.
797
+ */
798
+ uniform float SubpixelGamma;
799
+
800
+ /**
801
+ * Higher values allow subpixels to be more blended-together
802
+ * and brighter.
803
+ * Lower values keep subpixel colors more separated.
804
+ * You'll normally want this to be a number from 0 to 1.
805
+ */
806
+ uniform float SubpixelColorBleed;
807
+
808
+ /**
809
+ * Determines the distance between subpixels.
810
+ * Lower values put the red, green, and blue subpixels
811
+ * within a single pixel closer together.
812
+ * Higher values put them farther apart.
813
+ * You'll normally want this to be a number from 0 to 1.
814
+ */
815
+ uniform float SubpixelSpread;
816
+
817
+ /**
818
+ * Determines the vertical offset of subpixels within
819
+ * a pixel.
820
+ * Lower values put the red, green, and blue subpixels
821
+ * within a single pixel higher up.
822
+ * Higher values put them further down.
823
+ * You'll normally want this to be a number from 0 to 1.
824
+ */
825
+ uniform float SubpixelVerticalOffset;
826
+
827
+ /**
828
+ * Lower values make the subpixels horizontally thinner,
829
+ * and higher values make them thicker.
830
+ * You'll normally want this to be a number from 0 to 1.
831
+ */
832
+ uniform float SubpixelLightWidth;
833
+
834
+ /**
835
+ * Lower values make the subpixels vertically taller,
836
+ * and higher values make them shorter.
837
+ * You'll normally want this to be a number from 0 to 1.
838
+ */
839
+ uniform float SubpixelLightHeight;
840
+
841
+ /**
842
+ * Lower values make the subpixels sharper and more
843
+ * individually distinct.
844
+ * Higher values add an increasingly intense glowing
845
+ * effect around each subpixel.
846
+ * You'll normally want this to be a number from 0 to 1.
847
+ */
848
+ uniform float SubpixelLightGlow;
849
+
850
+ /**
851
+ * Scale the size of pixels up or down.
852
+ * Useful for looking at larger than 8x8 subpixel sizes.
853
+ * You'll normally want this number to be exactly 1,
854
+ * meaning that every group of 3 subpixels corresponds
855
+ * to one pixel in the display.
856
+ */
857
+ uniform float SubpixelScale;
858
+
859
+ /**
860
+ * GBC subpixels are roughly rectangular shaped, but
861
+ * with a rectangular gap in the lower-right corner.
862
+ * Lower values make the lower-right gap in each GBC
863
+ * subpixel less distinct. A value of 0 results in no
864
+ * gap being shown at all.
865
+ * Higher values make the gap more distinct.
866
+ * You'll normally want this to be a number from 0 to 1.
867
+ */
868
+ uniform float SubpixelTabHeight;
869
+
870
+ /**
871
+ * The following three uniforms decide the base colors
872
+ * of each of the subpixels.
873
+ *
874
+ * Default subpixel colors are based on this resource:
875
+ * https://gbcc.dev/technology/
876
+ * R: #FF7145 (1.00, 0.44, 0.27)
877
+ * G: #C1D650 (0.75, 0.84, 0.31)
878
+ * B: #3BCEFF (0.23, 0.81, 1.00)
879
+ */
880
+ uniform vec3 SubpixelColorRed; // vec3(1.00, 0.38, 0.22);
881
+ uniform vec3 SubpixelColorGreen; // vec3(0.60, 0.88, 0.30);
882
+ uniform vec3 SubpixelColorBlue; // vec3(0.23, 0.65, 1.00);
883
+
884
+ /**
885
+ * Helper to get luminosity of an RGB color.
886
+ * Used with HCL color space related code.
887
+ */
888
+ float getColorLumosity(in vec3 rgb) {
889
+ return (
890
+ (rgb.r * (5.0 / 16.0)) +
891
+ (rgb.g * (9.0 / 16.0)) +
892
+ (rgb.b * (2.0 / 16.0))
893
+ );
894
+ }
895
+
896
+ /**
897
+ * Helper to convert RGB color to HCL. (Hue, Chroma, Luma)
898
+ */
899
+ vec3 convertRgbToHcl(in vec3 rgb) {
900
+ float xMin = min(rgb.r, min(rgb.g, rgb.b));
901
+ float xMax = max(rgb.r, max(rgb.g, rgb.b));
902
+ float c = xMax - xMin;
903
+ float l = getColorLumosity(rgb);
904
+ float h = mod((
905
+ c == 0 ? 0.0 :
906
+ xMax == rgb.r ? ((rgb.g - rgb.b) / c) :
907
+ xMax == rgb.g ? ((rgb.b - rgb.r) / c) + 2.0 :
908
+ xMax == rgb.b ? ((rgb.r - rgb.g) / c) + 4.0 :
909
+ 0.0
910
+ ), 6.0);
911
+ return vec3(h, c, l);
912
+ }
913
+
914
+ /**
915
+ * Helper to convert HCL color to RGB. (Hue, Chroma, Luma)
916
+ */
917
+ vec3 convertHclToRgb(in vec3 hcl) {
918
+ vec3 rgb;
919
+ float h = mod(hcl.x, 6.0);
920
+ float c = hcl.y;
921
+ float l = hcl.z;
922
+ float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));
923
+ if(h <= 1.0) {
924
+ rgb = vec3(c, x, 0.0);
925
+ }
926
+ else if(h <= 2.0) {
927
+ rgb = vec3(x, c, 0.0);
928
+ }
929
+ else if(h <= 3.0) {
930
+ rgb = vec3(0.0, c, x);
931
+ }
932
+ else if(h <= 4.0) {
933
+ rgb = vec3(0.0, x, c);
934
+ }
935
+ else if(h <= 5.0) {
936
+ rgb = vec3(x, 0.0, c);
937
+ }
938
+ else {
939
+ rgb = vec3(c, 0.0, x);
940
+ }
941
+ float lRgb = getColorLumosity(rgb);
942
+ float m = l - lRgb;
943
+ return clamp(vec3(m, m, m) + rgb, 0.0, 1.0);
944
+ }
945
+
946
+ /**
947
+ * Helper to check if a point is contained within
948
+ * a rectangular area.
949
+ */
950
+ bool getPointInRect(
951
+ vec2 point,
952
+ vec2 rectTopLeft,
953
+ vec2 rectBottomRight
954
+ ) {
955
+ return (
956
+ point.x >= rectTopLeft.x &&
957
+ point.y >= rectTopLeft.y &&
958
+ point.x <= rectBottomRight.x &&
959
+ point.y <= rectBottomRight.y
960
+ );
961
+ }
962
+
963
+ /**
964
+ * Helper to get the nearest offset vector from a
965
+ * point to a line segment.
966
+ * (The length of this offset vector is the nearest
967
+ * distance from the point to the line segment.)
968
+ * Thank you to https://stackoverflow.com/a/1501725
969
+ */
970
+ vec2 getPointLineDistance(
971
+ vec2 point,
972
+ vec2 line0,
973
+ vec2 line1
974
+ ) {
975
+ vec2 lineDelta = line0 - line1;
976
+ float lineLengthSq = dot(lineDelta, lineDelta);
977
+ if(lineLengthSq <= 0) {
978
+ return line0 - point;
979
+ }
980
+ float t = (
981
+ dot(point - line0, line1 - line0) / lineLengthSq
982
+ );
983
+ vec2 projection = (
984
+ line0 + clamp(t, 0.0, 1.0) * (line1 - line0)
985
+ );
986
+ return projection - point;
987
+ }
988
+
989
+ /**
990
+ * Helper to get the nearest offset vector from a
991
+ * point to a rectangle.
992
+ * Returns (0, 0) for points within the rectangle.
993
+ */
994
+ vec2 getPointRectDistance(
995
+ vec2 point,
996
+ vec2 rectTopLeft,
997
+ vec2 rectBottomRight
998
+ ) {
999
+ if(getPointInRect(point, rectTopLeft, rectBottomRight)) {
1000
+ return vec2(0.0, 0.0);
1001
+ }
1002
+ vec2 rectTopRight = vec2(rectBottomRight.x, rectTopLeft.y);
1003
+ vec2 rectBottomLeft = vec2(rectTopLeft.x, rectBottomRight.y);
1004
+ vec2 v0 = getPointLineDistance(point, rectTopLeft, rectTopRight);
1005
+ vec2 v1 = getPointLineDistance(point, rectBottomLeft, rectBottomRight);
1006
+ vec2 v2 = getPointLineDistance(point, rectTopLeft, rectBottomLeft);
1007
+ vec2 v3 = getPointLineDistance(point, rectTopRight, rectBottomRight);
1008
+ float v0LengthSq = dot(v0, v0);
1009
+ float v1LengthSq = dot(v1, v1);
1010
+ float v2LengthSq = dot(v2, v2);
1011
+ float v3LengthSq = dot(v3, v3);
1012
+ float minLengthSq = min(
1013
+ min(v0LengthSq, v1LengthSq),
1014
+ min(v2LengthSq, v3LengthSq)
1015
+ );
1016
+ if(minLengthSq == v0LengthSq) {
1017
+ return v0;
1018
+ }
1019
+ else if(minLengthSq == v1LengthSq) {
1020
+ return v1;
1021
+ }
1022
+ else if(minLengthSq == v2LengthSq) {
1023
+ return v2;
1024
+ }
1025
+ else {
1026
+ return v3;
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * Helper to get the nearest offset vector from a
1032
+ * point to a subpixel.
1033
+ * GBC subpixels are roughly rectangular in shape,
1034
+ * but have a rectangular gap in their bottom-left
1035
+ * corner.
1036
+ * Returns (0, 0) for points within the subpixel.
1037
+ */
1038
+ vec2 getPointSubpixelDistance(
1039
+ vec2 point,
1040
+ vec2 subpixelCenter,
1041
+ vec2 subpixelSizeHalf
1042
+ ) {
1043
+ float rectLeft = subpixelCenter.x - subpixelSizeHalf.x;
1044
+ float rectRight = subpixelCenter.x + subpixelSizeHalf.x;
1045
+ float rectTop = subpixelCenter.y - subpixelSizeHalf.y;
1046
+ float rectBottom = subpixelCenter.y + subpixelSizeHalf.y;
1047
+ vec2 offsetLeft = getPointRectDistance(
1048
+ point,
1049
+ vec2(rectLeft, rectTop + SubpixelTabHeight),
1050
+ vec2(subpixelCenter.x, rectBottom)
1051
+ );
1052
+ vec2 offsetRight = getPointRectDistance(
1053
+ point,
1054
+ vec2(subpixelCenter.x, rectTop),
1055
+ vec2(rectRight, rectBottom)
1056
+ );
1057
+ float offsetLeftLengthSq = dot(offsetLeft, offsetLeft);
1058
+ float offsetRightLengthSq = dot(offsetRight, offsetRight);
1059
+ if(offsetLeftLengthSq <= offsetRightLengthSq) {
1060
+ return offsetLeft;
1061
+ }
1062
+ else {
1063
+ return offsetRight;
1064
+ }
1065
+ }
1066
+
1067
+ /**
1068
+ * Helper to get the intensity of light from a
1069
+ * subpixel.
1070
+ * The pixelPosition argument represents a
1071
+ * fragment's position within a pixel.
1072
+ * Spread represents the subpixel's horizontal
1073
+ * position within the pixel.
1074
+ */
1075
+ float getSubpixelIntensity(
1076
+ vec2 pixelPosition,
1077
+ float spread
1078
+ ) {
1079
+ vec2 subpixelCenter = vec2(
1080
+ 0.5 + (spread * SubpixelSpread),
1081
+ 1.0 - SubpixelVerticalOffset
1082
+ );
1083
+ vec2 subpixelSizeHalf = 0.5 * vec2(
1084
+ SubpixelLightWidth,
1085
+ SubpixelLightHeight
1086
+ );
1087
+ vec2 offset = getPointSubpixelDistance(
1088
+ pixelPosition,
1089
+ subpixelCenter,
1090
+ subpixelSizeHalf
1091
+ );
1092
+ if(SubpixelLightGlow <= 0) {
1093
+ return dot(offset, offset) <= 0.0 ? 1.0 : 0.0;
1094
+ }
1095
+ else {
1096
+ float dist = length(offset);
1097
+ float glow = max(0.0,
1098
+ 1.0 - (dist / SubpixelLightGlow)
1099
+ );
1100
+ return glow;
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * Helper to apply SubpixelColorBleed to the intensity
1106
+ * value computed for a fragment and subpixel.
1107
+ * Subpixel color bleed allows subpixel colors to be
1108
+ * more strongly coerced to more accurately represent
1109
+ * the underlying pixel color.
1110
+ */
1111
+ float applySubpixelBleed(
1112
+ float subpixelIntensity,
1113
+ float colorSourceChannel
1114
+ ) {
1115
+ return subpixelIntensity * (
1116
+ SubpixelColorBleed +
1117
+ ((1.0 - SubpixelColorBleed) * colorSourceChannel)
1118
+ );
1119
+ }
1120
+
1121
+ void main() {
1122
+ // Get base color of the pixel, adjust based on
1123
+ // contrast and luminosity settings.
1124
+ vec3 colorSource = texture2D(tex, texCoord).rgb;
1125
+ vec3 colorSourceHcl = convertRgbToHcl(colorSource);
1126
+ vec3 colorSourceAdjusted = convertHclToRgb(vec3(
1127
+ colorSourceHcl.x,
1128
+ colorSourceHcl.y * SourceContrast,
1129
+ colorSourceHcl.z * SourceLuminosity
1130
+ ));
1131
+ // Determine how much each subpixel's light should
1132
+ // affect this fragment.
1133
+ vec2 pixelPosition = (
1134
+ mod(texCoord * texSize * SubpixelScale, 1.0)
1135
+ );
1136
+ float subpixelIntensityRed = applySubpixelBleed(
1137
+ getSubpixelIntensity(pixelPosition, -1.0),
1138
+ colorSourceAdjusted.r
1139
+ );
1140
+ float subpixelIntensityGreen = applySubpixelBleed(
1141
+ getSubpixelIntensity(pixelPosition, +0.0),
1142
+ colorSourceAdjusted.g
1143
+ );
1144
+ float subpixelIntensityBlue = applySubpixelBleed(
1145
+ getSubpixelIntensity(pixelPosition, +1.0),
1146
+ colorSourceAdjusted.b
1147
+ );
1148
+ vec3 subpixelLightColor = SubpixelGamma * (
1149
+ (subpixelIntensityRed * SubpixelColorRed) +
1150
+ (subpixelIntensityGreen * SubpixelColorGreen) +
1151
+ (subpixelIntensityBlue * SubpixelColorBlue)
1152
+ );
1153
+ // Compute final color
1154
+ vec3 colorResult = clamp(
1155
+ subpixelLightColor * colorSourceAdjusted, 0.0, 1.0
1156
+ );
1157
+ vec3 colorResultBlended = (
1158
+ ((1.0 - SubpixelBlendAmount) * colorSourceAdjusted) +
1159
+ (SubpixelBlendAmount * colorResult)
1160
+ );
1161
+ colorResultBlended = BaseColor + (
1162
+ (colorResultBlended * (vec3(1.0, 1.0, 1.0) - BaseColor))
1163
+ );
1164
+ gl_FragColor = vec4(
1165
+ colorResultBlended,
1166
+ 1.0
1167
+ );
1168
+ }
1169
+ This is free and unencumbered software released into the public domain.
1170
+
1171
+ Anyone is free to copy, modify, publish, use, compile, sell, or
1172
+ distribute this software, either in source code form or as a compiled
1173
+ binary, for any purpose, commercial or non-commercial, and by any
1174
+ means.
1175
+
1176
+ In jurisdictions that recognize copyright laws, the author or authors
1177
+ of this software dedicate any and all copyright interest in the
1178
+ software to the public domain. We make this dedication for the benefit
1179
+ of the public at large and to the detriment of our heirs and
1180
+ successors. We intend this dedication to be an overt act of
1181
+ relinquishment in perpetuity of all present and future rights to this
1182
+ software under copyright law.
1183
+
1184
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1185
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1186
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1187
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1188
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1189
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1190
+ OTHER DEALINGS IN THE SOFTWARE.
1191
+
1192
+ For more information, please refer to <http://unlicense.org/>
1193
+ [shader]
1194
+ name=gbc-lcd
1195
+ author=Sophie Kirschner
1196
+ description=Imitates the GameBoy Color LCD screen subpixel arrangement, with an optional backlight effect.
1197
+ passes=2
1198
+
1199
+ [pass.0]
1200
+ integerScaling=1
1201
+ fragmentShader=gbc-lcd.fs
1202
+ blend=1
1203
+
1204
+ [pass.1]
1205
+ fragmentShader=gbc-lcd-light.fs
1206
+
1207
+ [pass.0.uniform.BaseColor]
1208
+ type=float3
1209
+ default[0]=0.130
1210
+ default[1]=0.128
1211
+ default[2]=0.101
1212
+ readableName=Screen base color
1213
+
1214
+ [pass.0.uniform.SubpixelColorRed]
1215
+ type=float3
1216
+ default[0]=1.00
1217
+ default[1]=0.38
1218
+ default[2]=0.22
1219
+ readableName=Red subpixel color
1220
+
1221
+ [pass.0.uniform.SubpixelColorGreen]
1222
+ type=float3
1223
+ default[0]=0.60
1224
+ default[1]=0.88
1225
+ default[2]=0.30
1226
+ readableName=Green subpixel color
1227
+
1228
+ [pass.0.uniform.SubpixelColorBlue]
1229
+ type=float3
1230
+ default[0]=0.23
1231
+ default[1]=0.65
1232
+ default[2]=1.00
1233
+ readableName=Blue subpixel color
1234
+
1235
+ [pass.0.uniform.SourceContrast]
1236
+ type=float
1237
+ default=0.85
1238
+ readableName=Screen contrast
1239
+
1240
+ [pass.0.uniform.SourceLuminosity]
1241
+ type=float
1242
+ default=0.88
1243
+ readableName=Screen luminosity
1244
+
1245
+ [pass.0.uniform.SubpixelBlendAmount]
1246
+ type=float
1247
+ default=1.0
1248
+ readableName=Subpixel effect amount
1249
+
1250
+ [pass.0.uniform.SubpixelColorBleed]
1251
+ type=float
1252
+ default=0.31
1253
+ readableName=Subpixel color bleeding
1254
+
1255
+ [pass.0.uniform.SubpixelSpread]
1256
+ type=float
1257
+ default=0.333
1258
+ readableName=Subpixel spread X
1259
+
1260
+ [pass.0.uniform.SubpixelVerticalOffset]
1261
+ type=float
1262
+ default=0.48
1263
+ readableName=Subpixel offset Y
1264
+
1265
+ [pass.0.uniform.SubpixelGamma]
1266
+ type=float
1267
+ default=1.040
1268
+ readableName=Subpixel brightness
1269
+
1270
+ [pass.0.uniform.SubpixelLightWidth]
1271
+ type=float
1272
+ default=0.220
1273
+ readableName=Subpixel width
1274
+
1275
+ [pass.0.uniform.SubpixelLightHeight]
1276
+ type=float
1277
+ default=0.850
1278
+ readableName=Subpixel height
1279
+
1280
+ [pass.0.uniform.SubpixelLightGlow]
1281
+ type=float
1282
+ default=0.195
1283
+ readableName=Subpixel glow size
1284
+
1285
+ [pass.0.uniform.SubpixelTabHeight]
1286
+ type=float
1287
+ default=0.175
1288
+ readableName=Subpixel tab shaping
1289
+
1290
+ [pass.0.uniform.SubpixelScale]
1291
+ type=float
1292
+ default=1.0
1293
+ readableName=Subpixel scale
1294
+
1295
+ [pass.1.uniform.LightColor]
1296
+ type=float3
1297
+ default[0]=1.000
1298
+ default[1]=0.968
1299
+ default[2]=0.882
1300
+ readableName=Backlight color
1301
+
1302
+ [pass.1.uniform.LightIntensity]
1303
+ type=float
1304
+ default=0.06
1305
+ readableName=Backlight intensity
1306
+
1307
+ [pass.1.uniform.LightSoftness]
1308
+ type=float
1309
+ default=0.67
1310
+ readableName=Backlight softness
1311
+
1312
+ [pass.1.uniform.ReflectionDistance]
1313
+ type=float2
1314
+ default[0]=0
1315
+ default[1]=0.025
1316
+ readableName=Internal reflection distance
1317
+
1318
+ [pass.1.uniform.ReflectionBrightness]
1319
+ type=float
1320
+ default=0.032
1321
+ readableName=Internal reflection brightness
1322
+ /* MIT License
1323
+ *
1324
+ * Copyright (c) 2015-2023 Lior Halphon
1325
+ *
1326
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
1327
+ * of this software and associated documentation files (the "Software"), to deal
1328
+ * in the Software without restriction, including without limitation the rights
1329
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1330
+ * copies of the Software, and to permit persons to whom the Software is
1331
+ * furnished to do so, subject to the following conditions:
1332
+ *
1333
+ * The above copyright notice and this permission notice shall be included in all
1334
+ * copies or substantial portions of the Software.
1335
+ *
1336
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1337
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1338
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1339
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1340
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1341
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1342
+ * SOFTWARE.
1343
+ */
1344
+ /* Based on this (really good) article: http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html */
1345
+
1346
+ /* The colorspace used by the HQnx filters is not really YUV, despite the algorithm description claims it is. It is
1347
+ also not normalized. Therefore, we shall call the colorspace used by HQnx "HQ Colorspace" to avoid confusion. */
1348
+
1349
+ varying vec2 texCoord;
1350
+ uniform sampler2D tex;
1351
+ uniform vec2 texSize;
1352
+
1353
+ float _bit(float v, int i)
1354
+ {
1355
+ // v is an integer value stored as float
1356
+ float p = exp2(float(i)); // 2^i, GLSL ES 1.00-friendly
1357
+ return floor(mod(floor(v / p), 2.0));
1358
+ }
1359
+
1360
+ bool _matchMask(float pattern, int mask, int ref)
1361
+ {
1362
+ // (pattern & mask) == ref, implemented without bitwise ops.
1363
+ float m = float(mask);
1364
+ float r = float(ref);
1365
+ float mism = 0.0;
1366
+ for (int i = 0; i < 8; ++i)
1367
+ {
1368
+ float mb = _bit(m, i);
1369
+ if (mb > 0.5)
1370
+ {
1371
+ float pb = _bit(pattern, i);
1372
+ float rb = _bit(r, i);
1373
+ mism += abs(pb - rb);
1374
+ }
1375
+ }
1376
+ return mism < 0.5;
1377
+ }
1378
+
1379
+ vec3 rgb_to_hq_colospace(vec4 rgb)
1380
+ {
1381
+ return vec3(
1382
+ 0.250 * rgb.r + 0.250 * rgb.g + 0.250 * rgb.b,
1383
+ 0.250 * rgb.r - 0.000 * rgb.g - 0.250 * rgb.b,
1384
+ -0.125 * rgb.r + 0.250 * rgb.g - 0.125 * rgb.b
1385
+ );
1386
+ }
1387
+
1388
+ bool is_different(vec4 a, vec4 b)
1389
+ {
1390
+ vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
1391
+ return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
1392
+ }
1393
+
1394
+ // WebGL1: no bitwise ops in GLSL ES 1.00
1395
+
1396
+ vec4 interp_2px(vec4 c1, float w1, vec4 c2, float w2)
1397
+ {
1398
+ return (c1 * w1 + c2 * w2) / (w1 + w2);
1399
+ }
1400
+
1401
+ vec4 interp_3px(vec4 c1, float w1, vec4 c2, float w2, vec4 c3, float w3)
1402
+ {
1403
+ return (c1 * w1 + c2 * w2 + c3 * w3) / (w1 + w2 + w3);
1404
+ }
1405
+
1406
+ vec4 scale(sampler2D image, vec2 position, vec2 input_resolution)
1407
+ {
1408
+ // o = offset, the width of a pixel
1409
+ vec2 o = vec2(1.0, 1.0) / input_resolution;
1410
+
1411
+ /* We always calculate the top left pixel. If we need a different pixel, we flip the image */
1412
+
1413
+ // p = the position within a pixel [0...1]
1414
+ vec2 p = fract(position * input_resolution);
1415
+
1416
+ if (p.x > 0.5) o.x = -o.x;
1417
+ if (p.y > 0.5) o.y = -o.y;
1418
+
1419
+ vec4 w0 = texture2D(image, position + vec2( -o.x, -o.y));
1420
+ vec4 w1 = texture2D(image, position + vec2( 0.0, -o.y));
1421
+ vec4 w2 = texture2D(image, position + vec2( o.x, -o.y));
1422
+ vec4 w3 = texture2D(image, position + vec2( -o.x, 0.0));
1423
+ vec4 w4 = texture2D(image, position + vec2( 0.0, 0.0));
1424
+ vec4 w5 = texture2D(image, position + vec2( o.x, 0.0));
1425
+ vec4 w6 = texture2D(image, position + vec2( -o.x, o.y));
1426
+ vec4 w7 = texture2D(image, position + vec2( 0.0, o.y));
1427
+ vec4 w8 = texture2D(image, position + vec2( o.x, o.y));
1428
+
1429
+ float pattern = 0.0;
1430
+ if (is_different(w0, w4)) pattern += 1.0;
1431
+ if (is_different(w1, w4)) pattern += 2.0;
1432
+ if (is_different(w2, w4)) pattern += 4.0;
1433
+ if (is_different(w3, w4)) pattern += 8.0;
1434
+ if (is_different(w5, w4)) pattern += 16.0;
1435
+ if (is_different(w6, w4)) pattern += 32.0;
1436
+ if (is_different(w7, w4)) pattern += 64.0;
1437
+ if (is_different(w8, w4)) pattern += 128.0;
1438
+
1439
+ // NOTE: hex literals (0x..) are not GLSL ES 1.00. Converted to decimal.
1440
+
1441
+ if ((_matchMask(pattern, 191, 55) || _matchMask(pattern, 219, 19)) && is_different(w1, w5))
1442
+ {
1443
+ return interp_2px(w4, 3.0, w3, 1.0);
1444
+ }
1445
+ if ((_matchMask(pattern, 219, 73) || _matchMask(pattern, 239, 109)) && is_different(w7, w3))
1446
+ {
1447
+ return interp_2px(w4, 3.0, w1, 1.0);
1448
+ }
1449
+ if ((_matchMask(pattern, 11, 11) || _matchMask(pattern, 254, 74) || _matchMask(pattern, 254, 26)) && is_different(w3, w1))
1450
+ {
1451
+ return w4;
1452
+ }
1453
+ if ((_matchMask(pattern, 111, 42) || _matchMask(pattern, 91, 10) || _matchMask(pattern, 191, 58) || _matchMask(pattern, 223, 90) ||
1454
+ _matchMask(pattern, 159, 138) || _matchMask(pattern, 207, 138) || _matchMask(pattern, 239, 78) || _matchMask(pattern, 63, 14) ||
1455
+ _matchMask(pattern, 251, 90) || _matchMask(pattern, 187, 138) || _matchMask(pattern, 127, 90) || _matchMask(pattern, 175, 138) ||
1456
+ _matchMask(pattern, 235, 138)) && is_different(w3, w1))
1457
+ {
1458
+ return interp_2px(w4, 3.0, w0, 1.0);
1459
+ }
1460
+ if (_matchMask(pattern, 11, 8))
1461
+ {
1462
+ return interp_3px(w4, 2.0, w0, 1.0, w1, 1.0);
1463
+ }
1464
+ if (_matchMask(pattern, 11, 2))
1465
+ {
1466
+ return interp_3px(w4, 2.0, w0, 1.0, w3, 1.0);
1467
+ }
1468
+ if (_matchMask(pattern, 47, 47))
1469
+ {
1470
+ return interp_3px(w4, 4.0, w3, 1.0, w1, 1.0);
1471
+ }
1472
+ if (_matchMask(pattern, 191, 55) || _matchMask(pattern, 219, 19))
1473
+ {
1474
+ return interp_3px(w4, 5.0, w1, 2.0, w3, 1.0);
1475
+ }
1476
+ if (_matchMask(pattern, 219, 73) || _matchMask(pattern, 239, 109))
1477
+ {
1478
+ return interp_3px(w4, 5.0, w3, 2.0, w1, 1.0);
1479
+ }
1480
+ if (_matchMask(pattern, 27, 3) || _matchMask(pattern, 79, 67) || _matchMask(pattern, 139, 131) || _matchMask(pattern, 107, 67))
1481
+ {
1482
+ return interp_2px(w4, 3.0, w3, 1.0);
1483
+ }
1484
+ if (_matchMask(pattern, 75, 9) || _matchMask(pattern, 139, 137) || _matchMask(pattern, 31, 25) || _matchMask(pattern, 59, 25))
1485
+ {
1486
+ return interp_2px(w4, 3.0, w1, 1.0);
1487
+ }
1488
+ if (_matchMask(pattern, 126, 42) || _matchMask(pattern, 239, 171) || _matchMask(pattern, 191, 143) || _matchMask(pattern, 126, 14))
1489
+ {
1490
+ return interp_3px(w4, 2.0, w3, 3.0, w1, 3.0);
1491
+ }
1492
+ if (_matchMask(pattern, 251, 106) || _matchMask(pattern, 111, 110) || _matchMask(pattern, 63, 62) || _matchMask(pattern, 251, 250) ||
1493
+ _matchMask(pattern, 223, 222) || _matchMask(pattern, 223, 30))
1494
+ {
1495
+ return interp_2px(w4, 3.0, w0, 1.0);
1496
+ }
1497
+ if (_matchMask(pattern, 10, 0) || _matchMask(pattern, 79, 75) || _matchMask(pattern, 159, 27) || _matchMask(pattern, 47, 11) ||
1498
+ _matchMask(pattern, 190, 10) || _matchMask(pattern, 238, 10) || _matchMask(pattern, 126, 10) || _matchMask(pattern, 235, 75) ||
1499
+ _matchMask(pattern, 59, 27))
1500
+ {
1501
+ return interp_3px(w4, 2.0, w3, 1.0, w1, 1.0);
1502
+ }
1503
+
1504
+ return interp_3px(w4, 6.0, w3, 1.0, w1, 1.0);
1505
+ }
1506
+
1507
+ void main()
1508
+ {
1509
+ gl_FragColor = scale(tex, texCoord, texSize);
1510
+ }
1511
+ [shader]
1512
+ name=hq2x
1513
+ author=Lior Halphon
1514
+ description="High Quality" 2x scaling
1515
+ passes=1
1516
+
1517
+ [pass.0]
1518
+ fragmentShader=hq2x.fs
1519
+ blend=0
1520
+ width=-2
1521
+ height=-2
1522
+ /*
1523
+ LCD Shader
1524
+
1525
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
1526
+
1527
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1528
+ of this software and associated documentation files (the "Software"), to deal
1529
+ in the Software without restriction, including without limitation the rights
1530
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1531
+ copies of the Software, and to permit persons to whom the Software is
1532
+ furnished to do so, subject to the following conditions:
1533
+ The above copyright notice and this permission notice shall be included in
1534
+ all copies or substantial portions of the Software.
1535
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1536
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1537
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1538
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1539
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1540
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1541
+ THE SOFTWARE.
1542
+ */
1543
+
1544
+ uniform sampler2D tex;
1545
+ uniform vec2 texSize;
1546
+ varying vec2 texCoord;
1547
+
1548
+ uniform float boundBrightness;
1549
+
1550
+ void main()
1551
+ {
1552
+ vec4 color = texture2D(tex, texCoord);
1553
+
1554
+ if (int(mod(texCoord.s * texSize.x * 3.0, 3.0)) == 0 ||
1555
+ int(mod(texCoord.t * texSize.y * 3.0, 3.0)) == 0)
1556
+ {
1557
+ color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness;
1558
+ }
1559
+
1560
+ gl_FragColor = color;
1561
+ }
1562
+ [shader]
1563
+ name=LCD
1564
+ author=Dominus Iniquitatis
1565
+ description=Simple LCD emulation.
1566
+ passes=1
1567
+
1568
+ [pass.0]
1569
+ fragmentShader=lcd.fs
1570
+ blend=1
1571
+ width=-3
1572
+ height=-3
1573
+
1574
+ [pass.0.uniform.boundBrightness]
1575
+ type=float
1576
+ readableName=Bound brightness
1577
+ default=0.9
1578
+ min=0.0
1579
+ max=1.0
1580
+ [shader]
1581
+ name=Motion Blur
1582
+ author=Dominus Iniquitatis
1583
+ description=Simple motion blur.
1584
+ passes=1
1585
+
1586
+ [pass.0]
1587
+ fragmentShader=motion_blur.fs
1588
+ blend=1
1589
+ width=-1
1590
+ height=-1
1591
+
1592
+ [pass.0.uniform.amount]
1593
+ type=float
1594
+ readableName=Amount
1595
+ default=0.3
1596
+ min=0.0
1597
+ max=1.0
1598
+ /*
1599
+ Motion Blur Shader
1600
+
1601
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
1602
+
1603
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1604
+ of this software and associated documentation files (the "Software"), to deal
1605
+ in the Software without restriction, including without limitation the rights
1606
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1607
+ copies of the Software, and to permit persons to whom the Software is
1608
+ furnished to do so, subject to the following conditions:
1609
+ The above copyright notice and this permission notice shall be included in
1610
+ all copies or substantial portions of the Software.
1611
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1612
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1613
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1614
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1615
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1616
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1617
+ THE SOFTWARE.
1618
+ */
1619
+
1620
+ uniform sampler2D tex;
1621
+ uniform vec2 texSize;
1622
+ varying vec2 texCoord;
1623
+
1624
+ uniform float amount;
1625
+
1626
+ void main()
1627
+ {
1628
+ vec4 color = texture2D(tex, texCoord);
1629
+ color.a = 1.0 - amount;
1630
+
1631
+ gl_FragColor = color;
1632
+ }
1633
+ [shader]
1634
+ name=Nintendo DS Color
1635
+ author=Pokefan531 and hunterk
1636
+ description=Shader that replicates the LCD Colorspace from a Nintendo DS Phat.
1637
+ passes=1
1638
+
1639
+ [pass.0]
1640
+ fragmentShader=nds-color.fs
1641
+ vertexShader=nds-color.vs
1642
+ blend=1
1643
+ width=-1
1644
+ height=-1
1645
+
1646
+ [pass.0.uniform.color_mode]
1647
+ type=int
1648
+ default=1
1649
+ min=1
1650
+ max=3
1651
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
1652
+ // Shader that replicates the LCD Colorspace from a Nintendo DS Phat --
1653
+ varying vec2 texCoord;
1654
+ varying mat4 profile;
1655
+ uniform sampler2D tex;
1656
+ uniform vec2 texSize;
1657
+
1658
+ const float target_gamma = 2.2;
1659
+ const float display_gamma = 2.2;
1660
+
1661
+ void main() {
1662
+ // bring out our stored luminance value
1663
+ float lum = profile[3].w;
1664
+
1665
+ // our adjustments need to happen in linear gamma
1666
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma)).rgba;
1667
+
1668
+ screen = clamp(screen * lum, 0.0, 1.0);
1669
+ screen = profile * screen;
1670
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
1671
+ }
1672
+ uniform int color_mode;
1673
+ attribute vec4 position;
1674
+ varying vec2 texCoord;
1675
+ varying mat4 profile;
1676
+
1677
+ const mat4 NDS_sRGB = mat4(
1678
+ 0.835, 0.10, 0.105, 0.0, //red channel
1679
+ 0.27, 0.6375, 0.175, 0.0, //green channel
1680
+ -0.105, 0.2625, 0.72, 0.0, //blue channel
1681
+ 0.0, 0.0, 0.0, 0.905 //alpha channel
1682
+ );
1683
+
1684
+ const mat4 NDS_DCI = mat4(
1685
+ 0.70, 0.125, 0.12, 0.0, //red channel
1686
+ 0.34, 0.625, 0.20, 0.0, //green channel
1687
+ -0.04, 0.25, 0.68, 0.0, //blue channel
1688
+ 0.0, 0.0, 0.0, 0.96 //alpha channel
1689
+ );
1690
+
1691
+ const mat4 NDS_Rec2020 = mat4(
1692
+ 0.555, 0.1475, 0.1175, 0.0, //red channel
1693
+ 0.39, 0.6075, 0.2075, 0.0, //green channel
1694
+ 0.055, 0.245, 0.675, 0.0, //blue channel
1695
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
1696
+ );
1697
+
1698
+ void main() {
1699
+ if (color_mode == 1) profile = NDS_sRGB;
1700
+ else if (color_mode == 2) profile = NDS_DCI;
1701
+ else if (color_mode == 3) profile = NDS_Rec2020;
1702
+
1703
+ gl_Position = position;
1704
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
1705
+ }
1706
+ [shader]
1707
+ name=NSO GBA Color
1708
+ author=Pokefan531 and hunterk
1709
+ description=Shader that replicates the Nintendo Switch Online's GBA color filter.
1710
+ passes=1
1711
+
1712
+ [pass.0]
1713
+ fragmentShader=nso-gba-color.fs
1714
+ vertexShader=nso-gba-color.vs
1715
+ blend=1
1716
+ width=-1
1717
+ height=-1
1718
+
1719
+ [pass.0.uniform.darken_screen]
1720
+ type=float
1721
+ default=0.8
1722
+ min=0
1723
+ max=1
1724
+ readableName=Darken Screen
1725
+
1726
+ [pass.0.uniform.color_mode]
1727
+ type=int
1728
+ default=1
1729
+ min=1
1730
+ max=3
1731
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
1732
+ // Shader that replicates the LCD Colorspace from Gameboy Advance --
1733
+ varying vec2 texCoord;
1734
+ varying mat4 profile;
1735
+ uniform sampler2D tex;
1736
+ uniform vec2 texSize;
1737
+
1738
+ uniform float darken_screen;
1739
+ const float target_gamma = 2.2;
1740
+ const float display_gamma = 2.2;
1741
+
1742
+ void main() {
1743
+ // bring out our stored luminance value
1744
+ float lum = profile[3].w;
1745
+
1746
+ // our adjustments need to happen in linear gamma
1747
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba;
1748
+
1749
+ screen = clamp(screen * lum, 0.0, 1.0);
1750
+ screen = profile * screen;
1751
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
1752
+ }
1753
+ uniform int color_mode;
1754
+ attribute vec4 position;
1755
+ varying vec2 texCoord;
1756
+ varying mat4 profile;
1757
+
1758
+ const mat4 GBA_sRGB = mat4(
1759
+ 0.865, 0.0575, 0.0575, 0.0, //red channel
1760
+ 0.1225, 0.925, 0.1225, 0.0, //green channel
1761
+ 0.0125, 0.0125, 0.82, 0.0, //blue channel
1762
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
1763
+ );
1764
+
1765
+ const mat4 GBA_DCI = mat4(
1766
+ 0.72, 0.0875, 0.0725, 0.0, //red channel
1767
+ 0.2675, 0.9, 0.185, 0.0, //green channel
1768
+ 0.0125, 0.0125, 0.7425, 0.0, //blue channel
1769
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
1770
+ );
1771
+
1772
+ const mat4 GBA_Rec2020 = mat4(
1773
+ 0.57, 0.115, 0.0725, 0.0, //red channel
1774
+ 0.3825, 0.8625, 0.195, 0.0, //green channel
1775
+ 0.0475, 0.0225, 0.7325, 0.0, //blue channel
1776
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
1777
+ );
1778
+
1779
+ void main() {
1780
+ if (color_mode == 1) profile = GBA_sRGB;
1781
+ else if (color_mode == 2) profile = GBA_DCI;
1782
+ else if (color_mode == 3) profile = GBA_Rec2020;
1783
+
1784
+ gl_Position = position;
1785
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
1786
+ }
1787
+ [shader]
1788
+ name=NSO GBC Color
1789
+ author=Pokefan531 and hunterk
1790
+ description=Shader that replicates the Nintendo Switch Online's GBC color filter.
1791
+ passes=1
1792
+
1793
+ [pass.0]
1794
+ fragmentShader=nso-gbc-color.fs
1795
+ vertexShader=nso-gbc-color.vs
1796
+ blend=1
1797
+ width=-1
1798
+ height=-1
1799
+
1800
+ [pass.0.uniform.lighten_screen]
1801
+ type=float
1802
+ default=0.0
1803
+ min=0
1804
+ max=1
1805
+ readableName=Lighten Screen
1806
+
1807
+ [pass.0.uniform.color_mode]
1808
+ type=int
1809
+ default=1
1810
+ min=1
1811
+ max=3
1812
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
1813
+ // Shader that replicates the Nintendo Switch Online's GBC color filter --
1814
+ varying vec2 texCoord;
1815
+ varying mat4 profile;
1816
+ uniform sampler2D tex;
1817
+ uniform vec2 texSize;
1818
+
1819
+ uniform float lighten_screen;
1820
+
1821
+ void main() {
1822
+ // bring out our stored luminance value
1823
+ float lum = profile[3].w;
1824
+
1825
+ // our adjustments need to happen in linear gamma
1826
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(1.24, 0.8, 0.7, 1.0)).rgba;
1827
+
1828
+ screen = clamp(screen * lum, 0.0, 1.0);
1829
+ screen = profile * screen;
1830
+ gl_FragColor = pow(screen, vec4(1.0));
1831
+ }
1832
+ uniform int color_mode;
1833
+ attribute vec4 position;
1834
+ varying vec2 texCoord;
1835
+ varying mat4 profile;
1836
+
1837
+ const mat4 GBC_sRGB = mat4(
1838
+ 0.84, 0.105, 0.15, 0.0, //red channel
1839
+ 0.265, 0.67, 0.30, 0.0, //green channel
1840
+ 0.0, 0.24, 0.525, 0.0, //blue channel
1841
+ 0.175, 0.18, 0.18, 0.85 //alpha channel
1842
+ );
1843
+
1844
+ const mat4 GBC_DCI = mat4(
1845
+ 0.84, 0.105, 0.15, 0.0, //red channel
1846
+ 0.265, 0.67, 0.30, 0.0, //green channel
1847
+ 0.0, 0.24, 0.525, 0.0, //blue channel
1848
+ 0.175, 0.18, 0.18, 1.0 //alpha channel
1849
+ );
1850
+
1851
+ const mat4 GBC_Rec2020 = mat4(
1852
+ 0.84, 0.105, 0.15, 0.0, //red channel
1853
+ 0.265, 0.67, 0.30, 0.0, //green channel
1854
+ 0.0, 0.24, 0.525, 0.0, //blue channel
1855
+ 0.175, 0.18, 0.18, 1.0 //alpha channel
1856
+ );
1857
+
1858
+ void main() {
1859
+ if (color_mode == 1) profile = GBC_sRGB;
1860
+ else if (color_mode == 2) profile = GBC_DCI;
1861
+ else if (color_mode == 3) profile = GBC_Rec2020;
1862
+
1863
+ gl_Position = position;
1864
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
1865
+ }
1866
+ [shader]
1867
+ name=OmniScale
1868
+ author=Lior Halphon
1869
+ description=Resolution-indepedent scaler inspired by the hqx family scalers
1870
+ passes=1
1871
+
1872
+ [pass.0]
1873
+ fragmentShader=omniscale.fs
1874
+ blend=0
1875
+ /* MIT License
1876
+ *
1877
+ * Copyright (c) 2015-2023 Lior Halphon
1878
+ *
1879
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
1880
+ * of this software and associated documentation files (the "Software"), to deal
1881
+ * in the Software without restriction, including without limitation the rights
1882
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1883
+ * copies of the Software, and to permit persons to whom the Software is
1884
+ * furnished to do so, subject to the following conditions:
1885
+ *
1886
+ * The above copyright notice and this permission notice shall be included in all
1887
+ * copies or substantial portions of the Software.
1888
+ *
1889
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1890
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1891
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1892
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1893
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1894
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1895
+ * SOFTWARE.
1896
+ */
1897
+ /* OmniScale is derived from the pattern based design of HQnx, but with the following general differences:
1898
+ - The actual output calculating was completely redesigned as resolution independent graphic generator. This allows
1899
+ scaling to any factor.
1900
+ - HQnx approximations that were good enough for a 2x/3x/4x factor were refined, creating smoother gradients.
1901
+ - "Quarters" can be interpolated in more ways than in the HQnx filters
1902
+ - If a pattern does not provide enough information to determine the suitable scaling interpolation, up to 16 pixels
1903
+ per quarter are sampled (in contrast to the usual 9) in order to determine the best interpolation.
1904
+ */
1905
+
1906
+ varying vec2 texCoord;
1907
+ uniform sampler2D tex;
1908
+ uniform vec2 texSize;
1909
+ uniform vec2 outputSize;
1910
+
1911
+ float _bit(float v, int i)
1912
+ {
1913
+ // v is an integer value stored as float
1914
+ float p = exp2(float(i)); // 2^i, GLSL ES 1.00-friendly
1915
+ return floor(mod(floor(v / p), 2.0));
1916
+ }
1917
+
1918
+ bool _matchMask(float pattern, int mask, int ref)
1919
+ {
1920
+ // (pattern & mask) == ref, implemented without bitwise ops.
1921
+ float m = float(mask);
1922
+ float r = float(ref);
1923
+ float mism = 0.0;
1924
+ for (int i = 0; i < 15; ++i)
1925
+ {
1926
+ float mb = _bit(m, i);
1927
+ if (mb > 0.5)
1928
+ {
1929
+ float pb = _bit(pattern, i);
1930
+ float rb = _bit(r, i);
1931
+ mism += abs(pb - rb);
1932
+ }
1933
+ }
1934
+ return mism < 0.5;
1935
+ }
1936
+
1937
+ /* We use the same colorspace as the HQ algorithms. */
1938
+ vec3 rgb_to_hq_colospace(vec4 rgb)
1939
+ {
1940
+ return vec3(
1941
+ 0.250 * rgb.r + 0.250 * rgb.g + 0.250 * rgb.b,
1942
+ 0.250 * rgb.r - 0.000 * rgb.g - 0.250 * rgb.b,
1943
+ -0.125 * rgb.r + 0.250 * rgb.g - 0.125 * rgb.b
1944
+ );
1945
+ }
1946
+
1947
+ bool is_different(vec4 a, vec4 b)
1948
+ {
1949
+ vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
1950
+ return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
1951
+ }
1952
+
1953
+ // WebGL1: no bitwise ops in GLSL ES 1.00
1954
+
1955
+ vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 output_resolution)
1956
+ {
1957
+ // o = offset, the width of a pixel
1958
+ vec2 o = vec2(1.0, 1.0) / input_resolution;
1959
+
1960
+ /* We always calculate the top left quarter. If we need a different quarter, we flip our co-ordinates */
1961
+
1962
+ // p = the position within a pixel [0...1]
1963
+ vec2 p = fract(position * input_resolution);
1964
+
1965
+ if (p.x > 0.5)
1966
+ {
1967
+ o.x = -o.x;
1968
+ p.x = 1.0 - p.x;
1969
+ }
1970
+ if (p.y > 0.5)
1971
+ {
1972
+ o.y = -o.y;
1973
+ p.y = 1.0 - p.y;
1974
+ }
1975
+
1976
+ vec4 w0 = texture2D(image, position + vec2(-o.x, -o.y));
1977
+ vec4 w1 = texture2D(image, position + vec2( 0.0, -o.y));
1978
+ vec4 w2 = texture2D(image, position + vec2( o.x, -o.y));
1979
+ vec4 w3 = texture2D(image, position + vec2(-o.x, 0.0));
1980
+ vec4 w4 = texture2D(image, position + vec2( 0.0, 0.0));
1981
+ vec4 w5 = texture2D(image, position + vec2( o.x, 0.0));
1982
+ vec4 w6 = texture2D(image, position + vec2(-o.x, o.y));
1983
+ vec4 w7 = texture2D(image, position + vec2( 0.0, o.y));
1984
+ vec4 w8 = texture2D(image, position + vec2( o.x, o.y));
1985
+
1986
+ float pattern = 0.0;
1987
+ if (is_different(w0, w4)) pattern += 1.0;
1988
+ if (is_different(w1, w4)) pattern += 2.0;
1989
+ if (is_different(w2, w4)) pattern += 4.0;
1990
+ if (is_different(w3, w4)) pattern += 8.0;
1991
+ if (is_different(w5, w4)) pattern += 16.0;
1992
+ if (is_different(w6, w4)) pattern += 32.0;
1993
+ if (is_different(w7, w4)) pattern += 64.0;
1994
+ if (is_different(w8, w4)) pattern += 128.0;
1995
+
1996
+ // NOTE: hex literals (0x..) are not GLSL ES 1.00. Converted to decimal.
1997
+
1998
+ if ((_matchMask(pattern, 191, 55) || _matchMask(pattern, 219, 19)) && is_different(w1, w5))
1999
+ {
2000
+ return mix(w4, w3, 0.5 - p.x);
2001
+ }
2002
+ if ((_matchMask(pattern, 219, 73) || _matchMask(pattern, 239, 109)) && is_different(w7, w3))
2003
+ {
2004
+ return mix(w4, w1, 0.5 - p.y);
2005
+ }
2006
+ if ((_matchMask(pattern, 11, 11) || _matchMask(pattern, 254, 74) || _matchMask(pattern, 254, 26)) && is_different(w3, w1))
2007
+ {
2008
+ return w4;
2009
+ }
2010
+ if ((_matchMask(pattern, 111, 42) || _matchMask(pattern, 91, 10) || _matchMask(pattern, 191, 58) || _matchMask(pattern, 223, 90) ||
2011
+ _matchMask(pattern, 159, 138) || _matchMask(pattern, 207, 138) || _matchMask(pattern, 239, 78) || _matchMask(pattern, 63, 14) ||
2012
+ _matchMask(pattern, 251, 90) || _matchMask(pattern, 187, 138) || _matchMask(pattern, 127, 90) || _matchMask(pattern, 175, 138) ||
2013
+ _matchMask(pattern, 235, 138)) && is_different(w3, w1))
2014
+ {
2015
+ return mix(w4, mix(w4, w0, 0.5 - p.x), 0.5 - p.y);
2016
+ }
2017
+ if (_matchMask(pattern, 11, 8))
2018
+ {
2019
+ return mix(mix(w0 * 0.375 + w1 * 0.25 + w4 * 0.375, w4 * 0.5 + w1 * 0.5, p.x * 2.0), w4, p.y * 2.0);
2020
+ }
2021
+ if (_matchMask(pattern, 11, 2))
2022
+ {
2023
+ return mix(mix(w0 * 0.375 + w3 * 0.25 + w4 * 0.375, w4 * 0.5 + w3 * 0.5, p.y * 2.0), w4, p.x * 2.0);
2024
+ }
2025
+ if (_matchMask(pattern, 47, 47))
2026
+ {
2027
+ float dist = length(p - vec2(0.5));
2028
+ float pixel_size = length(1.0 / (output_resolution / input_resolution));
2029
+ if (dist < 0.5 - pixel_size / 2.0)
2030
+ {
2031
+ return w4;
2032
+ }
2033
+ vec4 r;
2034
+ if (is_different(w0, w1) || is_different(w0, w3))
2035
+ {
2036
+ r = mix(w1, w3, p.y - p.x + 0.5);
2037
+ }
2038
+ else
2039
+ {
2040
+ r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
2041
+ }
2042
+
2043
+ if (dist > 0.5 + pixel_size / 2.0)
2044
+ {
2045
+ return r;
2046
+ }
2047
+ return mix(w4, r, (dist - 0.5 + pixel_size / 2.0) / pixel_size);
2048
+ }
2049
+ if (_matchMask(pattern, 191, 55) || _matchMask(pattern, 219, 19))
2050
+ {
2051
+ float dist = p.x - 2.0 * p.y;
2052
+ float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
2053
+ if (dist > pixel_size / 2.0)
2054
+ {
2055
+ return w1;
2056
+ }
2057
+ vec4 r = mix(w3, w4, p.x + 0.5);
2058
+ if (dist < -pixel_size / 2.0)
2059
+ {
2060
+ return r;
2061
+ }
2062
+ return mix(r, w1, (dist + pixel_size / 2.0) / pixel_size);
2063
+ }
2064
+ if (_matchMask(pattern, 219, 73) || _matchMask(pattern, 239, 109))
2065
+ {
2066
+ float dist = p.y - 2.0 * p.x;
2067
+ float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
2068
+ if (dist > pixel_size / 2.0)
2069
+ {
2070
+ return w3;
2071
+ }
2072
+ vec4 r = mix(w1, w4, p.x + 0.5);
2073
+ if (dist < -pixel_size / 2.0)
2074
+ {
2075
+ return r;
2076
+ }
2077
+ return mix(r, w3, (dist + pixel_size / 2.0) / pixel_size);
2078
+ }
2079
+ if (_matchMask(pattern, 191, 143) || _matchMask(pattern, 126, 14))
2080
+ {
2081
+ float dist = p.x + 2.0 * p.y;
2082
+ float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
2083
+
2084
+ if (dist > 1.0 + pixel_size / 2.0)
2085
+ {
2086
+ return w4;
2087
+ }
2088
+
2089
+ vec4 r;
2090
+ if (is_different(w0, w1) || is_different(w0, w3))
2091
+ {
2092
+ r = mix(w1, w3, p.y - p.x + 0.5);
2093
+ }
2094
+ else
2095
+ {
2096
+ r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
2097
+ }
2098
+
2099
+ if (dist < 1.0 - pixel_size / 2.0)
2100
+ {
2101
+ return r;
2102
+ }
2103
+
2104
+ return mix(r, w4, (dist + pixel_size / 2.0 - 1.0) / pixel_size);
2105
+ }
2106
+
2107
+ if (_matchMask(pattern, 126, 42) || _matchMask(pattern, 239, 171))
2108
+ {
2109
+ float dist = p.y + 2.0 * p.x;
2110
+ float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
2111
+
2112
+ if (dist > 1.0 + pixel_size / 2.0)
2113
+ {
2114
+ return w4;
2115
+ }
2116
+
2117
+ vec4 r;
2118
+
2119
+ if (is_different(w0, w1) || is_different(w0, w3))
2120
+ {
2121
+ r = mix(w1, w3, p.y - p.x + 0.5);
2122
+ }
2123
+ else
2124
+ {
2125
+ r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
2126
+ }
2127
+
2128
+ if (dist < 1.0 - pixel_size / 2.0)
2129
+ {
2130
+ return r;
2131
+ }
2132
+
2133
+ return mix(r, w4, (dist + pixel_size / 2.0 - 1.0) / pixel_size);
2134
+ }
2135
+
2136
+ if (_matchMask(pattern, 27, 3) || _matchMask(pattern, 79, 67) || _matchMask(pattern, 139, 131) || _matchMask(pattern, 107, 67))
2137
+ {
2138
+ return mix(w4, w3, 0.5 - p.x);
2139
+ }
2140
+
2141
+ if (_matchMask(pattern, 75, 9) || _matchMask(pattern, 139, 137) || _matchMask(pattern, 31, 25) || _matchMask(pattern, 59, 25))
2142
+ {
2143
+ return mix(w4, w1, 0.5 - p.y);
2144
+ }
2145
+
2146
+ if (_matchMask(pattern, 251, 106) || _matchMask(pattern, 111, 110) || _matchMask(pattern, 63, 62) || _matchMask(pattern, 251, 250) ||
2147
+ _matchMask(pattern, 223, 222) || _matchMask(pattern, 223, 30))
2148
+ {
2149
+ return mix(w4, w0, (1.0 - p.x - p.y) / 2.0);
2150
+ }
2151
+
2152
+ if (_matchMask(pattern, 79, 75) || _matchMask(pattern, 159, 27) || _matchMask(pattern, 47, 11) ||
2153
+ _matchMask(pattern, 190, 10) || _matchMask(pattern, 238, 10) || _matchMask(pattern, 126, 10) || _matchMask(pattern, 235, 75) ||
2154
+ _matchMask(pattern, 59, 27))
2155
+ {
2156
+ float dist = p.x + p.y;
2157
+ float pixel_size = length(1.0 / (output_resolution / input_resolution));
2158
+
2159
+ if (dist > 0.5 + pixel_size / 2.0)
2160
+ {
2161
+ return w4;
2162
+ }
2163
+
2164
+ vec4 r;
2165
+ if (is_different(w0, w1) || is_different(w0, w3))
2166
+ {
2167
+ r = mix(w1, w3, p.y - p.x + 0.5);
2168
+ }
2169
+ else
2170
+ {
2171
+ r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
2172
+ }
2173
+
2174
+ if (dist < 0.5 - pixel_size / 2.0)
2175
+ {
2176
+ return r;
2177
+ }
2178
+
2179
+ return mix(r, w4, (dist + pixel_size / 2.0 - 0.5) / pixel_size);
2180
+ }
2181
+
2182
+ if (_matchMask(pattern, 11, 1))
2183
+ {
2184
+ return mix(mix(w4, w3, 0.5 - p.x), mix(w1, (w1 + w3) / 2.0, 0.5 - p.x), 0.5 - p.y);
2185
+ }
2186
+
2187
+ if (_matchMask(pattern, 11, 0))
2188
+ {
2189
+ return mix(mix(w4, w3, 0.5 - p.x), mix(w1, w0, 0.5 - p.x), 0.5 - p.y);
2190
+ }
2191
+
2192
+ float dist = p.x + p.y;
2193
+ float pixel_size = length(1.0 / (output_resolution / input_resolution));
2194
+
2195
+ if (dist > 0.5 + pixel_size / 2.0)
2196
+ {
2197
+ return w4;
2198
+ }
2199
+
2200
+ /* We need more samples to "solve" this diagonal */
2201
+ vec4 x0 = texture2D(image, position + vec2(-o.x * 2.0, -o.y * 2.0));
2202
+ vec4 x1 = texture2D(image, position + vec2(-o.x , -o.y * 2.0));
2203
+ vec4 x2 = texture2D(image, position + vec2( 0.0 , -o.y * 2.0));
2204
+ vec4 x3 = texture2D(image, position + vec2( o.x , -o.y * 2.0));
2205
+ vec4 x4 = texture2D(image, position + vec2(-o.x * 2.0, -o.y ));
2206
+ vec4 x5 = texture2D(image, position + vec2(-o.x * 2.0, 0.0 ));
2207
+ vec4 x6 = texture2D(image, position + vec2(-o.x * 2.0, o.y ));
2208
+
2209
+ if (is_different(x0, w4)) pattern += exp2(8.0);
2210
+ if (is_different(x1, w4)) pattern += exp2(9.0);
2211
+ if (is_different(x2, w4)) pattern += exp2(10.0);
2212
+ if (is_different(x3, w4)) pattern += exp2(11.0);
2213
+ if (is_different(x4, w4)) pattern += exp2(12.0);
2214
+ if (is_different(x5, w4)) pattern += exp2(13.0);
2215
+ if (is_different(x6, w4)) pattern += exp2(14.0);
2216
+
2217
+ float diagonal_bias = -7.0;
2218
+ for (int i = 0; i < 15; ++i)
2219
+ {
2220
+ diagonal_bias += _bit(pattern, i);
2221
+ }
2222
+
2223
+ if (diagonal_bias <= 0.0)
2224
+ {
2225
+ vec4 r = mix(w1, w3, p.y - p.x + 0.5);
2226
+ if (dist < 0.5 - pixel_size / 2.0)
2227
+ {
2228
+ return r;
2229
+ }
2230
+ return mix(r, w4, (dist + pixel_size / 2.0 - 0.5) / pixel_size);
2231
+ }
2232
+
2233
+ return w4;
2234
+ }
2235
+
2236
+ void main()
2237
+ {
2238
+ gl_FragColor = scale(tex, texCoord, texSize, outputSize);
2239
+ }
2240
+ [shader]
2241
+ name=Pixelate
2242
+ author=endrift
2243
+ description=Only scale up the screen at an integer ratio
2244
+ passes=1
2245
+
2246
+ [pass.0]
2247
+ blend=1
2248
+ integerScaling=1
2249
+ [shader]
2250
+ name=Scale2x
2251
+ author=singron
2252
+ description=AdvanceMAME's Scale2x algorithm
2253
+ passes=1
2254
+
2255
+ [pass.0]
2256
+ fragmentShader=scale2x.fs
2257
+ blend=1
2258
+ width=-2
2259
+ height=-2
2260
+ /* Shader implementation of Scale2x is adapted from https://gist.github.com/singron/3161079 */
2261
+ varying vec2 texCoord;
2262
+ uniform sampler2D tex;
2263
+ uniform vec2 texSize;
2264
+
2265
+ void main() {
2266
+ // o = offset, the width of a pixel
2267
+ vec2 o = 1.0 / texSize;
2268
+
2269
+ // texel arrangement
2270
+ // A B C
2271
+ // D E F
2272
+ // G H I
2273
+ vec4 B = texture2D(tex, texCoord + vec2( 0.0, o.y));
2274
+ vec4 D = texture2D(tex, texCoord + vec2( -o.x, 0.0));
2275
+ vec4 E = texture2D(tex, texCoord + vec2( 0.0, 0.0));
2276
+ vec4 F = texture2D(tex, texCoord + vec2( o.x, 0.0));
2277
+ vec4 H = texture2D(tex, texCoord + vec2( 0.0, -o.y));
2278
+ vec2 p = texCoord * texSize;
2279
+ // p = the texCoord within a pixel [0...1]
2280
+ p = fract(p);
2281
+ if (p.x > .5) {
2282
+ if (p.y > .5) {
2283
+ // Top Right
2284
+ gl_FragColor = B == F && B != D && F != H ? F : E;
2285
+ } else {
2286
+ // Bottom Right
2287
+ gl_FragColor = H == F && D != H && B != F ? F : E;
2288
+ }
2289
+ } else {
2290
+ if (p.y > .5) {
2291
+ // Top Left
2292
+ gl_FragColor = D == B && B != F && D != H ? D : E;
2293
+ } else {
2294
+ // Bottom Left
2295
+ gl_FragColor = D == H && D != B && H != F ? D : E;
2296
+ }
2297
+ }
2298
+ }
2299
+ [shader]
2300
+ name=Scale4x
2301
+ author=singron, endrift
2302
+ description=AdvanceMAME's Scale4x algorithm
2303
+ passes=1
2304
+
2305
+ [pass.0]
2306
+ fragmentShader=scale4x.fs
2307
+ blend=1
2308
+ width=-4
2309
+ height=-4
2310
+ /* Shader implementation of Scale2x is adapted from https://gist.github.com/singron/3161079 */
2311
+ varying vec2 texCoord;
2312
+ uniform sampler2D tex;
2313
+ uniform vec2 texSize;
2314
+
2315
+ vec4 scale2x(vec4 pixels[5], vec2 p) {
2316
+ // texel arrangement
2317
+ // x 0 x
2318
+ // 1 2 3
2319
+ // x 4 x
2320
+ // p = the texCoord within a pixel [0...1]
2321
+ p = fract(p);
2322
+ if (p.x > .5) {
2323
+ if (p.y > .5) {
2324
+ // Top Right
2325
+ return pixels[0] == pixels[3] && pixels[0] != pixels[1] && pixels[3] != pixels[4] ? pixels[3] : pixels[2];
2326
+ } else {
2327
+ // Bottom Right
2328
+ return pixels[4] == pixels[3] && pixels[1] != pixels[4] && pixels[0] != pixels[3] ? pixels[3] : pixels[2];
2329
+ }
2330
+ } else {
2331
+ if (p.y > .5) {
2332
+ // Top Left
2333
+ return pixels[1] == pixels[0] && pixels[0] != pixels[3] && pixels[1] != pixels[4] ? pixels[1] : pixels[2];
2334
+ } else {
2335
+ // Bottom Left
2336
+ return pixels[1] == pixels[4] && pixels[1] != pixels[0] && pixels[4] != pixels[3] ? pixels[1] : pixels[2];
2337
+ }
2338
+ }
2339
+ }
2340
+
2341
+ vec4 scaleNeighborhood(vec2 p, vec2 x, vec2 o) {
2342
+ vec4 neighborhood[5];
2343
+ neighborhood[0] = texture2D(tex, texCoord + x + vec2( 0.0, o.y));
2344
+ neighborhood[1] = texture2D(tex, texCoord + x + vec2(-o.x, 0.0));
2345
+ neighborhood[2] = texture2D(tex, texCoord + x + vec2( 0.0, 0.0));
2346
+ neighborhood[3] = texture2D(tex, texCoord + x + vec2( o.x, 0.0));
2347
+ neighborhood[4] = texture2D(tex, texCoord + x + vec2( 0.0, -o.y));
2348
+ return scale2x(neighborhood, p + x * texSize);
2349
+ }
2350
+
2351
+ void main() {
2352
+ // o = offset, the width of a pixel
2353
+ vec2 o = 1.0 / texSize;
2354
+
2355
+ vec2 p = texCoord * texSize;
2356
+ vec4 pixels[5];
2357
+ pixels[0] = scaleNeighborhood(p, vec2( 0.0, o.y / 2.0), o);
2358
+ pixels[1] = scaleNeighborhood(p, vec2(-o.x / 2.0, 0.0), o);
2359
+ pixels[2] = scaleNeighborhood(p, vec2( 0.0, 0.0), o);
2360
+ pixels[3] = scaleNeighborhood(p, vec2( o.x / 2.0, 0.0), o);
2361
+ pixels[4] = scaleNeighborhood(p, vec2( 0.0, -o.y / 2.0), o);
2362
+ gl_FragColor = scale2x(pixels, p * 2.0);
2363
+ }
2364
+ [shader]
2365
+ name=Scanlines
2366
+ author=Dominus Iniquitatis
2367
+ description=Simple scanlines.
2368
+ passes=1
2369
+
2370
+ [pass.0]
2371
+ fragmentShader=scanlines.fs
2372
+ blend=1
2373
+ width=-2
2374
+ height=-2
2375
+
2376
+ [pass.0.uniform.lineBrightness]
2377
+ type=float
2378
+ readableName=Line brightness
2379
+ default=0.5
2380
+ min=0.0
2381
+ max=1.0
2382
+ /*
2383
+ Scanlines Shader
2384
+
2385
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
2386
+
2387
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2388
+ of this software and associated documentation files (the "Software"), to deal
2389
+ in the Software without restriction, including without limitation the rights
2390
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2391
+ copies of the Software, and to permit persons to whom the Software is
2392
+ furnished to do so, subject to the following conditions:
2393
+ The above copyright notice and this permission notice shall be included in
2394
+ all copies or substantial portions of the Software.
2395
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2396
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2397
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2398
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2399
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2400
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2401
+ THE SOFTWARE.
2402
+ */
2403
+
2404
+ uniform sampler2D tex;
2405
+ uniform vec2 texSize;
2406
+ varying vec2 texCoord;
2407
+
2408
+ uniform float lineBrightness;
2409
+
2410
+ void main() {
2411
+ vec4 color = texture2D(tex, texCoord);
2412
+
2413
+ // Compute the Y coordinate in pixel space
2414
+ float y = texCoord.y * texSize.y;
2415
+
2416
+ // Use floor and mod to get even/odd row
2417
+ if (mod(floor(y), 2.0) < 1.0) {
2418
+ color.rgb *= lineBrightness;
2419
+ }
2420
+
2421
+ gl_FragColor = color;
2422
+ }
2423
+ [shader]
2424
+ name=Soften
2425
+ author=Dominus Iniquitatis
2426
+ description=Soft image blurring.
2427
+ passes=1
2428
+
2429
+ [pass.0]
2430
+ fragmentShader=soften.fs
2431
+ blend=1
2432
+ width=-1
2433
+ height=-1
2434
+
2435
+ [pass.0.uniform.amount]
2436
+ type=float
2437
+ readableName=Amount
2438
+ default=0.5
2439
+ min=0.0
2440
+ max=1.0
2441
+ /*
2442
+ Soften Shader
2443
+
2444
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
2445
+
2446
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2447
+ of this software and associated documentation files (the "Software"), to deal
2448
+ in the Software without restriction, including without limitation the rights
2449
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2450
+ copies of the Software, and to permit persons to whom the Software is
2451
+ furnished to do so, subject to the following conditions:
2452
+ The above copyright notice and this permission notice shall be included in
2453
+ all copies or substantial portions of the Software.
2454
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2455
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2456
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2457
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2458
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2459
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2460
+ THE SOFTWARE.
2461
+ */
2462
+
2463
+ uniform sampler2D tex;
2464
+ uniform vec2 texSize;
2465
+ varying vec2 texCoord;
2466
+
2467
+ uniform float amount;
2468
+
2469
+ vec2 GetTexelSize()
2470
+ {
2471
+ return vec2(1.0 / texSize.x, 1.0 / texSize.y);
2472
+ }
2473
+
2474
+ void main()
2475
+ {
2476
+ vec4 color = texture2D(tex, texCoord);
2477
+
2478
+ vec4 northColor = texture2D(tex, texCoord + vec2(0.0, GetTexelSize().y));
2479
+ vec4 southColor = texture2D(tex, texCoord - vec2(0.0, GetTexelSize().y));
2480
+ vec4 eastColor = texture2D(tex, texCoord + vec2(GetTexelSize().x, 0.0));
2481
+ vec4 westColor = texture2D(tex, texCoord - vec2(GetTexelSize().x, 0.0));
2482
+
2483
+ if (abs(length(color) - length(northColor)) > 0.0)
2484
+ {
2485
+ color = mix(color, northColor, amount / 4.0);
2486
+ }
2487
+
2488
+ if (abs(length(color) - length(southColor)) > 0.0)
2489
+ {
2490
+ color = mix(color, southColor, amount / 4.0);
2491
+ }
2492
+
2493
+ if (abs(length(color) - length(eastColor)) > 0.0)
2494
+ {
2495
+ color = mix(color, eastColor, amount / 4.0);
2496
+ }
2497
+
2498
+ if (abs(length(color) - length(westColor)) > 0.0)
2499
+ {
2500
+ color = mix(color, westColor, amount / 4.0);
2501
+ }
2502
+
2503
+ gl_FragColor = color;
2504
+ }
2505
+ [shader]
2506
+ name=GBA SP 101 Color
2507
+ author=Pokefan531 and hunterk
2508
+ description=Shader that replicates the LCD Colorspace from a Gameboy SP 101 (backlit version).
2509
+ passes=1
2510
+
2511
+ [pass.0]
2512
+ fragmentShader=sp101-color.fs
2513
+ vertexShader=sp101-color.vs
2514
+ blend=1
2515
+ width=-1
2516
+ height=-1
2517
+
2518
+ [pass.0.uniform.color_mode]
2519
+ type=int
2520
+ default=1
2521
+ min=1
2522
+ max=3
2523
+ readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)
2524
+ // Shader that replicates the LCD Colorspace from a Gameboy SP 101 (backlit version) --
2525
+ varying vec2 texCoord;
2526
+ varying mat4 profile;
2527
+ uniform sampler2D tex;
2528
+ uniform vec2 texSize;
2529
+
2530
+ const float target_gamma = 2.2;
2531
+ const float display_gamma = 2.2;
2532
+
2533
+ void main() {
2534
+ // bring out our stored luminance value
2535
+ float lum = profile[3].w;
2536
+
2537
+ // our adjustments need to happen in linear gamma
2538
+ vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma)).rgba;
2539
+
2540
+ screen = clamp(screen * lum, 0.0, 1.0);
2541
+ screen = profile * screen;
2542
+ gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
2543
+ }
2544
+ uniform int color_mode;
2545
+ attribute vec4 position;
2546
+ varying vec2 texCoord;
2547
+ varying mat4 profile;
2548
+
2549
+ const mat4 SP1_sRGB = mat4(
2550
+ 0.96, 0.0325, 0.001, 0.0, //red channel
2551
+ 0.11, 0.89, -0.03, 0.0, //green channel
2552
+ -0.07, 0.0775, 1.029, 0.0, //blue channel
2553
+ 0.0, 0.0, 0.0, 0.935 //alpha channel
2554
+ );
2555
+
2556
+ const mat4 SP1_DCI = mat4(
2557
+ 0.805, 0.0675, 0.017, 0.0, //red channel
2558
+ 0.24, 0.86, 0.02, 0.0, //green channel
2559
+ -0.045, 0.0725, 0.963, 0.0, //blue channel
2560
+ 0.0, 0.0, 0.0, 0.955 //alpha channel
2561
+ );
2562
+
2563
+ const mat4 SP1_Rec2020 = mat4(
2564
+ 0.625, 0.10, 0.015, 0.0, //red channel
2565
+ 0.35, 0.82, 0.0325, 0.0, //green channel
2566
+ 0.025, 0.08, 0.9525, 0.0, //blue channel
2567
+ 0.0, 0.0, 0.0, 1.0 //alpha channel
2568
+ );
2569
+
2570
+ void main() {
2571
+ if (color_mode == 1) profile = SP1_sRGB;
2572
+ else if (color_mode == 2) profile = SP1_DCI;
2573
+ else if (color_mode == 3) profile = SP1_Rec2020;
2574
+
2575
+ gl_Position = position;
2576
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
2577
+ }
2578
+ [shader]
2579
+ name=TV Mode
2580
+ author=Dominus Iniquitatis
2581
+ description=Scanlines along with a subtle blurring.
2582
+ passes=1
2583
+
2584
+ [pass.0]
2585
+ fragmentShader=tv.fs
2586
+ blend=1
2587
+ width=-2
2588
+ height=-2
2589
+
2590
+ [pass.0.uniform.lineBrightness]
2591
+ type=float
2592
+ readableName=Line brightness
2593
+ default=0.75
2594
+ min=0.0
2595
+ max=1.0
2596
+
2597
+ [pass.0.uniform.blurring]
2598
+ type=float
2599
+ readableName=Blurring
2600
+ default=1.0
2601
+ min=0.0
2602
+ max=1.0
2603
+ /*
2604
+ TV Mode Shader
2605
+
2606
+ Copyright (C) 2022 Dominus Iniquitatis - zerosaiko@gmail.com
2607
+
2608
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2609
+ of this software and associated documentation files (the "Software"), to deal
2610
+ in the Software without restriction, including without limitation the rights
2611
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2612
+ copies of the Software, and to permit persons to whom the Software is
2613
+ furnished to do so, subject to the following conditions:
2614
+ The above copyright notice and this permission notice shall be included in
2615
+ all copies or substantial portions of the Software.
2616
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2617
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2618
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2619
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2620
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2621
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2622
+ THE SOFTWARE.
2623
+ */
2624
+
2625
+ uniform sampler2D tex;
2626
+ uniform vec2 texSize;
2627
+ varying vec2 texCoord;
2628
+
2629
+ uniform float lineBrightness;
2630
+ uniform float blurring;
2631
+
2632
+ void main()
2633
+ {
2634
+ vec4 c = texture2D(tex, texCoord);
2635
+ vec4 n = texture2D(tex, texCoord + vec2(1.0 / texSize.x * 0.5, 0.0));
2636
+
2637
+ vec4 color = mix(c, (c + n) / 2.0, blurring);
2638
+ color.a = c.a;
2639
+
2640
+ if (int(mod(texCoord.t * texSize.y * 2.0, 2.0)) == 0)
2641
+ {
2642
+ color.rgb *= lineBrightness;
2643
+ }
2644
+
2645
+ gl_FragColor = color;
2646
+ }
2647
+ [shader]
2648
+ name=VBA Pixelate
2649
+ author=Dominus Iniquitatis
2650
+ description=VisualBoyAdvance-style pixelation.
2651
+ passes=1
2652
+
2653
+ [pass.0]
2654
+ fragmentShader=vba_pixelate.fs
2655
+ blend=1
2656
+ width=-2
2657
+ height=-2
2658
+
2659
+ [pass.0.uniform.boundBrightness]
2660
+ type=float
2661
+ readableName=Bound brightness
2662
+ default=0.5
2663
+ min=0.0
2664
+ max=1.0
2665
+ /*
2666
+ VBA Pixelate Shader
2667
+
2668
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
2669
+
2670
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2671
+ of this software and associated documentation files (the "Software"), to deal
2672
+ in the Software without restriction, including without limitation the rights
2673
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2674
+ copies of the Software, and to permit persons to whom the Software is
2675
+ furnished to do so, subject to the following conditions:
2676
+ The above copyright notice and this permission notice shall be included in
2677
+ all copies or substantial portions of the Software.
2678
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2679
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2680
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2681
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2682
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2683
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2684
+ THE SOFTWARE.
2685
+ */
2686
+
2687
+ uniform sampler2D tex;
2688
+ uniform vec2 texSize;
2689
+ varying vec2 texCoord;
2690
+
2691
+ uniform float boundBrightness;
2692
+
2693
+ void main()
2694
+ {
2695
+ vec4 color = texture2D(tex, texCoord);
2696
+
2697
+ float x = floor(texCoord.s * texSize.x);
2698
+ float y = floor(texCoord.t * texSize.y);
2699
+
2700
+ bool onVerticalLine = mod(x, 2.0) == 0.0;
2701
+ bool onHorizontalLine = mod(y, 2.0) == 0.0;
2702
+
2703
+ if (onVerticalLine || onHorizontalLine)
2704
+ {
2705
+ color.rgb *= boundBrightness;
2706
+ }
2707
+
2708
+ gl_FragColor = color;
2709
+ }[shader]
2710
+ name=Vignette
2711
+ author=Dominus Iniquitatis
2712
+ description=Configurable vignette effect.
2713
+ passes=1
2714
+
2715
+ [pass.0]
2716
+ fragmentShader=vignette.fs
2717
+ blend=1
2718
+ width=-1
2719
+ height=-1
2720
+
2721
+ [pass.0.uniform.intensity]
2722
+ type=float
2723
+ readableName=Intensity
2724
+ default=1.0
2725
+ min=0.0
2726
+ max=1.0
2727
+ /*
2728
+ Vignette Shader
2729
+
2730
+ Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
2731
+
2732
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2733
+ of this software and associated documentation files (the "Software"), to deal
2734
+ in the Software without restriction, including without limitation the rights
2735
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2736
+ copies of the Software, and to permit persons to whom the Software is
2737
+ furnished to do so, subject to the following conditions:
2738
+ The above copyright notice and this permission notice shall be included in
2739
+ all copies or substantial portions of the Software.
2740
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2741
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2742
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2743
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2744
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2745
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2746
+ THE SOFTWARE.
2747
+ */
2748
+
2749
+ uniform sampler2D tex;
2750
+ uniform vec2 texSize;
2751
+ varying vec2 texCoord;
2752
+
2753
+ uniform float intensity;
2754
+
2755
+ void main()
2756
+ {
2757
+ vec4 color = texture2D(tex, texCoord);
2758
+ color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), length(texCoord - 0.5) * intensity);
2759
+
2760
+ gl_FragColor = color;
2761
+ }
2762
+ [shader]
2763
+ name=Wii U
2764
+ author=Prof. 9
2765
+ description=Uses the color palette from the Wii U Virtual Console. Enable bilinear filtering to further mimic Wii U output.
2766
+ passes=1
2767
+
2768
+ [pass.0]
2769
+ fragmentShader=wiiu.fs
2770
+ blend=1
2771
+ width=-4
2772
+ height=-4
2773
+ varying vec2 texCoord;
2774
+ uniform sampler2D tex;
2775
+ uniform vec2 texSize;
2776
+
2777
+ float wiiuLut31(float x) {
2778
+ int i = int(floor(x * 31.0 + 0.5));
2779
+
2780
+ if (i < 0) i = 0;
2781
+ if (i > 31) i = 31;
2782
+
2783
+ if (i == 0) return 0.0 / 255.0;
2784
+ if (i == 1) return 6.0 / 255.0;
2785
+ if (i == 2) return 12.0 / 255.0;
2786
+ if (i == 3) return 18.0 / 255.0;
2787
+ if (i == 4) return 24.0 / 255.0;
2788
+ if (i == 5) return 31.0 / 255.0;
2789
+ if (i == 6) return 37.0 / 255.0;
2790
+ if (i == 7) return 43.0 / 255.0;
2791
+ if (i == 8) return 49.0 / 255.0;
2792
+ if (i == 9) return 55.0 / 255.0;
2793
+ if (i == 10) return 61.0 / 255.0;
2794
+ if (i == 11) return 67.0 / 255.0;
2795
+ if (i == 12) return 73.0 / 255.0;
2796
+ if (i == 13) return 79.0 / 255.0;
2797
+ if (i == 14) return 86.0 / 255.0;
2798
+ if (i == 15) return 92.0 / 255.0;
2799
+ if (i == 16) return 98.0 / 255.0;
2800
+ if (i == 17) return 104.0 / 255.0;
2801
+ if (i == 18) return 111.0 / 255.0;
2802
+ if (i == 19) return 117.0 / 255.0;
2803
+ if (i == 20) return 123.0 / 255.0;
2804
+ if (i == 21) return 129.0 / 255.0;
2805
+ if (i == 22) return 135.0 / 255.0;
2806
+ if (i == 23) return 141.0 / 255.0;
2807
+ if (i == 24) return 148.0 / 255.0;
2808
+ if (i == 25) return 154.0 / 255.0;
2809
+ if (i == 26) return 159.0 / 255.0;
2810
+ if (i == 27) return 166.0 / 255.0;
2811
+ if (i == 28) return 172.0 / 255.0;
2812
+ if (i == 29) return 178.0 / 255.0;
2813
+ if (i == 30) return 184.0 / 255.0;
2814
+ // i == 31
2815
+ return 191.0 / 255.0;
2816
+ }
2817
+
2818
+ void main() {
2819
+ vec4 color = texture2D(tex, texCoord);
2820
+
2821
+ color.r = wiiuLut31(color.r);
2822
+ color.g = wiiuLut31(color.g);
2823
+ color.b = wiiuLut31(color.b);
2824
+
2825
+ gl_FragColor = color;
2826
+ }
2827
+ [shader]
2828
+ name=xBR-lv2
2829
+ author=Hyllian
2830
+ description=xBR-lv2 upsampling filter
2831
+ passes=1
2832
+
2833
+ [pass.0]
2834
+ integerScaling=1
2835
+ vertexShader=xbr.vs
2836
+ fragmentShader=xbr.fs
2837
+
2838
+ [pass.0.uniform.XBR_Y_WEIGHT]
2839
+ type=float
2840
+ default=48
2841
+ readableName=Y Weight
2842
+ min=0
2843
+ max=100
2844
+
2845
+ [pass.0.uniform.XBR_EQ_THRESHOLD]
2846
+ type=float
2847
+ readableName=Eq Threshold
2848
+ default=25.0
2849
+ min=0.0
2850
+ max=50.0
2851
+
2852
+ [pass.0.uniform.XBR_SCALE]
2853
+ type=float
2854
+ readableName=xBR Scale
2855
+ default=4.0
2856
+ min=1.0
2857
+ max=5.0
2858
+
2859
+ [pass.0.uniform.XBR_LV2_COEFFICIENT]
2860
+ type=float
2861
+ readableName=Lv2 Coefficient
2862
+ default=2.0
2863
+ min=1.0
2864
+ max=3.0
2865
+ /*
2866
+ Hyllian's xBR-lv2 Shader
2867
+
2868
+ Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
2869
+
2870
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2871
+ of this software and associated documentation files (the "Software"), to deal
2872
+ in the Software without restriction, including without limitation the rights
2873
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2874
+ copies of the Software, and to permit persons to whom the Software is
2875
+ furnished to do so, subject to the following conditions:
2876
+
2877
+ The above copyright notice and this permission notice shall be included in
2878
+ all copies or substantial portions of the Software.
2879
+
2880
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2881
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2882
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2883
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2884
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2885
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2886
+ THE SOFTWARE.
2887
+
2888
+
2889
+ Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
2890
+ */
2891
+
2892
+ uniform float XBR_Y_WEIGHT;
2893
+ uniform float XBR_EQ_THRESHOLD;
2894
+ uniform float XBR_SCALE;
2895
+ uniform float XBR_LV2_COEFFICIENT;
2896
+
2897
+ uniform sampler2D tex;
2898
+ uniform vec2 texSize;
2899
+
2900
+ varying vec2 texCoord;
2901
+ varying vec4 TEX1;
2902
+ varying vec4 TEX2;
2903
+ varying vec4 TEX3;
2904
+ varying vec4 TEX4;
2905
+ varying vec4 TEX5;
2906
+ varying vec4 TEX6;
2907
+ varying vec4 TEX7;
2908
+
2909
+ const vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
2910
+ const vec4 Bo = vec4( 1.0, 1.0, -1.0, -1.0 );
2911
+ const vec4 Co = vec4( 1.5, 0.5, -0.5, 0.5 );
2912
+ const vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
2913
+ const vec4 Bx = vec4( 0.5, 2.0, -0.5, -2.0 );
2914
+ const vec4 Cx = vec4( 1.0, 1.0, -0.5, 0.0 );
2915
+ const vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
2916
+ const vec4 By = vec4( 2.0, 0.5, -2.0, -0.5 );
2917
+ const vec4 Cy = vec4( 2.0, 0.0, -1.0, 0.5 );
2918
+ const vec4 Ci = vec4( 0.25, 0.25, 0.25, 0.25 );
2919
+
2920
+ const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
2921
+
2922
+ vec4 df4(vec4 A, vec4 B) {
2923
+ return abs(A - B);
2924
+ }
2925
+
2926
+ float c_df(vec3 c1, vec3 c2) {
2927
+ vec3 d = abs(c1 - c2);
2928
+ return d.r + d.g + d.b;
2929
+ }
2930
+
2931
+ bvec4 eq4(vec4 A, vec4 B) {
2932
+ return lessThan(df4(A, B), vec4(XBR_EQ_THRESHOLD));
2933
+ }
2934
+
2935
+ bvec4 and4(bvec4 A, bvec4 B) {
2936
+ return bvec4(A.x && B.x, A.y && B.y, A.z && B.z, A.w && B.w);
2937
+ }
2938
+
2939
+ bvec4 nand4(bvec4 A, bvec4 B) {
2940
+ return bvec4(!(A.x && B.x), !(A.y && B.y), !(A.z && B.z), !(A.w && B.w));
2941
+ }
2942
+
2943
+ vec4 weighted_distance(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h) {
2944
+ return (df4(a,b) + df4(a,c) + df4(d,e) + df4(d,f) + 4.0 * df4(g,h));
2945
+ }
2946
+
2947
+ float lum(vec3 rgb) {
2948
+ return dot(rgb, Y) * XBR_Y_WEIGHT;
2949
+ }
2950
+
2951
+ void main() {
2952
+ // fractional position inside the source pixel (in source-pixel space)
2953
+ vec2 fp = fract(texCoord * texSize);
2954
+
2955
+ // Sample neighborhood
2956
+ vec3 A1 = texture2D(tex, TEX1.xw).rgb;
2957
+ vec3 B1 = texture2D(tex, TEX1.yw).rgb;
2958
+ vec3 C1 = texture2D(tex, TEX1.zw).rgb;
2959
+
2960
+ vec3 A = texture2D(tex, TEX2.xw).rgb;
2961
+ vec3 B = texture2D(tex, TEX2.yw).rgb;
2962
+ vec3 C = texture2D(tex, TEX2.zw).rgb;
2963
+
2964
+ vec3 D = texture2D(tex, TEX3.xw).rgb;
2965
+ vec3 E = texture2D(tex, TEX3.yw).rgb;
2966
+ vec3 F = texture2D(tex, TEX3.zw).rgb;
2967
+
2968
+ vec3 G = texture2D(tex, TEX4.xw).rgb;
2969
+ vec3 H = texture2D(tex, TEX4.yw).rgb;
2970
+ vec3 I = texture2D(tex, TEX4.zw).rgb;
2971
+
2972
+ vec3 G5 = texture2D(tex, TEX5.xw).rgb;
2973
+ vec3 H5 = texture2D(tex, TEX5.yw).rgb;
2974
+ vec3 I5 = texture2D(tex, TEX5.zw).rgb;
2975
+
2976
+ vec3 A0 = texture2D(tex, TEX6.xy).rgb;
2977
+ vec3 D0 = texture2D(tex, TEX6.xz).rgb;
2978
+ vec3 G0 = texture2D(tex, TEX6.xw).rgb;
2979
+
2980
+ vec3 C4 = texture2D(tex, TEX7.xy).rgb;
2981
+ vec3 F4 = texture2D(tex, TEX7.xz).rgb;
2982
+ vec3 I4 = texture2D(tex, TEX7.xw).rgb;
2983
+
2984
+ // Replace mat4x3/transpose with explicit luma packing (GLSL ES 1.00 safe)
2985
+ vec4 b = vec4(lum(B), lum(D), lum(H), lum(F));
2986
+ vec4 c = vec4(lum(C), lum(A), lum(G), lum(I));
2987
+ vec4 e = vec4(lum(E), lum(E), lum(E), lum(E));
2988
+ vec4 d = b.yzwx;
2989
+ vec4 f = b.wxyz;
2990
+ vec4 g = c.zwxy;
2991
+ vec4 h = b.zwxy;
2992
+ vec4 i = c.wxyz;
2993
+
2994
+ vec4 i4v = vec4(lum(I4), lum(C1), lum(A0), lum(G5));
2995
+ vec4 i5v = vec4(lum(I5), lum(C4), lum(A1), lum(G0));
2996
+ vec4 h5v = vec4(lum(H5), lum(F4), lum(B1), lum(D0));
2997
+ vec4 f4v = h5v.yzwx;
2998
+
2999
+ // Edge detection math
3000
+ vec4 fx = (Ao * fp.y + Bo * fp.x);
3001
+ vec4 fx_left = (Ax * fp.y + Bx * fp.x);
3002
+ vec4 fx_up = (Ay * fp.y + By * fp.x);
3003
+
3004
+ bvec4 interp_restriction_lv0 = and4(notEqual(e, f), notEqual(e, h));
3005
+ bvec4 interp_restriction_lv1 = interp_restriction_lv0;
3006
+
3007
+ bvec4 interp_restriction_lv2_left = and4(notEqual(e, g), notEqual(d, g));
3008
+ bvec4 interp_restriction_lv2_up = and4(notEqual(e, c), notEqual(b, c));
3009
+
3010
+ vec4 delta = vec4(1.0 / XBR_SCALE);
3011
+ vec4 deltaL = vec4(0.5 / XBR_SCALE, 1.0 / XBR_SCALE, 0.5 / XBR_SCALE, 1.0 / XBR_SCALE);
3012
+ vec4 deltaU = deltaL.yxwz;
3013
+
3014
+ vec4 fx45i = clamp((fx + delta - Co - Ci) / (2.0 * delta ), 0.0, 1.0);
3015
+ vec4 fx45 = clamp((fx + delta - Co ) / (2.0 * delta ), 0.0, 1.0);
3016
+ vec4 fx30 = clamp((fx_left + deltaL - Co ) / (2.0 * deltaL), 0.0, 1.0);
3017
+ vec4 fx60 = clamp((fx_up + deltaU - Co ) / (2.0 * deltaU), 0.0, 1.0);
3018
+
3019
+ vec4 wd1 = weighted_distance(e, c, g, i, h5v, f4v, h, f);
3020
+ vec4 wd2 = weighted_distance(h, d, i5v, f, i4v, b, e, i);
3021
+
3022
+ bvec4 edri = and4(lessThanEqual(wd1, wd2), interp_restriction_lv0);
3023
+ bvec4 edr = and4(lessThan(wd1, wd2), interp_restriction_lv1);
3024
+
3025
+ bvec4 edr_left = and4(
3026
+ lessThanEqual((XBR_LV2_COEFFICIENT * df4(f, g)), df4(h, c)),
3027
+ interp_restriction_lv2_left
3028
+ );
3029
+ bvec4 edr_up = and4(
3030
+ greaterThanEqual(df4(f, g), (XBR_LV2_COEFFICIENT * df4(h, c))),
3031
+ interp_restriction_lv2_up
3032
+ );
3033
+
3034
+ edr = and4(edr, nand4(edri.yzwx, edri.wxyz));
3035
+ edr_left = and4(and4(edr_left, edr), eq4(e, c));
3036
+ edr_up = and4(and4(edr_up, edr), eq4(e, g));
3037
+
3038
+ fx45 *= vec4(edr);
3039
+ fx30 *= vec4(edr_left);
3040
+ fx60 *= vec4(edr_up);
3041
+ fx45i *= vec4(edri);
3042
+
3043
+ vec4 px = vec4(lessThanEqual(df4(e, f), df4(e, h)));
3044
+
3045
+ vec4 maximos = max(max(fx30, fx60), max(fx45, fx45i));
3046
+
3047
+ vec3 res1 = E;
3048
+ res1 = mix(res1, mix(H, F, px.x), maximos.x);
3049
+ res1 = mix(res1, mix(B, D, px.z), maximos.z);
3050
+
3051
+ vec3 res2 = E;
3052
+ res2 = mix(res1, mix(F, B, px.y), maximos.y);
3053
+ res2 = mix(res1, mix(D, H, px.w), maximos.w);
3054
+
3055
+ vec3 res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));
3056
+
3057
+ gl_FragColor = vec4(res, 1.0);
3058
+ }/*
3059
+ Hyllian's xBR-lv3 Shader
3060
+
3061
+ Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
3062
+
3063
+ Permission is hereby granted, free of charge, to any person obtaining a copy
3064
+ of this software and associated documentation files (the "Software"), to deal
3065
+ in the Software without restriction, including without limitation the rights
3066
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3067
+ copies of the Software, and to permit persons to whom the Software is
3068
+ furnished to do so, subject to the following conditions:
3069
+
3070
+ The above copyright notice and this permission notice shall be included in
3071
+ all copies or substantial portions of the Software.
3072
+
3073
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3074
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3075
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3076
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3077
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3078
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3079
+ THE SOFTWARE.
3080
+
3081
+ Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
3082
+ */
3083
+
3084
+ attribute vec4 position;
3085
+
3086
+ uniform vec2 texSize;
3087
+
3088
+ varying vec2 texCoord;
3089
+ varying vec4 TEX1;
3090
+ varying vec4 TEX2;
3091
+ varying vec4 TEX3;
3092
+ varying vec4 TEX4;
3093
+ varying vec4 TEX5;
3094
+ varying vec4 TEX6;
3095
+ varying vec4 TEX7;
3096
+
3097
+ void main() {
3098
+ gl_Position = position;
3099
+
3100
+ vec2 ps = vec2(1.0) / texSize;
3101
+ float dx = ps.x;
3102
+ float dy = ps.y;
3103
+
3104
+ // A1 B1 C1
3105
+ // A0 A B C C4
3106
+ // D0 D E F F4
3107
+ // G0 G H I I4
3108
+ // G5 H5 I5
3109
+
3110
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
3111
+
3112
+ // IMPORTANT: use 0.0 (float literal) for strict GLSL ES 1.00 compilers
3113
+ TEX1 = texCoord.xxxy + vec4(-dx, 0.0, dx, -2.0 * dy); // A1 B1 C1
3114
+ TEX2 = texCoord.xxxy + vec4(-dx, 0.0, dx, -1.0 * dy); // A B C
3115
+ TEX3 = texCoord.xxxy + vec4(-dx, 0.0, dx, 0.0 ); // D E F
3116
+ TEX4 = texCoord.xxxy + vec4(-dx, 0.0, dx, 1.0 * dy); // G H I
3117
+ TEX5 = texCoord.xxxy + vec4(-dx, 0.0, dx, 2.0 * dy); // G5 H5 I5
3118
+ TEX6 = texCoord.xyyy + vec4(-2.0 * dx, -dy, 0.0, dy); // A0 D0 G0
3119
+ TEX7 = texCoord.xyyy + vec4( 2.0 * dx, -dy, 0.0, dy); // C4 F4 I4
3120
+ }[shader]
3121
+ name=xBR-lv3
3122
+ author=Hyllian
3123
+ description=xBR-lv3 upsampling filter
3124
+ passes=1
3125
+
3126
+ [pass.0]
3127
+ integerScaling=1
3128
+ vertexShader=xbr.vs
3129
+ fragmentShader=xbr.fs
3130
+
3131
+ [pass.0.uniform.XBR_Y_WEIGHT]
3132
+ type=float
3133
+ default=48
3134
+ readableName=Y Weight
3135
+ min=0
3136
+ max=100
3137
+
3138
+ [pass.0.uniform.XBR_EQ_THRESHOLD]
3139
+ type=float
3140
+ readableName=Eq Threshold
3141
+ default=10.0
3142
+ min=0.0
3143
+ max=50.0
3144
+
3145
+ [pass.0.uniform.XBR_EQ_THRESHOLD2]
3146
+ type=float
3147
+ readableName=Eq Threshold2
3148
+ default=2.0
3149
+ min=0.0
3150
+ max=4.0
3151
+
3152
+ [pass.0.uniform.XBR_LV2_COEFFICIENT]
3153
+ type=float
3154
+ readableName=Lv2 Coefficient
3155
+ default=2.0
3156
+ min=1.0
3157
+ max=3.0
3158
+ /*
3159
+ Hyllian's xBR-lv3 Shader
3160
+
3161
+ Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
3162
+
3163
+ Permission is hereby granted, free of charge, to any person obtaining a copy
3164
+ of this software and associated documentation files (the "Software"), to deal
3165
+ in the Software without restriction, including without limitation the rights
3166
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3167
+ copies of the Software, and to permit persons to whom the Software is
3168
+ furnished to do so, subject to the following conditions:
3169
+
3170
+ The above copyright notice and this permission notice shall be included in
3171
+ all copies or substantial portions of the Software.
3172
+
3173
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3174
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3175
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3176
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3177
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3178
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3179
+ THE SOFTWARE.
3180
+
3181
+
3182
+ Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
3183
+ */
3184
+
3185
+ uniform float XBR_Y_WEIGHT;
3186
+ uniform float XBR_EQ_THRESHOLD;
3187
+ uniform float XBR_EQ_THRESHOLD2;
3188
+ uniform float XBR_LV2_COEFFICIENT;
3189
+
3190
+ uniform sampler2D tex;
3191
+ uniform vec2 texSize;
3192
+
3193
+ varying vec2 texCoord;
3194
+ varying vec4 TEX1;
3195
+ varying vec4 TEX2;
3196
+ varying vec4 TEX3;
3197
+ varying vec4 TEX4;
3198
+ varying vec4 TEX5;
3199
+ varying vec4 TEX6;
3200
+ varying vec4 TEX7;
3201
+
3202
+ const mat3 yuv = mat3(
3203
+ 0.299, 0.587, 0.114,
3204
+ -0.169, -0.331, 0.499,
3205
+ 0.499, -0.418, -0.0813
3206
+ );
3207
+
3208
+ // keep same delta
3209
+ const vec4 delta = vec4(0.4, 0.4, 0.4, 0.4);
3210
+
3211
+ vec4 df4(vec4 A, vec4 B) {
3212
+ return abs(A - B);
3213
+ }
3214
+
3215
+ float c_df(vec3 c1, vec3 c2) {
3216
+ vec3 d = abs(c1 - c2);
3217
+ return d.r + d.g + d.b;
3218
+ }
3219
+
3220
+ bvec4 eq4(vec4 A, vec4 B) {
3221
+ return lessThan(df4(A, B), vec4(XBR_EQ_THRESHOLD));
3222
+ }
3223
+
3224
+ bvec4 eq24(vec4 A, vec4 B) {
3225
+ return lessThan(df4(A, B), vec4(XBR_EQ_THRESHOLD2));
3226
+ }
3227
+
3228
+ bvec4 and4(bvec4 A, bvec4 B) {
3229
+ return bvec4(A.x && B.x, A.y && B.y, A.z && B.z, A.w && B.w);
3230
+ }
3231
+
3232
+ bvec4 or4(bvec4 A, bvec4 B) {
3233
+ return bvec4(A.x || B.x, A.y || B.y, A.z || B.z, A.w || B.w);
3234
+ }
3235
+
3236
+ vec4 weighted_distance(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h) {
3237
+ return (df4(a,b) + df4(a,c) + df4(d,e) + df4(d,f) + 4.0 * df4(g,h));
3238
+ }
3239
+
3240
+ void main() {
3241
+ bvec4 edr, edr_left, edr_up, edr3_left, edr3_up, px;
3242
+ bvec4 interp_restriction_lv1, interp_restriction_lv2_left, interp_restriction_lv2_up;
3243
+ bvec4 interp_restriction_lv3_left, interp_restriction_lv3_up;
3244
+ bvec4 nc, nc30, nc60, nc45, nc15, nc75;
3245
+ vec4 fx, fx_left, fx_up, fx3_left, fx3_up;
3246
+ vec3 res1, res2, pix1, pix2;
3247
+ float blend1, blend2;
3248
+
3249
+ // IMPORTANT: initialize to avoid undefined reads on some compilers
3250
+ pix1 = vec3(0.0);
3251
+ pix2 = vec3(0.0);
3252
+ blend1 = 0.0;
3253
+ blend2 = 0.0;
3254
+
3255
+ vec2 fp = fract(texCoord * texSize);
3256
+
3257
+ vec3 A1 = texture2D(tex, TEX1.xw).rgb;
3258
+ vec3 B1 = texture2D(tex, TEX1.yw).rgb;
3259
+ vec3 C1 = texture2D(tex, TEX1.zw).rgb;
3260
+
3261
+ vec3 A = texture2D(tex, TEX2.xw).rgb;
3262
+ vec3 B = texture2D(tex, TEX2.yw).rgb;
3263
+ vec3 C = texture2D(tex, TEX2.zw).rgb;
3264
+
3265
+ vec3 D = texture2D(tex, TEX3.xw).rgb;
3266
+ vec3 E = texture2D(tex, TEX3.yw).rgb;
3267
+ vec3 F = texture2D(tex, TEX3.zw).rgb;
3268
+
3269
+ vec3 G = texture2D(tex, TEX4.xw).rgb;
3270
+ vec3 H = texture2D(tex, TEX4.yw).rgb;
3271
+ vec3 I = texture2D(tex, TEX4.zw).rgb;
3272
+
3273
+ vec3 G5 = texture2D(tex, TEX5.xw).rgb;
3274
+ vec3 H5 = texture2D(tex, TEX5.yw).rgb;
3275
+ vec3 I5 = texture2D(tex, TEX5.zw).rgb;
3276
+
3277
+ vec3 A0 = texture2D(tex, TEX6.xy).rgb;
3278
+ vec3 D0 = texture2D(tex, TEX6.xz).rgb;
3279
+ vec3 G0 = texture2D(tex, TEX6.xw).rgb;
3280
+
3281
+ vec3 C4 = texture2D(tex, TEX7.xy).rgb;
3282
+ vec3 F4 = texture2D(tex, TEX7.xz).rgb;
3283
+ vec3 I4 = texture2D(tex, TEX7.xw).rgb;
3284
+
3285
+ // Replace mat4x3 + transpose with explicit dot products (GLSL ES 1.00 safe)
3286
+ // Original: transpose(mat4x3(...)) * (XBR_Y_WEIGHT * yuv[0])
3287
+ vec3 Yw = (XBR_Y_WEIGHT * yuv[0]);
3288
+
3289
+ vec4 b = vec4(dot(B, Yw), dot(D, Yw), dot(H, Yw), dot(F, Yw));
3290
+ vec4 c = vec4(dot(C, Yw), dot(A, Yw), dot(G, Yw), dot(I, Yw));
3291
+ vec4 e = vec4(dot(E, Yw), dot(E, Yw), dot(E, Yw), dot(E, Yw));
3292
+ vec4 d = b.yzwx;
3293
+ vec4 f = b.wxyz;
3294
+ vec4 g = c.zwxy;
3295
+ vec4 h = b.zwxy;
3296
+ vec4 i = c.wxyz;
3297
+
3298
+ vec4 i4v = vec4(dot(I4, Yw), dot(C1, Yw), dot(A0, Yw), dot(G5, Yw));
3299
+ vec4 i5v = vec4(dot(I5, Yw), dot(C4, Yw), dot(A1, Yw), dot(G0, Yw));
3300
+ vec4 h5v = vec4(dot(H5, Yw), dot(F4, Yw), dot(B1, Yw), dot(D0, Yw));
3301
+ vec4 f4v = h5v.yzwx;
3302
+
3303
+ vec4 c1 = i4v.yzwx;
3304
+ vec4 g0 = i5v.wxyz;
3305
+ vec4 b1 = h5v.zwxy;
3306
+ vec4 d0 = h5v.wxyz;
3307
+
3308
+ // same constants as original
3309
+ vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
3310
+ vec4 Bo = vec4( 1.0, 1.0, -1.0, -1.0 );
3311
+ vec4 Co = vec4( 1.5, 0.5, -0.5, 0.5 );
3312
+ vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
3313
+ vec4 Bx = vec4( 0.5, 2.0, -0.5, -2.0 );
3314
+ vec4 Cx = vec4( 1.0, 1.0, -0.5, 0.0 );
3315
+ vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
3316
+ vec4 By = vec4( 2.0, 0.5, -2.0, -0.5 );
3317
+ vec4 Cy = vec4( 2.0, 0.0, -1.0, 0.5 );
3318
+
3319
+ vec4 Az = vec4( 6.0, -2.0, -6.0, 2.0 );
3320
+ vec4 Bz = vec4( 2.0, 6.0, -2.0, -6.0 );
3321
+ vec4 Cz = vec4( 5.0, 3.0, -3.0, -1.0 );
3322
+ vec4 Aw = vec4( 2.0, -6.0, -2.0, 6.0 );
3323
+ vec4 Bw = vec4( 6.0, 2.0, -6.0, -2.0 );
3324
+ vec4 Cw = vec4( 5.0, -1.0, -3.0, 3.0 );
3325
+
3326
+ fx = (Ao * fp.y + Bo * fp.x);
3327
+ fx_left = (Ax * fp.y + Bx * fp.x);
3328
+ fx_up = (Ay * fp.y + By * fp.x);
3329
+ fx3_left = (Az * fp.y + Bz * fp.x);
3330
+ fx3_up = (Aw * fp.y + Bw * fp.x);
3331
+
3332
+ // Keep CORNER_* behavior EXACTLY. Default path is CORNER_C branch (the else).
3333
+ #ifdef CORNER_A
3334
+ interp_restriction_lv1 = and4(notEqual(e, f), notEqual(e, h));
3335
+ #elif defined(CORNER_B)
3336
+ interp_restriction_lv1 = ((e!=f) && (e!=h) && ( !eq4(f,b) && !eq4(h,d) || eq4(e,i) && !eq4(f,i4v) && !eq4(h,i5v) || eq4(e,g) || eq4(e,c) ) );
3337
+ #elif defined(CORNER_D)
3338
+ interp_restriction_lv1 = ((e!=f) && (e!=h) && ( !eq4(f,b) && !eq4(h,d) || eq4(e,i) && !eq4(f,i4v) && !eq4(h,i5v) || eq4(e,g) || eq4(e,c) ) && (f!=f4v && f!=i || h!=h5v && h!=i || h!=g || f!=c || eq4(b,c1) && eq4(d,g0)));
3339
+ #else
3340
+ interp_restriction_lv1 = and4(
3341
+ and4(notEqual(e, f), notEqual(e, h)),
3342
+ or4(
3343
+ or4(
3344
+ and4(not(eq4(f,b)), not(eq4(f,c))),
3345
+ and4(not(eq4(h,d)), not(eq4(h,g)))
3346
+ ),
3347
+ or4(
3348
+ and4(
3349
+ eq4(e,i),
3350
+ or4(
3351
+ and4(not(eq4(f,f4v)), not(eq4(f,i4v))),
3352
+ and4(not(eq4(h,h5v)), not(eq4(h,i5v)))
3353
+ )
3354
+ ),
3355
+ or4(eq4(e,g), eq4(e,c))
3356
+ )
3357
+ )
3358
+ );
3359
+ #endif
3360
+
3361
+ interp_restriction_lv2_left = and4(notEqual(e, g), notEqual(d, g));
3362
+ interp_restriction_lv2_up = and4(notEqual(e, c), notEqual(b, c));
3363
+ interp_restriction_lv3_left = and4(eq24(g, g0), not(eq24(d0, g0)));
3364
+ interp_restriction_lv3_up = and4(eq24(c, c1), not(eq24(b1, c1)));
3365
+
3366
+ vec4 fx45 = smoothstep(Co - delta, Co + delta, fx);
3367
+ vec4 fx30 = smoothstep(Cx - delta, Cx + delta, fx_left);
3368
+ vec4 fx60 = smoothstep(Cy - delta, Cy + delta, fx_up);
3369
+ vec4 fx15 = smoothstep(Cz - delta, Cz + delta, fx3_left);
3370
+ vec4 fx75 = smoothstep(Cw - delta, Cw + delta, fx3_up);
3371
+
3372
+ edr = and4(
3373
+ lessThan(
3374
+ weighted_distance(e, c, g, i, h5v, f4v, h, f),
3375
+ weighted_distance(h, d, i5v, f, i4v, b, e, i)
3376
+ ),
3377
+ interp_restriction_lv1
3378
+ );
3379
+
3380
+ edr_left = and4(lessThanEqual((XBR_LV2_COEFFICIENT * df4(f,g)), df4(h,c)), interp_restriction_lv2_left);
3381
+ edr_up = and4(greaterThanEqual(df4(f,g), (XBR_LV2_COEFFICIENT * df4(h,c))), interp_restriction_lv2_up);
3382
+ edr3_left = interp_restriction_lv3_left;
3383
+ edr3_up = interp_restriction_lv3_up;
3384
+
3385
+ nc45 = and4(edr, bvec4(fx45));
3386
+ nc30 = and4(edr, and4(edr_left, bvec4(fx30)));
3387
+ nc60 = and4(edr, and4(edr_up, bvec4(fx60)));
3388
+ nc15 = and4(and4(edr, edr_left), and4(edr3_left, bvec4(fx15)));
3389
+ nc75 = and4(and4(edr, edr_up), and4(edr3_up, bvec4(fx75)));
3390
+
3391
+ px = lessThanEqual(df4(e, f), df4(e, h));
3392
+
3393
+ nc = bvec4(
3394
+ (nc75.x || nc15.x || nc30.x || nc60.x || nc45.x),
3395
+ (nc75.y || nc15.y || nc30.y || nc60.y || nc45.y),
3396
+ (nc75.z || nc15.z || nc30.z || nc60.z || nc45.z),
3397
+ (nc75.w || nc15.w || nc30.w || nc60.w || nc45.w)
3398
+ );
3399
+
3400
+ vec4 final45 = vec4(nc45) * fx45;
3401
+ vec4 final30 = vec4(nc30) * fx30;
3402
+ vec4 final60 = vec4(nc60) * fx60;
3403
+ vec4 final15 = vec4(nc15) * fx15;
3404
+ vec4 final75 = vec4(nc75) * fx75;
3405
+
3406
+ vec4 maximo = max(max(max(final15, final75), max(final30, final60)), final45);
3407
+
3408
+ if (nc.x) { pix1 = px.x ? F : H; blend1 = maximo.x; }
3409
+ else if (nc.y) { pix1 = px.y ? B : F; blend1 = maximo.y; }
3410
+ else if (nc.z) { pix1 = px.z ? D : B; blend1 = maximo.z; }
3411
+ else if (nc.w) { pix1 = px.w ? H : D; blend1 = maximo.w; }
3412
+
3413
+ if (nc.w) { pix2 = px.w ? H : D; blend2 = maximo.w; }
3414
+ else if (nc.z) { pix2 = px.z ? D : B; blend2 = maximo.z; }
3415
+ else if (nc.y) { pix2 = px.y ? B : F; blend2 = maximo.y; }
3416
+ else if (nc.x) { pix2 = px.x ? F : H; blend2 = maximo.x; }
3417
+
3418
+ res1 = mix(E, pix1, blend1);
3419
+ res2 = mix(E, pix2, blend2);
3420
+
3421
+ vec3 res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));
3422
+
3423
+ gl_FragColor = vec4(res, 1.0);
3424
+ }/*
3425
+ Hyllian's xBR-lv3 Shader
3426
+
3427
+ Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
3428
+
3429
+ Permission is hereby granted, free of charge, to any person obtaining a copy
3430
+ of this software and associated documentation files (the "Software"), to deal
3431
+ in the Software without restriction, including without limitation the rights
3432
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3433
+ copies of the Software, and to permit persons to whom the Software is
3434
+ furnished to do so, subject to the following conditions:
3435
+
3436
+ The above copyright notice and this permission notice shall be included in
3437
+ all copies or substantial portions of the Software.
3438
+
3439
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3440
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3441
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3442
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3443
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3444
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3445
+ THE SOFTWARE.
3446
+
3447
+ Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
3448
+ */
3449
+
3450
+ attribute vec4 position;
3451
+
3452
+ uniform vec2 texSize;
3453
+
3454
+ varying vec2 texCoord;
3455
+ varying vec4 TEX1;
3456
+ varying vec4 TEX2;
3457
+ varying vec4 TEX3;
3458
+ varying vec4 TEX4;
3459
+ varying vec4 TEX5;
3460
+ varying vec4 TEX6;
3461
+ varying vec4 TEX7;
3462
+
3463
+ void main() {
3464
+ gl_Position = position;
3465
+
3466
+ vec2 ps = vec2(1.0) / texSize;
3467
+ float dx = ps.x;
3468
+ float dy = ps.y;
3469
+
3470
+ texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
3471
+
3472
+ // use 0.0 (float literals) for strict GLSL ES 1.00 compilers
3473
+ TEX1 = texCoord.xxxy + vec4(-dx, 0.0, dx, -2.0 * dy); // A1 B1 C1
3474
+ TEX2 = texCoord.xxxy + vec4(-dx, 0.0, dx, -1.0 * dy); // A B C
3475
+ TEX3 = texCoord.xxxy + vec4(-dx, 0.0, dx, 0.0 ); // D E F
3476
+ TEX4 = texCoord.xxxy + vec4(-dx, 0.0, dx, 1.0 * dy); // G H I
3477
+ TEX5 = texCoord.xxxy + vec4(-dx, 0.0, dx, 2.0 * dy); // G5 H5 I5
3478
+ TEX6 = texCoord.xyyy + vec4(-2.0 * dx, -dy, 0.0, dy); // A0 D0 G0
3479
+ TEX7 = texCoord.xyyy + vec4( 2.0 * dx, -dy, 0.0, dy); // C4 F4 I4
3480
+ }