1//_____________________________/\_______________________________
2//==============================================================
3//
4//
5//      [CRTS] PUBLIC DOMAIN CRT-STYLED SCALAR - 20180120b
6//
7//                      by Timothy Lottes
8//             https://www.shadertoy.com/view/MtSfRK
9//               adapted for RetroArch by hunterk
10//
11//
12//==============================================================
13////////////////////////////////////////////////////////////////
14////////////////////////////////////////////////////////////////
15////////////////////////////////////////////////////////////////
16//_____________________________/\_______________________________
17//==============================================================
18//
19//                         WHAT'S NEW
20//
21//--------------------------------------------------------------
22// Evolution of prior shadertoy example
23//--------------------------------------------------------------
24// This one is semi-optimized
25//  - Less texture fetches
26//  - Didn't get to instruction level optimization
27//  - Could likely use texture fetch to generate phosphor mask
28//--------------------------------------------------------------
29// Added options to disable unused features
30//--------------------------------------------------------------
31// Added in exposure matching
32//  - Given scan-line effect and mask always darkens image
33//  - Uses generalized tonemapper to boost mid-level
34//  - Note this can compress highlights
35//  - And won't get back peak brightness
36//  - But best option if one doesn't want as much darkening
37//--------------------------------------------------------------
38// Includes option saturation and contrast controls
39//--------------------------------------------------------------
40// Added in subtractive aperture grille
41//  - This is a bit brighter than prior
42//--------------------------------------------------------------
43// Make sure input to this filter is already low-resolution
44//  - This is not designed to work on titles doing the following
45//     - Rendering to hi-res with nearest sampling
46//--------------------------------------------------------------
47// Added a fast and more pixely option for 2 tap/pixel
48//--------------------------------------------------------------
49// Improved the vignette when WARP is enabled
50//--------------------------------------------------------------
51// Didn't test HLSL or CPU options
52//  - Will incorportate patches if they are broken
53//  - But out of time to try them myself
54//==============================================================
55////////////////////////////////////////////////////////////////
56////////////////////////////////////////////////////////////////
57////////////////////////////////////////////////////////////////
58//_____________________________/\_______________________________
59//==============================================================
60//
61//          LICENSE = UNLICENSE (aka PUBLIC DOMAIN)
62//
63//--------------------------------------------------------------
64// This is free and unencumbered software released into the
65// public domain.
66//--------------------------------------------------------------
67// Anyone is free to copy, modify, publish, use, compile, sell,
68// or distribute this software, either in source code form or as
69// a compiled binary, for any purpose, commercial or
70// non-commercial, and by any means.
71//--------------------------------------------------------------
72// In jurisdictions that recognize copyright laws, the author or
73// authors of this software dedicate any and all copyright
74// interest in the software to the public domain. We make this
75// dedication for the benefit of the public at large and to the
76// detriment of our heirs and successors. We intend this
77// dedication to be an overt act of relinquishment in perpetuity
78// of all present and future rights to this software under
79// copyright law.
80//--------------------------------------------------------------
81// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
82// KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
83// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
84// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
85// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
86// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
87// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
88// DEALINGS IN THE SOFTWARE.
89//--------------------------------------------------------------
90// For more information, please refer to
91// <http://unlicense.org/>
92//==============================================================
93////////////////////////////////////////////////////////////////
94////////////////////////////////////////////////////////////////
95////////////////////////////////////////////////////////////////
96
97#pragma parameter MASK "Mask Type" 1.0 0.0 3.0 1.0
98#pragma parameter MASK_INTENSITY "Mask Intensity" 0.5 0.0 1.0 0.05
99#pragma parameter SCANLINE_THINNESS "Scanline Intensity" 0.5 0.0 1.0 0.1
100#pragma parameter SCAN_BLUR "Sharpness" 2.5 1.0 3.0 0.1
101#pragma parameter CURVATURE "Curvature" 0.02 0.0 0.25 0.01
102#pragma parameter TRINITRON_CURVE "Trinitron-style Curve" 0.0 0.0 1.0 1.0
103#pragma parameter CORNER "Corner Round" 3.0 0.0 11.0 1.0
104#pragma parameter CRT_GAMMA "CRT Gamma" 2.4 0.0 51.0 0.1
105
106#if defined(VERTEX)
107
108#if __VERSION__ >= 130
109#define COMPAT_VARYING out
110#define COMPAT_ATTRIBUTE in
111#define COMPAT_TEXTURE texture
112#else
113#define COMPAT_VARYING varying
114#define COMPAT_ATTRIBUTE attribute
115#define COMPAT_TEXTURE texture2D
116#endif
117
118#ifdef GL_ES
119#define COMPAT_PRECISION mediump
120#else
121#define COMPAT_PRECISION
122#endif
123
124COMPAT_ATTRIBUTE vec4 VertexCoord;
125COMPAT_ATTRIBUTE vec4 COLOR;
126COMPAT_ATTRIBUTE vec4 TexCoord;
127COMPAT_VARYING vec4 COL0;
128COMPAT_VARYING vec4 TEX0;
129
130vec4 _oPosition1;
131uniform mat4 MVPMatrix;
132uniform COMPAT_PRECISION int FrameDirection;
133uniform COMPAT_PRECISION int FrameCount;
134uniform COMPAT_PRECISION vec2 OutputSize;
135uniform COMPAT_PRECISION vec2 TextureSize;
136uniform COMPAT_PRECISION vec2 InputSize;
137
138// compatibility #defines
139#define vTexCoord TEX0.xy
140#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
141#define OutSize vec4(OutputSize, 1.0 / OutputSize)
142
143void main()
144{
145    gl_Position = MVPMatrix * VertexCoord;
146    TEX0.xy = TexCoord.xy;
147}
148
149#elif defined(FRAGMENT)
150
151#ifdef GL_ES
152#ifdef GL_FRAGMENT_PRECISION_HIGH
153precision highp float;
154#else
155precision mediump float;
156#endif
157#define COMPAT_PRECISION mediump
158#else
159#define COMPAT_PRECISION
160#endif
161
162#if __VERSION__ >= 130
163#define COMPAT_VARYING in
164#define COMPAT_TEXTURE texture
165out COMPAT_PRECISION vec4 FragColor;
166#else
167#define COMPAT_VARYING varying
168#define FragColor gl_FragColor
169#define COMPAT_TEXTURE texture2D
170#endif
171
172uniform COMPAT_PRECISION int FrameDirection;
173uniform COMPAT_PRECISION int FrameCount;
174uniform COMPAT_PRECISION vec2 OutputSize;
175uniform COMPAT_PRECISION vec2 TextureSize;
176uniform COMPAT_PRECISION vec2 InputSize;
177uniform sampler2D Texture;
178COMPAT_VARYING vec4 TEX0;
179
180// compatibility #defines
181#define Source Texture
182#define vTexCoord TEX0.xy
183
184#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
185#define OutSize vec4(OutputSize, 1.0 / OutputSize)
186
187#ifdef PARAMETER_UNIFORM
188uniform COMPAT_PRECISION float CRT_GAMMA;
189uniform COMPAT_PRECISION float SCANLINE_THINNESS;
190uniform COMPAT_PRECISION float SCAN_BLUR;
191uniform COMPAT_PRECISION float MASK_INTENSITY;
192uniform COMPAT_PRECISION float CURVATURE;
193uniform COMPAT_PRECISION float CORNER;
194uniform COMPAT_PRECISION float MASK;
195uniform COMPAT_PRECISION float TRINITRON_CURVE;
196#else
197#define CRT_GAMMA 2.4
198#define SCANLINE_THINNESS 0.5
199#define SCAN_BLUR 2.5
200#define MASK_INTENSITY 0.54
201#define CURVATURE 0.02
202#define CORNER 3.0
203#define MASK 1.0
204#define TRINITRON_CURVE 0.0
205#endif
206
207//_____________________________/\_______________________________
208//==============================================================
209//
210//                       GAMMA FUNCTIONS
211//
212//--------------------------------------------------------------
213//--------------------------------------------------------------
214// Since shadertoy doesn't have sRGB textures
215// And we need linear input into shader
216// Don't do this in your code
217	float FromSrgb1(float c){
218		return (c<=0.04045)?c*(1.0/12.92):
219			pow(c*(1.0/1.055)+(0.055/1.055),CRT_GAMMA);}
220//--------------------------------------------------------------
221vec3 FromSrgb(vec3 c){return vec3(
222 FromSrgb1(c.r),FromSrgb1(c.g),FromSrgb1(c.b));}
223
224// Convert from linear to sRGB
225// Since shader toy output is not linear
226float ToSrgb1(float c){
227 return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
228//--------------------------------------------------------------
229vec3 ToSrgb(vec3 c){return vec3(
230 ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
231//--------------------------------------------------------------
232
233//_____________________________/\_______________________________
234//==============================================================
235//
236//                           DEFINES
237//
238//--------------------------------------------------------------
239// CRTS_CPU - CPU code
240// CRTS_GPU - GPU code
241//--------------------------------------------------------------
242// CRTS_GLSL - GLSL
243// CRTS_HLSL - HLSL (not tested yet)
244//--------------------------------------------------------------
245// CRTS_DEBUG - Define to see on/off split screen
246//--------------------------------------------------------------
247// CRTS_WARP - Apply screen warp
248//--------------------------------------------------------------
249// CRTS_2_TAP - Faster very pixely 2-tap filter (off is 8)
250//--------------------------------------------------------------
251// CRTS_MASK_GRILLE      - Aperture grille (aka Trinitron)
252// CRTS_MASK_GRILLE_LITE - Brighter (subtractive channels)
253// CRTS_MASK_NONE        - No mask
254// CRTS_MASK_SHADOW      - Horizontally stretched shadow mask
255//--------------------------------------------------------------
256// CRTS_TONE       - Normalize mid-level and process color
257// CRTS_CONTRAST   - Process color - enable contrast control
258// CRTS_SATURATION - Process color - enable saturation control
259//--------------------------------------------------------------
260#define CRTS_STATIC
261#define CrtsPow
262#define CRTS_RESTRICT
263//==============================================================
264////////////////////////////////////////////////////////////////
265////////////////////////////////////////////////////////////////
266////////////////////////////////////////////////////////////////
267
268//==============================================================
269//                      SETUP FOR CRTS
270//--------------------------------------------------------------
271//==============================================================
272//#define CRTS_DEBUG 1
273#define CRTS_GPU 1
274#define CRTS_GLSL 1
275//--------------------------------------------------------------
276//#define CRTS_2_TAP 1
277//--------------------------------------------------------------
278#define CRTS_TONE 1
279#define CRTS_CONTRAST 0
280#define CRTS_SATURATION 0
281//--------------------------------------------------------------
282#define CRTS_WARP 1
283//--------------------------------------------------------------
284// Try different masks -> moved to runtime parameters
285//#define CRTS_MASK_GRILLE 1
286//#define CRTS_MASK_GRILLE_LITE 1
287//#define CRTS_MASK_NONE 1
288//#define CRTS_MASK_SHADOW 1
289//--------------------------------------------------------------
290// Scanline thinness
291//  0.50 = fused scanlines
292//  0.70 = recommended default
293//  1.00 = thinner scanlines (too thin)
294#define INPUT_THIN 0.5 + (0.5 * SCANLINE_THINNESS)
295//--------------------------------------------------------------
296// Horizonal scan blur
297//  -3.0 = pixely
298//  -2.5 = default
299//  -2.0 = smooth
300//  -1.0 = too blurry
301#define INPUT_BLUR -1.0 * SCAN_BLUR
302//--------------------------------------------------------------
303// Shadow mask effect, ranges from,
304//  0.25 = large amount of mask (not recommended, too dark)
305//  0.50 = recommended default
306//  1.00 = no shadow mask
307#define INPUT_MASK 1.0 - MASK_INTENSITY
308//--------------------------------------------------------------
309#define INPUT_X InputSize.x
310#define INPUT_Y InputSize.y
311//--------------------------------------------------------------
312// Setup the function which returns input image color
313vec3 CrtsFetch(vec2 uv){
314 // For shadertoy, scale to get native texels in the image
315 uv*=vec2(INPUT_X,INPUT_Y)/TextureSize.xy;
316 // Move towards intersting parts
317// uv+=vec2(0.5,0.5);
318 // Non-shadertoy case would not have the color conversion
319 return FromSrgb(COMPAT_TEXTURE(Texture,uv.xy,-16.0).rgb);}
320#endif
321
322////////////////////////////////////////////////////////////////
323////////////////////////////////////////////////////////////////
324////////////////////////////////////////////////////////////////
325//_____________________________/\_______________________________
326//==============================================================
327//
328//                          GPU CODE
329//
330//==============================================================
331#ifdef CRTS_GPU
332//_____________________________/\_______________________________
333//==============================================================
334//                         PORTABILITY
335//==============================================================
336 #ifdef CRTS_GLSL
337  #define CrtsF1 float
338  #define CrtsF2 vec2
339  #define CrtsF3 vec3
340  #define CrtsF4 vec4
341  #define CrtsFractF1 fract
342  #define CrtsRcpF1(x) (1.0/(x))
343  #define CrtsSatF1(x) clamp((x),0.0,1.0)
344//--------------------------------------------------------------
345  CrtsF1 CrtsMax3F1(CrtsF1 a,CrtsF1 b,CrtsF1 c){
346   return max(a,max(b,c));}
347 #endif
348//==============================================================
349 #ifdef CRTS_HLSL
350  #define CrtsF1 float
351  #define CrtsF2 float2
352  #define CrtsF3 float3
353  #define CrtsF4 float4
354  #define CrtsFractF1 frac
355  #define CrtsRcpF1(x) (1.0/(x))
356  #define CrtsSatF1(x) saturate(x)
357//--------------------------------------------------------------
358  CrtsF1 CrtsMax3F1(CrtsF1 a,CrtsF1 b,CrtsF1 c){
359   return max(a,max(b,c));}
360 #endif
361//_____________________________/\_______________________________
362//==============================================================
363//              TONAL CONTROL CONSTANT GENERATION
364//--------------------------------------------------------------
365// This is in here for rapid prototyping
366// Please use the CPU code and pass in as constants
367//==============================================================
368 CrtsF4 CrtsTone(
369 CrtsF1 contrast,
370 CrtsF1 saturation,
371 CrtsF1 thin,
372 CrtsF1 mask){
373//--------------------------------------------------------------
374  if(MASK == 0.0) mask=1.0;
375//--------------------------------------------------------------
376  if(MASK == 1.0){
377   // Normal R mask is {1.0,mask,mask}
378   // LITE   R mask is {mask,1.0,1.0}
379   mask=0.5+mask*0.5;
380   }
381//--------------------------------------------------------------
382  CrtsF4 ret;
383  CrtsF1 midOut=0.18/((1.5-thin)*(0.5*mask+0.5));
384  CrtsF1 pMidIn=pow(0.18,contrast);
385  ret.x=contrast;
386  ret.y=((-pMidIn)+midOut)/((1.0-pMidIn)*midOut);
387  ret.z=((-pMidIn)*midOut+pMidIn)/(midOut*(-pMidIn)+midOut);
388  ret.w=contrast+saturation;
389  return ret;}
390//_____________________________/\_______________________________
391//==============================================================
392//                            MASK
393//--------------------------------------------------------------
394// Letting LCD/OLED pixel elements function like CRT phosphors
395// So "phosphor" resolution scales with display resolution
396//--------------------------------------------------------------
397// Not applying any warp to the mask (want high frequency)
398// Real aperture grille has a mask which gets wider on ends
399// Not attempting to be "real" but instead look the best
400//--------------------------------------------------------------
401// Shadow mask is stretched horizontally
402//  RRGGBB
403//  GBBRRG
404//  RRGGBB
405// This tends to look better on LCDs than vertical
406// Also 2 pixel width is required to get triad centered
407//--------------------------------------------------------------
408// The LITE version of the Aperture Grille is brighter
409// Uses {dark,1.0,1.0} for R channel
410// Non LITE version uses {1.0,dark,dark}
411//--------------------------------------------------------------
412// 'pos' - This is 'fragCoord.xy'
413//         Pixel {0,0} should be {0.5,0.5}
414//         Pixel {1,1} should be {1.5,1.5}
415//--------------------------------------------------------------
416// 'dark' - Exposure of of masked channel
417//          0.0=fully off, 1.0=no effect
418//==============================================================
419 CrtsF3 CrtsMask(CrtsF2 pos,CrtsF1 dark){
420  if(MASK == 2.0){
421   CrtsF3 m=CrtsF3(dark,dark,dark);
422   CrtsF1 x=CrtsFractF1(pos.x*(1.0/3.0));
423   if(x<(1.0/3.0))m.r=1.0;
424   else if(x<(2.0/3.0))m.g=1.0;
425   else m.b=1.0;
426   return m;
427  }
428//--------------------------------------------------------------
429  if(MASK == 1.0){
430   CrtsF3 m=CrtsF3(1.0,1.0,1.0);
431   CrtsF1 x=CrtsFractF1(pos.x*(1.0/3.0));
432   if(x<(1.0/3.0))m.r=dark;
433   else if(x<(2.0/3.0))m.g=dark;
434   else m.b=dark;
435   return m;
436  }
437//--------------------------------------------------------------
438  if(MASK == 0.0){
439   return CrtsF3(1.0,1.0,1.0);
440  }
441//--------------------------------------------------------------
442  if(MASK == 3.0){
443   pos.x+=pos.y*2.9999;
444   CrtsF3 m=CrtsF3(dark,dark,dark);
445   CrtsF1 x=CrtsFractF1(pos.x*(1.0/6.0));
446   if(x<(1.0/3.0))m.r=1.0;
447   else if(x<(2.0/3.0))m.g=1.0;
448   else m.b=1.0;
449   return m;
450  }
451 }
452//_____________________________/\_______________________________
453//==============================================================
454//                        FILTER ENTRY
455//--------------------------------------------------------------
456// Input must be linear
457// Output color is linear
458//--------------------------------------------------------------
459// Must have fetch function setup: CrtsF3 CrtsFetch(CrtsF2 uv)
460//  - The 'uv' range is {0.0 to 1.0} for input texture
461//  - Output of this must be linear color
462//--------------------------------------------------------------
463// SCANLINE MATH & AUTO-EXPOSURE NOTES
464// ===================================
465// Each output line has contribution from at most 2 scanlines
466// Scanlines are shaped by a windowed cosine function
467// This shape blends together well with only 2 lines of overlap
468//--------------------------------------------------------------
469// Base scanline intensity is as follows
470// which leaves output intensity range from {0 to 1.0}
471// --------
472// thin := range {thick 0.5 to thin 1.0}
473// off  := range {0.0 to <1.0},
474//         sub-pixel offset between two scanlines
475//  --------
476//  a0=cos(min(0.5,     off *thin)*2pi)*0.5+0.5;
477//  a1=cos(min(0.5,(1.0-off)*thin)*2pi)*0.5+0.5;
478//--------------------------------------------------------------
479// This leads to a image darkening factor of roughly:
480//  {(1.5-thin)/1.0}
481// This is further reduced by the mask:
482//  {1.0/2.0+mask*1.0/2.0}
483// Reciprocal of combined effect is used for auto-exposure
484//  to scale up the mid-level in the tonemapper
485//==============================================================
486 CrtsF3 CrtsFilter(
487//--------------------------------------------------------------
488  // SV_POSITION, fragCoord.xy
489  CrtsF2 ipos,
490//--------------------------------------------------------------
491  // inputSize / outputSize (in pixels)
492  CrtsF2 inputSizeDivOutputSize,
493//--------------------------------------------------------------
494  // 0.5 * inputSize (in pixels)
495  CrtsF2 halfInputSize,
496//--------------------------------------------------------------
497  // 1.0 / inputSize (in pixels)
498  CrtsF2 rcpInputSize,
499//--------------------------------------------------------------
500  // 1.0 / outputSize (in pixels)
501  CrtsF2 rcpOutputSize,
502//--------------------------------------------------------------
503  // 2.0 / outputSize (in pixels)
504  CrtsF2 twoDivOutputSize,
505//--------------------------------------------------------------
506  // inputSize.y
507  CrtsF1 inputHeight,
508//--------------------------------------------------------------
509  // Warp scanlines but not phosphor mask
510  //  0.0 = no warp
511  //  1.0/64.0 = light warping
512  //  1.0/32.0 = more warping
513  // Want x and y warping to be different (based on aspect)
514  CrtsF2 warp,
515//--------------------------------------------------------------
516  // Scanline thinness
517  //  0.50 = fused scanlines
518  //  0.70 = recommended default
519  //  1.00 = thinner scanlines (too thin)
520  // Shared with CrtsTone() function
521  CrtsF1 thin,
522//--------------------------------------------------------------
523  // Horizonal scan blur
524  //  -3.0 = pixely
525  //  -2.5 = default
526  //  -2.0 = smooth
527  //  -1.0 = too blurry
528  CrtsF1 blur,
529//--------------------------------------------------------------
530  // Shadow mask effect, ranges from,
531  //  0.25 = large amount of mask (not recommended, too dark)
532  //  0.50 = recommended default
533  //  1.00 = no shadow mask
534  // Shared with CrtsTone() function
535  CrtsF1 mask,
536//--------------------------------------------------------------
537  // Tonal curve parameters generated by CrtsTone()
538  CrtsF4 tone
539//--------------------------------------------------------------
540 ){
541//--------------------------------------------------------------
542  #ifdef CRTS_DEBUG
543   CrtsF2 uv=ipos*rcpOutputSize;
544   // Show second half processed, and first half un-processed
545   if(uv.x<0.5){
546    // Force nearest to get squares
547    uv*=1.0/rcpInputSize;
548    uv=floor(uv)+CrtsF2(0.5,0.5);
549    uv*=rcpInputSize;
550    CrtsF3 color=CrtsFetch(uv);
551    return color;}
552  #endif
553//--------------------------------------------------------------
554  // Optional apply warp
555  CrtsF2 pos;
556  #ifdef CRTS_WARP
557   // Convert to {-1 to 1} range
558   pos=ipos*twoDivOutputSize-CrtsF2(1.0,1.0);
559   // Distort pushes image outside {-1 to 1} range
560   pos*=CrtsF2(
561    1.0+(pos.y*pos.y)*warp.x,
562    1.0+(pos.x*pos.x)*warp.y);
563   // TODO: Vignette needs optimization
564   CrtsF1 vin=(1.0-(
565    (1.0-CrtsSatF1(pos.x*pos.x))*(1.0-CrtsSatF1(pos.y*pos.y)))) * (0.998 + (0.001 * CORNER));
566   vin=CrtsSatF1((-vin)*inputHeight+inputHeight);
567   // Leave in {0 to inputSize}
568   pos=pos*halfInputSize+halfInputSize;
569  #else
570   pos=ipos*inputSizeDivOutputSize;
571  #endif
572//--------------------------------------------------------------
573  // Snap to center of first scanline
574  CrtsF1 y0=floor(pos.y-0.5)+0.5;
575  #ifdef CRTS_2_TAP
576   // Using Inigo's "Improved Texture Interpolation"
577   // http://iquilezles.org/www/articles/texture/texture.htm
578   pos.x+=0.5;
579   CrtsF1 xi=floor(pos.x);
580   CrtsF1 xf=pos.x-xi;
581   xf=xf*xf*xf*(xf*(xf*6.0-15.0)+10.0);
582   CrtsF1 x0=xi+xf-0.5;
583   CrtsF2 p=CrtsF2(x0*rcpInputSize.x,y0*rcpInputSize.y);
584   // Coordinate adjusted bilinear fetch from 2 nearest scanlines
585   CrtsF3 colA=CrtsFetch(p);
586   p.y+=rcpInputSize.y;
587   CrtsF3 colB=CrtsFetch(p);
588  #else
589   // Snap to center of one of four pixels
590   CrtsF1 x0=floor(pos.x-1.5)+0.5;
591   // Inital UV position
592   CrtsF2 p=CrtsF2(x0*rcpInputSize.x,y0*rcpInputSize.y);
593   // Fetch 4 nearest texels from 2 nearest scanlines
594   CrtsF3 colA0=CrtsFetch(p);
595   p.x+=rcpInputSize.x;
596   CrtsF3 colA1=CrtsFetch(p);
597   p.x+=rcpInputSize.x;
598   CrtsF3 colA2=CrtsFetch(p);
599   p.x+=rcpInputSize.x;
600   CrtsF3 colA3=CrtsFetch(p);
601   p.y+=rcpInputSize.y;
602   CrtsF3 colB3=CrtsFetch(p);
603   p.x-=rcpInputSize.x;
604   CrtsF3 colB2=CrtsFetch(p);
605   p.x-=rcpInputSize.x;
606   CrtsF3 colB1=CrtsFetch(p);
607   p.x-=rcpInputSize.x;
608   CrtsF3 colB0=CrtsFetch(p);
609  #endif
610//--------------------------------------------------------------
611  // Vertical filter
612  // Scanline intensity is using sine wave
613  // Easy filter window and integral used later in exposure
614  CrtsF1 off=pos.y-y0;
615  CrtsF1 pi2=6.28318530717958;
616  CrtsF1 hlf=0.5;
617  CrtsF1 scanA=cos(min(0.5,  off *thin     )*pi2)*hlf+hlf;
618  CrtsF1 scanB=cos(min(0.5,(-off)*thin+thin)*pi2)*hlf+hlf;
619//--------------------------------------------------------------
620  #ifdef CRTS_2_TAP
621   #ifdef CRTS_WARP
622    // Get rid of wrong pixels on edge
623    scanA*=vin;
624    scanB*=vin;
625   #endif
626   // Apply vertical filter
627   CrtsF3 color=(colA*scanA)+(colB*scanB);
628  #else
629   // Horizontal kernel is simple gaussian filter
630   CrtsF1 off0=pos.x-x0;
631   CrtsF1 off1=off0-1.0;
632   CrtsF1 off2=off0-2.0;
633   CrtsF1 off3=off0-3.0;
634   CrtsF1 pix0=exp2(blur*off0*off0);
635   CrtsF1 pix1=exp2(blur*off1*off1);
636   CrtsF1 pix2=exp2(blur*off2*off2);
637   CrtsF1 pix3=exp2(blur*off3*off3);
638   CrtsF1 pixT=CrtsRcpF1(pix0+pix1+pix2+pix3);
639   #ifdef CRTS_WARP
640    // Get rid of wrong pixels on edge
641    pixT*=vin;
642   #endif
643   scanA*=pixT;
644   scanB*=pixT;
645   // Apply horizontal and vertical filters
646   CrtsF3 color=
647    (colA0*pix0+colA1*pix1+colA2*pix2+colA3*pix3)*scanA +
648    (colB0*pix0+colB1*pix1+colB2*pix2+colB3*pix3)*scanB;
649  #endif
650//--------------------------------------------------------------
651  // Apply phosphor mask
652  color*=CrtsMask(ipos,mask);
653//--------------------------------------------------------------
654  // Optional color processing
655  #ifdef CRTS_TONE
656   // Tonal control, start by protecting from /0
657   CrtsF1 peak=max(1.0/(256.0*65536.0),
658    CrtsMax3F1(color.r,color.g,color.b));
659   // Compute the ratios of {R,G,B}
660   CrtsF3 ratio=color*CrtsRcpF1(peak);
661   // Apply tonal curve to peak value
662   #ifdef CRTS_CONTRAST
663    peak=pow(peak,tone.x);
664   #endif
665   peak=peak*CrtsRcpF1(peak*tone.y+tone.z);
666   // Apply saturation
667   #ifdef CRTS_SATURATION
668    ratio=pow(ratio,CrtsF3(tone.w,tone.w,tone.w));
669   #endif
670   // Reconstruct color
671   return ratio*peak;
672 #else
673  return color;
674 #endif
675//--------------------------------------------------------------
676 }
677
678
679void main()
680{
681	vec2 warp_factor;
682	warp_factor.x = CURVATURE;
683	warp_factor.y = (3.0 / 4.0) * warp_factor.x; // assume 4:3 aspect
684	warp_factor.x *= (1.0 - TRINITRON_CURVE);
685	FragColor.rgb = CrtsFilter(vTexCoord.xy * OutputSize.xy*(TextureSize.xy / InputSize.xy),
686	InputSize.xy / OutputSize.xy,
687	InputSize.xy * vec2(0.5,0.5),
688	1.0/InputSize.xy,
689	1.0/OutputSize.xy,
690	2.0/OutputSize.xy,
691	InputSize.y,
692	warp_factor,
693	INPUT_THIN,
694	INPUT_BLUR,
695	INPUT_MASK,
696	CrtsTone(1.0,0.0,INPUT_THIN,INPUT_MASK));
697
698	// Shadertoy outputs non-linear color
699	FragColor.rgb=ToSrgb(FragColor.rgb);
700}
701#endif
702