1$input v_sinangle, v_cosangle, v_stretch, v_one, v_texCoord 2 3/* CRT shader 4 * 5 * Copyright (C) 2010-2016 cgwg, Themaister and DOLLS 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 */ 12 13#include "common.sh" 14 15// Comment the next line to disable interpolation in linear gamma (and gain speed). 16//#define LINEAR_PROCESSING 17 18// Enable 3x oversampling of the beam profile 19#define OVERSAMPLE 20 21// Use the older, purely gaussian beam profile 22#define USEGAUSSIAN 23 24// Macros. 25#define FIX(c) max(abs(c), 1e-5) 26#define PI 3.141592653589 27 28SAMPLER2D(mpass_texture, 0); 29SAMPLER2D(mask_texture, 1); 30#ifdef LINEAR_PROCESSING 31# define TEX2D(c) pow(texture2D(mpass_texture, (c)), vec4(CRTgamma)) 32#else 33# define TEX2D(c) texture2D(mpass_texture, (c)) 34#endif 35 36// Enable screen curvature. 37uniform vec4 curvature; 38 39uniform vec4 u_tex_size0; 40uniform vec4 u_tex_size1; 41uniform vec4 u_quad_dims; 42 43uniform vec4 aperture_strength; 44 45uniform vec4 CRTgamma; 46uniform vec4 monitorgamma; 47 48uniform vec4 overscan; 49uniform vec4 aspect; 50 51uniform vec4 d; 52uniform vec4 R; 53 54uniform vec4 cornersize; 55uniform vec4 cornersmooth; 56 57float intersect(vec2 xy , vec2 sinangle, vec2 cosangle) 58{ 59 float A = dot(xy,xy)+d.x*d.x; 60 float B = 2.0*(R.x*(dot(xy,sinangle)-d.x*cosangle.x*cosangle.y)-d.x*d.x); 61 float C = d.x*d.x + 2.0*R.x*d.x*cosangle.x*cosangle.y; 62 return (-B-sqrt(B*B-4.0*A*C))/(2.0*A); 63} 64 65vec2 bkwtrans(vec2 xy, vec2 sinangle, vec2 cosangle) 66{ 67 float c = intersect(xy, sinangle, cosangle); 68 vec2 pt = vec2_splat(c)*xy; 69 pt -= vec2_splat(-R.x)*sinangle; 70 pt /= vec2_splat(R.x); 71 vec2 tang = sinangle/cosangle; 72 vec2 poc = pt/cosangle; 73 float A = dot(tang,tang)+1.0; 74 float B = -2.0*dot(poc,tang); 75 float C = dot(poc,poc)-1.0; 76 float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A); 77 vec2 uv = (pt-a*sinangle)/cosangle; 78 float r = FIX(R.x*acos(a)); 79 return uv*r/sin(r/R.x); 80} 81 82vec2 transform(vec2 coord, vec3 stretch, vec2 sinangle, vec2 cosangle) 83{ 84 coord = (coord-vec2_splat(0.5))*aspect.xy*stretch.z+stretch.xy; 85 return (bkwtrans(coord, sinangle, cosangle)/overscan.xy/aspect.xy+vec2_splat(0.5)); 86} 87 88float corner(vec2 coord) 89{ 90 coord = (coord - vec2_splat(0.5)) * overscan.xy + vec2_splat(0.5); 91 coord = min(coord, vec2_splat(1.0)-coord) * aspect.xy; 92 vec2 cdist = vec2_splat(cornersize.x); 93 coord = (cdist - min(coord,cdist)); 94 float dist = sqrt(dot(coord,coord)); 95 return clamp((max(cdist.x,1e-3)-dist)*cornersmooth.x,0.0, 1.0); 96} 97 98// Calculate the influence of a scanline on the current pixel. 99// 100// 'distance' is the distance in texture coordinates from the current 101// pixel to the scanline in question. 102// 'color' is the colour of the scanline at the horizontal location of 103// the current pixel. 104vec4 scanlineWeights(float distance, vec4 color) 105{ 106 // "wid" controls the width of the scanline beam, for each RGB channel 107 // The "weights" lines basically specify the formula that gives 108 // you the profile of the beam, i.e. the intensity as 109 // a function of distance from the vertical center of the 110 // scanline. In this case, it is gaussian if width=2, and 111 // becomes nongaussian for larger widths. Ideally this should 112 // be normalized so that the integral across the beam is 113 // independent of its width. That is, for a narrower beam 114 // "weights" should have a higher peak at the center of the 115 // scanline than for a wider beam. 116#ifdef USEGAUSSIAN 117 vec4 wid = 0.3 + 0.1 * pow(color, vec4_splat(3.0)); 118 vec4 weights = vec4(distance / wid); 119 return 0.4 * exp(-weights * weights) / wid; 120#else 121 vec4 wid = 2.0 + 2.0 * pow(color, vec4_splat(4.0)); 122 vec4 weights = vec4_splat(distance / 0.3); 123 return 1.4 * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid); 124#endif 125} 126 127void main() 128{ 129 // Here's a helpful diagram to keep in mind while trying to 130 // understand the code: 131 // 132 // | | | | | 133 // ------------------------------- 134 // | | | | | 135 // | 01 | 11 | 21 | 31 | <-- current scanline 136 // | | @ | | | 137 // ------------------------------- 138 // | | | | | 139 // | 02 | 12 | 22 | 32 | <-- next scanline 140 // | | | | | 141 // ------------------------------- 142 // | | | | | 143 // 144 // Each character-cell represents a pixel on the output 145 // surface, "@" represents the current pixel (always somewhere 146 // in the bottom half of the current scan-line, or the top-half 147 // of the next scanline). The grid of lines represents the 148 // edges of the texels of the underlying texture. 149 150 // Texture coordinates of the texel containing the active pixel. 151 vec2 xy; 152 if (curvature.x > 0.5) 153 xy = transform(v_texCoord, v_stretch, v_sinangle, v_cosangle); 154 else 155 xy = (v_texCoord-vec2_splat(0.5))/overscan.xy+vec2_splat(0.5); 156 float cval = corner(xy); 157 158 // Of all the pixels that are mapped onto the texel we are 159 // currently rendering, which pixel are we currently rendering? 160 vec2 ratio_scale = xy * u_tex_size0.xy - vec2_splat(0.5); 161 162#ifdef OVERSAMPLE 163 float filter = fwidth(ratio_scale.y); 164#endif 165 vec2 uv_ratio = fract(ratio_scale); 166 167 // Snap to the center of the underlying texel. 168 xy = (floor(ratio_scale) + vec2_splat(0.5)) / u_tex_size0.xy; 169 170 // Calculate Lanczos scaling coefficients describing the effect 171 // of various neighbour texels in a scanline on the current 172 // pixel. 173 vec4 coeffs = PI * vec4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x); 174 175 // Prevent division by zero. 176 coeffs = FIX(coeffs); 177 178 // Lanczos2 kernel. 179 coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs); 180 181 // Normalize. 182 coeffs /= dot(coeffs, vec4_splat(1.0)); 183 184 // Calculate the effective colour of the current and next 185 // scanlines at the horizontal location of the current pixel, 186 // using the Lanczos coefficients above. 187 vec4 col = clamp(TEX2D(xy + vec2(-v_one.x, 0.0))*coeffs.x + 188 TEX2D(xy)*coeffs.y + 189 TEX2D(xy +vec2(v_one.x, 0.0))*coeffs.z + 190 TEX2D(xy + vec2(2.0 * v_one.x, 0.0))*coeffs.w , 0.0, 1.0); 191 192 vec4 col2 = clamp(TEX2D(xy + vec2(-v_one.x, v_one.y))*coeffs.x + 193 TEX2D(xy + vec2(0.0, v_one.y))*coeffs.y + 194 TEX2D(xy + v_one)*coeffs.z + 195 TEX2D(xy + vec2(2.0 * v_one.x, v_one.y))*coeffs.w , 0.0, 1.0); 196 197 198#ifndef LINEAR_PROCESSING 199 col = pow(col , vec4_splat(CRTgamma.x)); 200 col2 = pow(col2, vec4_splat(CRTgamma.x)); 201#endif 202 203 // Calculate the influence of the current and next scanlines on 204 // the current pixel. 205 vec4 weights = scanlineWeights(uv_ratio.y, col); 206 vec4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2); 207#ifdef OVERSAMPLE 208 uv_ratio.y =uv_ratio.y+1.0/3.0*filter; 209 weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0; 210 weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0; 211 uv_ratio.y =uv_ratio.y-2.0/3.0*filter; 212 weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0; 213 weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0; 214#endif 215 vec3 mul_res = (col * weights + col2 * weights2).rgb * vec3_splat(cval); 216 217 // Convert the image gamma for display on our output device. 218 mul_res = pow(mul_res, vec3_splat(1.0 / monitorgamma.x)); 219 220 // Color the texel. 221 222 xy = v_texCoord.xy * u_quad_dims.xy / u_tex_size1.xy; 223 vec3 mask = texture2D(mask_texture, xy).rgb; 224 mask = mix(vec3_splat(1.0), mask, aperture_strength.x); 225 226 gl_FragColor = vec4(mul_res*mask, col.a); 227} 228