/
grade.glsl
986 lines (724 loc) · 36.8 KB
/
grade.glsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
#version 140
/*
Grade - CRT emulation and color manipulation shader
Copyright (C) 2020-2023 Dogway (Jose Linares)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Grade (11-09-2023)
> See settings decriptions at: https://forums.libretro.com/t/dogways-grading-shader-slang/27148/442
> Ubershader grouping some monolithic color related shaders:
::color-mangler (hunterk), ntsc color tuning knobs (Doriphor), white_point (hunterk, Dogway), RA Reshade LUT.
> and the addition of:
::analogue color emulation, phosphor gamut, color space + TRC support, vibrance, HUE vs SAT, vignette (shared by Syh), black level, rolled gain and sigmoidal contrast.
**Thanks to those that helped me out keep motivated by continuous feedback and bug reports:
**Syh, Nesguy, hunterk, and the libretro forum members.
#####################################...STANDARDS...######################################
##########################################################################################
### ###
### PAL ###
### Phosphor: 470BG (#3) ###
### WP: D65 (6504K) (in practice more like 7000K-7500K range) ###
### Saturation: -0.02 ###
### ###
### NTSC-U ###
### Phosphor: P22/SMPTE-C (#1 #-3)(or a SMPTE-C based CRT phosphor gamut) ###
### WP: D65 (6504K) (in practice more like 7000K-7500K range) ###
### ###
### NTSC-J (Default) ###
### Phosphor: NTSC-J (#2) (or a NTSC-J based CRT phosphor gamut) ###
### WP: 9300K+27MPCD (8945K) (CCT from x:0.281 y:0.311)(in practice ~8500K)###
### ###
### ###
##########################################################################################
##########################################################################################
*/
#pragma parameter g_signal_type "Signal Type (0:RGB 1:Composite)" 1.0 0.0 1.0 1.0
#pragma parameter g_crtgamut "Phosphor (-2:CRT-95s -1:P22-80s 1:P22-90s 2:NTSC-J 3:PAL)" 2.0 -3.0 3.0 1.0
#pragma parameter g_space_out "Diplay Color Space (-1:709 0:sRGB 1:P3-D65 2:2020 3:Adobe)" 0.0 -1.0 3.0 1.0
#pragma parameter g_Dark_to_Dim "Dark to Dim adaptation" 1.0 0.0 1.0 1.0
#pragma parameter g_GCompress "Gamut Compression" 1.0 0.0 1.0 1.0
// Analogue controls
#pragma parameter g_analog "// ANALOG CONTROLS //" 0.0 0.0 1.0 1.0
#pragma parameter wp_temperature "White Point" 8504.0 5004.0 12004.0 100.0
#pragma parameter g_CRT_l "CRT Gamma" 2.50 2.30 2.60 0.01
#pragma parameter g_CRT_b "CRT Brightness" 50.0 0.0 100.0 1.0
#pragma parameter g_CRT_c "CRT Contrast" 50.0 0.0 100.0 1.0
#pragma parameter g_hue_degrees "CRT Hue" 0.0 -180.0 180.0 1.0
#pragma parameter g_U_SHIFT "CRT U Shift" 0.0 -0.2 0.2 0.01
#pragma parameter g_V_SHIFT "CRT V Shift" 0.0 -0.2 0.2 0.01
#pragma parameter g_U_MUL "CRT U Multiplier" 1.0 0.0 2.0 0.01
#pragma parameter g_V_MUL "CRT V Multiplier" 1.0 0.0 2.0 0.01
#pragma parameter g_CRT_br "CRT Beam Red" 1.0 0.0 1.2 0.01
#pragma parameter g_CRT_bg "CRT Beam Green" 1.0 0.0 1.2 0.01
#pragma parameter g_CRT_bb "CRT Beam Blue" 1.0 0.0 1.2 0.01
#pragma parameter g_CRT_rf "CRT Lambert Refl. in %" 5.0 2.0 5.0 0.1
#pragma parameter g_CRT_sl "Surround Luminance -nits-" 34.0 0.0 100.0 1.0
#pragma parameter g_vignette "Vignette Toggle" 1.0 0.0 1.0 1.0
#pragma parameter g_vstr "Vignette Strength" 50.0 0.0 50.0 1.0
#pragma parameter g_vpower "Vignette Power" 0.50 0.0 0.5 0.01
// Sega related fixes
#pragma parameter g_sfixes "// SEGA FIXES //" 0.0 0.0 1.0 1.0
#pragma parameter g_lum_fix "Sega Luma Fix" 0.0 0.0 1.0 1.0
#pragma parameter g_MD_Pal "MD Palette" 0.0 0.0 1.0 1.0
#pragma parameter g_SMS_bl "SMS Blue Lift" 0.0 0.0 1.0 1.0
// Digital controls
#pragma parameter g_digital "// DIGITAL CONTROLS //" 0.0 0.0 1.0 1.0
#pragma parameter g_lum "Brightness" 0.0 -0.5 1.0 0.01
#pragma parameter g_cntrst "Contrast" 0.0 -1.0 1.0 0.05
#pragma parameter g_mid "Contrast Pivot" 0.5 0.0 1.0 0.01
#pragma parameter g_sat "Saturation" 0.0 -1.0 1.0 0.01
#pragma parameter g_vibr "Dullness/Vibrance" 0.0 -1.0 1.0 0.05
#pragma parameter g_satr "Hue vs Sat Red" 0.0 -1.0 1.0 0.01
#pragma parameter g_satg "Hue vs Sat Green" 0.0 -1.0 1.0 0.01
#pragma parameter g_satb "Hue vs Sat Blue" 0.0 -1.0 1.0 0.01
#pragma parameter g_lift "Black Level" 0.0 -15.0 15.0 1.0
#pragma parameter blr "Black-Red Tint" 0.0 0.0 1.0 0.01
#pragma parameter blg "Black-Green Tint" 0.0 0.0 1.0 0.01
#pragma parameter blb "Black-Blue Tint" 0.0 0.0 1.0 0.01
#pragma parameter wlr "White-Red Tint" 1.0 0.0 2.0 0.01
#pragma parameter wlg "White-Green Tint" 1.0 0.0 2.0 0.01
#pragma parameter wlb "White-Blue Tint" 1.0 0.0 2.0 0.01
#pragma parameter rg "Red-Green Tint" 0.0 -1.0 1.0 0.005
#pragma parameter rb "Red-Blue Tint" 0.0 -1.0 1.0 0.005
#pragma parameter gr "Green-Red Tint" 0.0 -1.0 1.0 0.005
#pragma parameter gb "Green-Blue Tint" 0.0 -1.0 1.0 0.005
#pragma parameter br "Blue-Red Tint" 0.0 -1.0 1.0 0.005
#pragma parameter bg "Blue-Green Tint" 0.0 -1.0 1.0 0.005
#pragma parameter LUT_Size1 "LUT Size 1" 32.0 8.0 64.0 16.0
#pragma parameter LUT1_toggle "LUT 1 Toggle" 0.0 0.0 1.0 1.0
#pragma parameter LUT_Size2 "LUT Size 2" 64.0 0.0 64.0 16.0
#pragma parameter LUT2_toggle "LUT 2 Toggle" 0.0 0.0 1.0 1.0
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
uniform sampler2D SamplerLUT1;
uniform sampler2D SamplerLUT2;
COMPAT_VARYING vec4 TEX0;
// fragment compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float g_signal_type, g_crtgamut, g_space_out, g_Dark_to_Dim, g_GCompress, g_hue_degrees, g_U_SHIFT, g_V_SHIFT, g_U_MUL, g_V_MUL, g_CRT_l, g_CRT_b, g_CRT_c, g_CRT_br, g_CRT_bg, g_CRT_bb, g_CRT_rf, g_CRT_sl, g_vignette, g_vstr, g_vpower, g_lum_fix, g_MD_Pal, g_SMS_bl, g_lum, g_cntrst, g_mid, wp_temperature, g_sat, g_vibr, g_satr, g_satg, g_satb, g_lift, blr, blg, blb, wlr, wlg, wlb, rg, rb, gr, gb, br, bg, LUT_Size1, LUT1_toggle, LUT_Size2, LUT2_toggle;
#else
#define g_signal_type 1.0
#define g_crtgamut 2.0
#define g_space_out 0.0
#define g_Dark_to_Dim 1.0
#define g_GCompress 1.0
#define g_hue_degrees 0.0
#define g_U_SHIFT 0.0
#define g_V_SHIFT 0.0
#define g_U_MUL 1.0
#define g_V_MUL 1.0
#define g_CRT_l 2.50
#define g_CRT_b 50.0
#define g_CRT_c 50.0
#define g_CRT_br 1.0
#define g_CRT_bg 1.0
#define g_CRT_bb 1.0
#define g_CRT_rf 5.0
#define g_CRT_sl 34.0
#define g_vignette 1.0
#define g_vstr 50.0
#define g_vpower 0.50
#define g_lum_fix 0.0
#define g_MD_Pal 0.0
#define g_SMS_bl 0.0
#define g_lum 0.0
#define g_cntrst 0.0
#define g_mid 0.5
#define wp_temperature 8504.0
#define g_sat 0.0
#define g_vibr 0.0
#define g_satr 0.0
#define g_satg 0.0
#define g_satb 0.0
#define g_lift 0.0
#define blr 0.0
#define blg 0.0
#define blb 0.0
#define wlr 1.0
#define wlg 1.0
#define wlb 1.0
#define rg 0.0
#define rb 0.0
#define gr 0.0
#define gb 0.0
#define br 0.0
#define bg 0.0
#define LUT_Size1 32.0
#define LUT1_toggle 0.0
#define LUT_Size2 64.0
#define LUT2_toggle 0.0
#endif
// lift goes from -15 to 15 points on an 8-bit scale (0-255)
#define lift (g_space_out==0.0 ? moncurve_f(abs(g_lift)/255.0,2.4,0.055) : pow(abs(g_lift)/255.0,g_space_out==3.0?2.199:2.4)) * sign(g_lift)
#define M_PI 3.1415926535897932384626433832795/180.0 // 1º (one degree) in radians
#define RW vec3(0.950457397565471, 1.0, 1.089436035930324) // D65 Reference White
#define g_bl -(100000.*log((72981.-500000./(3.*max(2.3,g_CRT_l)))/9058.))/945461.
///////////////////////// Color Space Transformations //////////////////////////
// 'D65' based
mat3 RGB_to_XYZ_mat(mat3 primaries) {
vec3 T = RW * inverse(primaries);
mat3 TB = mat3(
T.x, 0.0, 0.0,
0.0, T.y, 0.0,
0.0, 0.0, T.z);
return TB * primaries;
}
vec3 RGB_to_XYZ(vec3 RGB, mat3 primaries) {
return RGB * RGB_to_XYZ_mat(primaries);
}
vec3 XYZ_to_RGB(vec3 XYZ, mat3 primaries) {
return XYZ * inverse(RGB_to_XYZ_mat(primaries));
}
vec3 XYZtoYxy(vec3 XYZ) {
float XYZrgb = XYZ.r+XYZ.g+XYZ.b;
float Yxyg = (XYZrgb <= 0.0) ? 0.3805 : XYZ.r / XYZrgb;
float Yxyb = (XYZrgb <= 0.0) ? 0.3769 : XYZ.g / XYZrgb;
return vec3(XYZ.g, Yxyg, Yxyb);
}
vec3 YxytoXYZ(vec3 Yxy) {
float Xs = Yxy.r * (Yxy.g/Yxy.b);
float Xsz = (Yxy.r <= 0.0) ? 0.0 : 1.0;
vec3 XYZ = vec3(Xsz,Xsz,Xsz) * vec3(Xs, Yxy.r, (Xs/Yxy.g)-Xs-Yxy.r);
return XYZ;
}
///////////////////////// White Point Mapping /////////////////////////
//
//
// PAL: D65 NTSC-U: D65 NTSC-J: CCT 9300K+27MPCD
// PAL: 6503.512K NTSC-U: 6503.512K NTSC-J: ~8945.436K
// [x:0.31266142 y:0.3289589] [x:0.281 y:0.311]
// For NTSC-J there's not a common agreed value, measured consumer units span from 8229.87K to 8945.623K with accounts for 8800K as well.
// Recently it's been standardized to 9300K which is closer to what master monitors (and not consumer units) were (x=0.2838 y=0.2984) (~9177.98K)
// "RGB to XYZ -> Temperature -> XYZ to RGB" joint matrix
vec3 wp_adjust(vec3 RGB, float temperature, mat3 primaries, mat3 display) {
float temp3 = 1000. / temperature;
float temp6 = 1000000. / pow(temperature, 2.0);
float temp9 = 1000000000. / pow(temperature, 3.0);
vec3 wp = vec3(1.0);
wp.x = (temperature < 5500.) ? 0.244058 + 0.0989971 * temp3 + 2.96545 * temp6 - 4.59673 * temp9 : \
(temperature < 8000.) ? 0.200033 + 0.9545630 * temp3 - 2.53169 * temp6 + 7.08578 * temp9 : \
0.237045 + 0.2437440 * temp3 + 1.94062 * temp6 - 2.11004 * temp9 ;
wp.y = -0.275275 + 2.87396 * wp.x - 3.02034 * pow(wp.x,2.0) + 0.0297408 * pow(wp.x,3.0);
wp.z = 1.0 - wp.x - wp.y;
const mat3 CAT16 = mat3(
0.401288,-0.250268, -0.002079,
0.650173, 1.204414, 0.048952,
-0.051461, 0.045854, 0.953127);
vec3 VKV = (vec3(wp.x/wp.y,1.,wp.z/wp.y) * CAT16) / (RW * CAT16);
mat3 VK = mat3(
VKV.x, 0.0, 0.0,
0.0, VKV.y, 0.0,
0.0, 0.0, VKV.z);
mat3 CAM = CAT16 * (VK * inverse(CAT16));
mat3 mata = RGB_to_XYZ_mat(primaries);
mat3 matb = RGB_to_XYZ_mat(display);
return RGB.rgb * ((mata * CAM) * inverse(matb));
}
////////////////////////////////////////////////////////////////////////////////
// CRT EOTF Function
//----------------------------------------------------------------------
float EOTF_1886a(float color, float bl, float brightness, float contrast) {
// Defaults:
// Black Level = 0.1
// Brightness = 0
// Contrast = 100
const float wl = 100.0;
float b = pow(bl, 1./2.4);
float a = pow(wl, 1./2.4)-b;
b = (brightness-50.) / 250. + b/a; // -0.20 to +0.20
a = contrast!=50. ? pow(2.,(contrast-50.)/50.) : 1.; // 0.50 to +2.00
const float Vc = 0.35; // Offset
float Lw = wl/100. * a; // White level
float Lb = min( b * a,Vc); // Black level
const float a1 = 2.6; // Shoulder gamma
const float a2 = 3.0; // Knee gamma
float k = Lw /pow(1. + Lb, a1);
float sl = k * pow(Vc + Lb, a1-a2); // Slope for knee gamma
color = color >= Vc ? k * pow(color + Lb, a1 ) : sl * pow(color + Lb, a2 );
// Black lift compensation
float bc = 0.00446395*pow(bl,1.23486);
color = min(max(color-bc,0.0)*(1.0/(1.0-bc)), 1.0); // Undo Lift
color = pow(color,1.0-0.00843283*pow(bl,1.22744)); // Restore Gamma from 'Undo Lift'
return color;
}
vec3 EOTF_1886a_f3( vec3 color, float BlackLevel, float brightness, float contrast) {
color.r = EOTF_1886a( color.r, BlackLevel, brightness, contrast);
color.g = EOTF_1886a( color.g, BlackLevel, brightness, contrast);
color.b = EOTF_1886a( color.b, BlackLevel, brightness, contrast);
return color.rgb;
}
// Monitor Curve Functions: https://github.com/ampas/aces-dev
//----------------------------------------------------------------------
float moncurve_f( float color, float gamma, float offs) {
// Forward monitor curve
color = clamp(color, 0.0, 1.0);
float fs = (( gamma - 1.0) / offs) * pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma);
float xb = offs / ( gamma - 1.0);
color = ( color > xb) ? pow( ( color + offs) / ( 1.0 + offs), gamma) : color * fs;
return color;
}
vec3 moncurve_f_f3( vec3 color, float gamma, float offs) {
color.r = moncurve_f( color.r, gamma, offs);
color.g = moncurve_f( color.g, gamma, offs);
color.b = moncurve_f( color.b, gamma, offs);
return color.rgb;
}
float moncurve_r( float color, float gamma, float offs) {
// Reverse monitor curve
color = clamp(color, 0.0, 1.0);
float yb = pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma);
float rs = pow( ( gamma - 1.0) / offs, gamma - 1.0) * pow( ( 1.0 + offs) / gamma, gamma);
color = ( color > yb) ? ( 1.0 + offs) * pow( color, 1.0 / gamma) - offs : color * rs;
return color;
}
vec3 moncurve_r_f3( vec3 color, float gamma, float offs) {
color.r = moncurve_r( color.r, gamma, offs);
color.g = moncurve_r( color.g, gamma, offs);
color.b = moncurve_r( color.b, gamma, offs);
return color.rgb;
}
//-------------------------- Luma Functions ----------------------------
// Performs better in gamma encoded space
float contrast_sigmoid(float color, float cont, float pivot) {
cont = pow(cont + 1., 3.);
float knee = 1. / (1. + exp(cont * pivot));
float shldr = 1. / (1. + exp(cont * (pivot - 1.)));
color =(1. / (1. + exp(cont * (pivot - color))) - knee) / (shldr - knee);
return color;
}
// Performs better in gamma encoded space
float contrast_sigmoid_inv(float color, float cont, float pivot) {
cont = pow(cont - 1., 3.);
float knee = 1. / (1. + exp (cont * pivot));
float shldr = 1. / (1. + exp (cont * (pivot - 1.)));
color = pivot - log(1. / (color * (shldr - knee) + knee) - 1.) / cont;
return color;
}
float rolled_gain(float color, float gain) {
float gx = abs(gain) + 0.001;
float anch = (gain > 0.0) ? 0.5 / (gx / 2.0) : 0.5 / gx;
color = (gain > 0.0) ? color * ((color - anch) / (1.0 - anch)) : color * ((1.0 - anch) / (color - anch)) * (1.0 - gain);
return color;
}
vec3 rolled_gain_v3(vec3 color, float gain) {
color.r = rolled_gain(color.r, gain);
color.g = rolled_gain(color.g, gain);
color.b = rolled_gain(color.b, gain);
return color.rgb;
}
float SatMask(float color_r, float color_g, float color_b) {
float max_rgb = max(color_r, max(color_g, color_b));
float min_rgb = min(color_r, min(color_g, color_b));
float msk = clamp((max_rgb - min_rgb) / (max_rgb + min_rgb), 0.0, 1.0);
return msk;
}
// This shouldn't be necessary but it seems some undefined values can
// creep in and each GPU vendor handles that differently. This keeps
// all values within a safe range
vec3 mixfix(vec3 a, vec3 b, float c) {
return (a.z < 1.0) ? mix(a, b, c) : a;
}
vec4 mixfix_v4(vec4 a, vec4 b, float c) {
return (a.z < 1.0) ? mix(a, b, c) : a;
}
//---------------------- Gamut Compression -------------------
// RGB 'Desaturate' Gamut Compression (by Jed Smith: https://github.com/jedypod/gamut-compress)
vec3 GamutCompression (vec3 rgb, float grey) {
// Limit/Thres order is Cyan, Magenta, Yellow
vec3 beam = max(vec3(0.0),vec3(g_CRT_bg,(g_CRT_bb+g_CRT_br)/2.,(g_CRT_br+g_CRT_bg)/2.));
vec3 sat = max(vec3(0.0),vec3(g_satg, (g_satb +g_satr )/2.,(g_satr +g_satg) /2.)+1.); // center at 1
float temp = max(0.0,abs(wp_temperature-7000.)-1000.)/825.+1.; // center at 1
vec3 WPD = wp_temperature < 7000. ? vec3(1.,temp,(temp-1.)/2.+1.) : vec3((temp-1.)/2.+1.,temp,1.);
sat = max(0.0,g_sat+1.0)*(sat*beam) * WPD;
mat2x3 LimThres = mat2x3( 0.100000,0.100000,0.100000,
0.125000,0.125000,0.125000);
if (g_space_out < 1.0) {
LimThres = \
g_crtgamut == 3.0 ? mat2x3( 0.000000,0.044065,0.000000,
0.000000,0.095638,0.000000) : \
g_crtgamut == 2.0 ? mat2x3( 0.006910,0.092133,0.000000,
0.039836,0.121390,0.000000) : \
g_crtgamut == 1.0 ? mat2x3( 0.018083,0.059489,0.017911,
0.066570,0.105996,0.066276) : \
g_crtgamut ==-1.0 ? mat2x3( 0.014947,0.098571,0.017911,
0.060803,0.123793,0.066276) : \
g_crtgamut ==-2.0 ? mat2x3( 0.004073,0.030307,0.012697,
0.028222,0.083075,0.056029) : \
g_crtgamut ==-3.0 ? mat2x3( 0.018424,0.053469,0.016841,
0.067146,0.102294,0.064393) : LimThres;
} else if (g_space_out==1.0) {
LimThres = \
g_crtgamut == 3.0 ? mat2x3( 0.000000,0.234229,0.007680,
0.000000,0.154983,0.042446) : \
g_crtgamut == 2.0 ? mat2x3( 0.078526,0.108432,0.006143,
0.115731,0.127194,0.037039) : \
g_crtgamut == 1.0 ? mat2x3( 0.021531,0.237184,0.013466,
0.072018,0.155438,0.057731) : \
g_crtgamut ==-1.0 ? mat2x3( 0.051640,0.103332,0.013550,
0.101092,0.125474,0.057912) : \
g_crtgamut ==-2.0 ? mat2x3( 0.032717,0.525361,0.023928,
0.085609,0.184491,0.075381) : \
g_crtgamut ==-3.0 ? mat2x3( 0.000000,0.377522,0.043076,
0.000000,0.172390,0.094873) : LimThres;
}
// Amount of outer gamut to affect
vec3 th = 1.0-vec3(LimThres[1])*(0.4*sat+0.3);
// Distance limit: How far beyond the gamut boundary to compress
vec3 dl = 1.0+vec3(LimThres[0])*sat;
// Calculate scale so compression function passes through distance limit: (x=dl, y=1)
vec3 s = (vec3(1.0)-th)/sqrt(max(vec3(1.001), dl)-1.0);
// Achromatic axis
float ac = max(rgb.x, max(rgb.y, rgb.z));
// Inverse RGB Ratios: distance from achromatic axis
vec3 d = ac==0.0?vec3(0.0):(ac-rgb)/abs(ac);
// Compressed distance. Parabolic compression function: https://www.desmos.com/calculator/nvhp63hmtj
vec3 cd;
vec3 sf = s*sqrt(d-th+s*s/4.0)-s*sqrt(s*s/4.0)+th;
cd.x = (d.x < th.x) ? d.x : sf.x;
cd.y = (d.y < th.y) ? d.y : sf.y;
cd.z = (d.z < th.z) ? d.z : sf.z;
// Inverse RGB Ratios to RGB
// and Mask with "luma"
return mix(rgb, ac-cd.xyz*abs(ac), pow(grey,1.0/g_CRT_l));
}
//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
// Matrices in column-major
//----------------------- Y'UV color model -----------------------
// 0-235 YUV PAL
// 0-235 YUV NTSC-J
// 16-235 YUV NTSC
// Bymax 0.885515
// Rymax 0.701088
// R'G'B' full range to Decorrelated Intermediate (Y,B-Y,R-Y)
// Rows should sum to 0, except first one which sums 1
const mat3 YByRy = mat3(
0.298912, 0.586603, 0.114485,
-0.298912,-0.586603, 0.885515,
0.701088,-0.586603,-0.114485);
// Umax 0.435812284313725
// Vmax 0.615857694117647
// R'G'B' full to Y'UV limited
// YUV is defined with headroom and footroom (TV range),
// UV excursion is limited to Umax and Vmax
// Y excursion is limited to 16-235 for NTSC-U and 0-235 for PAL and NTSC-J
vec3 r601_YUV(vec3 RGB, float NTSC_U) {
const float sclU = ((0.5*(235.-16.)+16.)/255.); // This yields Luma grey at around 0.49216 or 125.5 in 8-bit
const float sclV = (240.-16.) /255. ; // This yields Chroma range at around 0.87843 or 224 in 8-bit
mat3 conv_mat = mat3(
vec3(YByRy[0]),
vec3(sclU) * vec3(YByRy[1]),
vec3(sclV) * vec3(YByRy[2]));
// -0.147111592156863 -0.288700692156863 0.435812284313725
// 0.615857694117647 -0.515290478431373 -0.100567215686275
vec3 YUV = RGB.rgb * conv_mat;
YUV.x = NTSC_U==1.0 ? YUV.x * 219.0 + 16.0 : YUV.x * 235.0;
return vec3(YUV.x/255.0,YUV.yz);
}
// Y'UV limited to R'G'B' full
vec3 YUV_r601(vec3 YUV, float NTSC_U) {
const mat3 conv_mat = mat3(
1.0000000, -0.000000029378826483, 1.1383928060531616,
1.0000000, -0.396552562713623050, -0.5800843834877014,
1.0000000, 2.031872510910034000, 0.0000000000000000);
YUV.x = (YUV.x - (NTSC_U == 1.0 ? 16.0/255.0 : 0.0 )) * (255.0/(NTSC_U == 1.0 ? 219.0 : 235.0));
return YUV.xyz * conv_mat;
}
// FP32 to 8-bit mid-tread uniform quantization
float Quantize8(float col) {
col = min(255.0,floor(col * 255.0 + 0.5));
return col;
}
vec3 Quantize8_f3(vec3 col) {
col.r = Quantize8(col.r);
col.g = Quantize8(col.g);
col.b = Quantize8(col.b);
return col.rgb;
}
//------------------------- LMS --------------------------
// Hunt-Pointer-Estevez D65 cone response
// modification for IPT model
const mat3 LMS = mat3(
0.4002, 0.7075, -0.0807,
-0.2280, 1.1500, 0.0612,
0.0000, 0.0000, 0.9184);
const mat3 IPT = mat3(
0.4000, 0.4000, 0.2000,
4.4550, -4.8510, 0.3960,
0.8056, 0.3572, -1.1628);
//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
//----------------------- Phosphor Gamuts -----------------------
////// STANDARDS ///////
// SMPTE RP 145-1994 (SMPTE-C), 170M-1999
// SMPTE-C - Standard Phosphor (Rec.601 NTSC)
// Standardized in 1982 (as CCIR Rec.601-1) after "Conrac Corp. & RCA" P22 phosphors (circa 1969) for consumer CRTs
// ILLUMINANT: D65->[0.31266142,0.3289589]
const mat3 SMPTE170M_ph = mat3(
0.630, 0.310, 0.155,
0.340, 0.595, 0.070,
0.030, 0.095, 0.775);
// ITU-R BT.470/601 (B/G)
// EBU Tech.3213 PAL - Standard Phosphor for Studio Monitors (also used in Sony BVMs and Higher-end PVMs)
// ILLUMINANT: D65->[0.31266142,0.3289589]
const mat3 SMPTE470BG_ph = mat3(
0.640, 0.290, 0.150,
0.330, 0.600, 0.060,
0.030, 0.110, 0.790);
// NTSC-J P22
// Mix between averaging KV-20M20, KDS VS19, Dell D93, 4-TR-B09v1_0.pdf and Phosphor Handbook 'P22'
// Phosphors based on 1975's EBU Tech.3123-E (formerly known as JEDEC-P22)
// Typical P22 phosphors used in Japanese consumer CRTs with 9300K+27MPCD white point
// ILLUMINANT: D93->[0.281000,0.311000] (CCT of 8945.436K)
// ILLUMINANT: D97->[0.285000,0.285000] (CCT of 9696K) for Nanao MS-2930s series (around 10000.0K for wp_adjust() daylight fit)
const mat3 P22_J_ph = mat3(
0.625, 0.280, 0.152,
0.350, 0.605, 0.062,
0.025, 0.115, 0.786);
////// P22 ///////
// You can run any of these P22 primaries either through D65 or D93 indistinctly but typically these were D65 based.
// P22_80 is roughly the same as the old P22 gamut in Grade 2020. P22 1979-1994 meta measurement.
// ILLUMINANT: D65->[0.31266142,0.3289589]
const mat3 P22_80s_ph = mat3(
0.6470, 0.2820, 0.1472,
0.3430, 0.6200, 0.0642,
0.0100, 0.0980, 0.7886);
// P22 improved with tinted phosphors (Use this for NTSC-U 16-bits, and above for 8-bits)
const mat3 P22_90s_ph = mat3(
0.6661, 0.3134, 0.1472,
0.3329, 0.6310, 0.0642,
0.0010, 0.0556, 0.7886);
// RPTV (Rear Projection TV) for NTSC-U late 90s, early 00s
const mat3 RPTV_95s_ph = mat3(
0.640, 0.341, 0.150,
0.335, 0.586, 0.070,
0.025, 0.073, 0.780);
//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
//----------------------- Display Primaries -----------------------
// sRGB (IEC 61966-2-1) and ITU-R BT.709-6 (originally CCIR Rec.709)
const mat3 sRGB_prims = mat3(
0.640, 0.300, 0.150,
0.330, 0.600, 0.060,
0.030, 0.100, 0.790);
// Adobe RGB (1998)
const mat3 Adobe_prims = mat3(
0.640, 0.210, 0.150,
0.330, 0.710, 0.060,
0.030, 0.080, 0.790);
// BT-2020/BT-2100 (from 630nm, 532nm and 467nm)
const mat3 rec2020_prims = mat3(
0.707917792, 0.170237195, 0.131370635,
0.292027109, 0.796518542, 0.045875976,
0.000055099, 0.033244263, 0.822753389);
// SMPTE RP 432-2 (DCI-P3)
const mat3 DCIP3_prims = mat3(
0.680, 0.265, 0.150,
0.320, 0.690, 0.060,
0.000, 0.045, 0.790);
//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
void main()
{
// Retro Sega Systems: Genesis, 32x, CD and Saturn 2D had color palettes designed in TV levels to save on transformations.
float lum_exp = (g_lum_fix == 1.0) ? (255.0/239.0) : 1.0;
vec3 src = COMPAT_TEXTURE(Source, vTexCoord).rgb * lum_exp;
// Adding Sega Master System 1 non-linear blue "lift": https://github.com/ekeeke/Genesis-Plus-GX/issues/345#issuecomment-820885780
src = g_SMS_bl > 0.0 ? pow(src, vec3(1.0,1.0,1.0/1.16)) : src;
// Reproduce the Sega MegaDrive palette (same as the BlastEm core output so don't use for this core): https://github.com/ekeeke/Genesis-Plus-GX/issues/345
src = g_MD_Pal > 0.0 ? vec3(contrast_sigmoid_inv(src.r,2.578419881,0.520674), \
contrast_sigmoid_inv(src.g,2.578419881,0.520674), \
contrast_sigmoid_inv(src.b,2.578419881,0.520674)) : src;
// Clipping Logic / Gamut Limiting
bool NTSC_U = g_crtgamut < 2.0;
vec2 UVmax = vec2(Quantize8(0.435812284313725), Quantize8(0.615857694117647));
vec2 Ymax = NTSC_U ? vec2(16.0, 235.0) : vec2(0.0, 235.0);
// Assumes framebuffer in Rec.601 full range with baked gamma
// Quantize to 8-bit to replicate CRT's circuit board arithmetics
vec3 col = clamp(Quantize8_f3(r601_YUV(src, NTSC_U ? 1.0 : 0.0)), vec3(Ymax.x, -UVmax.x, -UVmax.y), \
vec3(Ymax.y, UVmax.x, UVmax.y))/255.0;
// YUV Analogue Color Controls (HUE + Color Shift + Color Burst)
float hue_radians = g_hue_degrees * M_PI;
float hue = hue_radians + (col.z==0.0 && col.y==0.0 ? 0.0 : atan(col.z, col.y));
float chroma = sqrt(col.z * col.z + col.y * col.y); // Euclidean Distance
col.y = (mod((chroma * cos(hue) + 1.0) + g_U_SHIFT, 2.0) - 1.0) * g_U_MUL;
col.z = (mod((chroma * sin(hue) + 1.0) + g_V_SHIFT, 2.0) - 1.0) * g_V_MUL;
// Back to R'G'B' full
col = g_signal_type > 0.0 ? max(Quantize8_f3(YUV_r601(col.xyz, NTSC_U ? 1.0 : 0.0))/255.0, 0.0) : src;
// Look LUT - (in SPC space)
float red = (col.r * (LUT_Size1 - 1.0) + 0.4999) / (LUT_Size1 * LUT_Size1);
float green = (col.g * (LUT_Size1 - 1.0) + 0.4999) / LUT_Size1;
float blue1 = (floor(col.b * (LUT_Size1 - 1.0)) / LUT_Size1) + red;
float blue2 = (ceil(col.b * (LUT_Size1 - 1.0)) / LUT_Size1) + red;
float mixer = clamp(max((col.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0);
vec3 color1 = COMPAT_TEXTURE(SamplerLUT1, vec2(blue1, green)).rgb;
vec3 color2 = COMPAT_TEXTURE(SamplerLUT1, vec2(blue2, green)).rgb;
vec3 vcolor = (LUT1_toggle == 0.0) ? col : mixfix(color1, color2, mixer);
// CRT EOTF. To Display Referred Linear: Undo developer baked CRT gamma (from 2.40 at default 0.1 CRT black level, to 2.60 at 0.0 CRT black level)
col = EOTF_1886a_f3(vcolor, g_bl, g_CRT_b, g_CRT_c);
//_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
// \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \
// HUE vs HUE
vec4 screen = vec4(max(col, 0.0), 1.0);
// r g b alpha ; alpha does nothing for our purposes
mat4 color = mat4(wlr, rg, rb, 0.0, //red tint
gr, wlg, gb, 0.0, //green tint
br, bg, wlb, 0.0, //blue tint
blr/20., blg/20., blb/20., 0.0); //black tint
screen *= transpose(color);
// CRT Phosphor Gamut (0.0 is sRGB/noop)
mat3 m_in;
if (g_crtgamut == -3.0) { m_in = SMPTE170M_ph; } else
if (g_crtgamut == -2.0) { m_in = RPTV_95s_ph; } else
if (g_crtgamut == -1.0) { m_in = P22_80s_ph; } else
if (g_crtgamut == 1.0) { m_in = P22_90s_ph; } else
if (g_crtgamut == 2.0) { m_in = P22_J_ph; } else
if (g_crtgamut == 3.0) { m_in = SMPTE470BG_ph; } else
{ m_in = sRGB_prims; }
m_in = (LUT1_toggle == 0.0) ? m_in : sRGB_prims;
// Display color space
mat3 m_ou;
if (g_space_out == 1.0) { m_ou = DCIP3_prims; } else
if (g_space_out == 2.0) { m_ou = rec2020_prims; } else
if (g_space_out == 3.0) { m_ou = Adobe_prims; } else
{ m_ou = sRGB_prims; }
// White Point Mapping
col = (g_signal_type==0.0) && (m_in==m_ou) && (6499. < wp_temperature) && (wp_temperature < 6505.) ? screen.rgb : \
wp_adjust(screen.rgb, wp_temperature, m_in, m_ou);
// SAT + HUE vs SAT (in IPT space)
vec3 coeff = RGB_to_XYZ_mat(m_in)[1];
vec3 src_h = RGB_to_XYZ(screen.rgb, m_in) * LMS;
src_h.x = src_h.x >= 0.0 ? pow(src_h.x, 0.43) : -pow(-src_h.x, 0.43);
src_h.y = src_h.y >= 0.0 ? pow(src_h.y, 0.43) : -pow(-src_h.y, 0.43);
src_h.z = src_h.z >= 0.0 ? pow(src_h.z, 0.43) : -pow(-src_h.z, 0.43);
src_h.xyz *= IPT;
float hue_at = atan(src_h.z, src_h.y);
chroma = sqrt(src_h.z * src_h.z + src_h.y * src_h.y);
// red 320º green 220º blue 100º
float hue_radians_r = 320.0 * M_PI;
float hue_r = cos(hue_at + hue_radians_r);
float hue_radians_g = 220.0 * M_PI;
float hue_g = cos(hue_at + hue_radians_g);
float hue_radians_b = 100.0 * M_PI;
float hue_b = cos(hue_at + hue_radians_b);
float msk = dot(clamp(vec3(hue_r, hue_g, hue_b) * chroma * 2.0, 0.0, 1.0), -vec3(g_satr, g_satg, g_satb));
src_h = mix(col, vec3(dot(coeff, col)), msk);
float sat_msk = (g_vibr < 0.0) ? 1.0 - abs(SatMask(src_h.x, src_h.y, src_h.z) - 1.0) * abs(g_vibr) : \
1.0 - (SatMask(src_h.x, src_h.y, src_h.z) * g_vibr) ;
float sat = g_sat + 1.0;
float msat = 1.0 - sat;
float msatx = msat * coeff.x;
float msaty = msat * coeff.y;
float msatz = msat * coeff.z;
mat3 adjust = mat3(msatx + sat, msatx , msatx ,
msaty , msaty + sat, msaty ,
msatz , msatz , msatz + sat);
src_h = mix(src_h, adjust * src_h, clamp(sat_msk, 0.0, 1.0));
src_h *= vec3(g_CRT_br,g_CRT_bg,g_CRT_bb);
// RGB 'Desaturate' Gamut Compression (by Jed Smith: https://github.com/jedypod/gamut-compress)
coeff = RGB_to_XYZ_mat(m_ou)[1];
src_h = g_GCompress==1.0 ? clamp(GamutCompression(src_h, dot(coeff.xyz, src_h)), 0.0, 1.0) : clamp(src_h, 0.0, 1.0);
// Sigmoidal Luma Contrast under 'Yxy' decorrelated model (in gamma space)
vec3 Yxy = XYZtoYxy(RGB_to_XYZ(src_h, m_ou));
float toGamma = clamp(moncurve_r(Yxy.r, 2.40, 0.055), 0.0, 1.0);
toGamma = (Yxy.r > 0.5) ? contrast_sigmoid_inv(toGamma, 2.3, 0.5) : toGamma;
float sigmoid = (g_cntrst > 0.0) ? contrast_sigmoid(toGamma, g_cntrst, g_mid) : contrast_sigmoid_inv(toGamma, g_cntrst, g_mid);
vec3 contrast = vec3(moncurve_f(sigmoid, 2.40, 0.055), Yxy.g, Yxy.b);
vec3 XYZsrgb = XYZ_to_RGB(YxytoXYZ(contrast), m_ou);
contrast = (g_cntrst == 0.0) ? src_h : XYZsrgb;
// Lift + Gain -PP Digital Controls- (Could do in Yxy but performance reasons)
src_h = clamp(rolled_gain_v3(contrast, clamp(g_lum, -0.49, 0.99)), 0.0, 1.0);
src_h += lift * (1.0 - contrast);
// Vignetting (in linear space, so after EOTF^-1 it's power shaped; 0.5 thres converts to ~0.75)
vec2 vpos = vTexCoord*(TextureSize.xy/InputSize.xy);
vpos *= 1.0 - vpos.xy;
float vig = vpos.x * vpos.y * g_vstr;
vig = min(pow(vig, g_vpower), 1.0);
vig = vig >= 0.5 ? smoothstep(0.0,1.0,vig) : vig;
src_h *= (g_vignette == 1.0) ? vig : 1.0;
// Dark to Dim adaptation OOTF; for 709, P3-D65 and 2020
float DtD = g_Dark_to_Dim > 0.0 ? 1.0/0.9811 : 1.0;
// EOTF^-1 - Inverted Electro-Optical Transfer Function
vec3 TRC = (g_space_out == 3.0) ? clamp(pow(src_h, vec3(1./ (563./256.))), 0., 1.) : \
(g_space_out == 0.0) ? moncurve_r_f3(src_h, 2.20 + 0.20, 0.0550) : \
clamp(pow( src_h, vec3(1./((2.20 + 0.20)*DtD))), 0., 1.) ;
// External Flare for Surround Illuminant 2700K (Soft White) at F0 (Lambertian reflectance); defines offset thus also black lift
vec3 Flare = 0.01 * (g_CRT_rf/5.0)*(0.049433*g_CRT_sl - 0.188367) * vec3(0.459993/0.410702,1.0,0.129305/0.410702);
TRC = g_CRT_sl > 0.0 ? min(TRC+Flare,1.0) : TRC;
// Technical LUT - (in SPC space)
float red_2 = (TRC.r * (LUT_Size2 - 1.0) + 0.4999) / (LUT_Size2 * LUT_Size2);
float green_2 = (TRC.g * (LUT_Size2 - 1.0) + 0.4999) / LUT_Size2;
float blue1_2 = (floor(TRC.b * (LUT_Size2 - 1.0)) / LUT_Size2) + red_2;
float blue2_2 = (ceil(TRC.b * (LUT_Size2 - 1.0)) / LUT_Size2) + red_2;
float mixer_2 = clamp(max((TRC.b - blue1_2) / (blue2_2 - blue1_2), 0.0), 0.0, 32.0);
vec3 color1_2 = COMPAT_TEXTURE(SamplerLUT2, vec2(blue1_2, green_2)).rgb;
vec3 color2_2 = COMPAT_TEXTURE(SamplerLUT2, vec2(blue2_2, green_2)).rgb;
vec3 LUT2_output = mixfix(color1_2, color2_2, mixer_2);
LUT2_output = (LUT2_toggle == 0.0) ? TRC : LUT2_output;
FragColor = vec4(LUT2_output, 1.0);
}
#endif