scanpass.fs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #version 150
  2. // PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
  3. //
  4. // by Timothy Lottes
  5. //
  6. // This is more along the style of a really good CGA arcade monitor.
  7. // With RGB inputs instead of NTSC.
  8. // The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
  9. //
  10. // Left it unoptimized to show the theory behind the algorithm.
  11. //
  12. // It is an example what I personally would want as a display option for pixel art games.
  13. // Please take and use, change, or whatever.
  14. #define hardScan -8.0
  15. #define hardPix -3.0
  16. #define warpX 0.031
  17. #define warpY 0.041
  18. #define maskDark 0.5
  19. #define maskLight 1.5
  20. #define scaleInLinearGamma 1.0
  21. #define shadowMask 3.0
  22. #define brightBoost 1.0
  23. #define hardBloomPix -1.5
  24. #define hardBloomScan -2.0
  25. #define bloomAmount 0.4
  26. #define shape 2.0
  27. uniform sampler2D source[];
  28. uniform vec4 sourceSize[];
  29. uniform vec4 outputSize;
  30. in Vertex {
  31. vec2 vTexCoord;
  32. };
  33. out vec4 FragColor;
  34. //Uncomment to reduce instructions with simpler linearization
  35. //(fixes HD3000 Sandy Bridge IGP)
  36. //#define SIMPLE_LINEAR_GAMMA
  37. #define DO_BLOOM 1
  38. // ------------- //
  39. // sRGB to Linear.
  40. // Assuming using sRGB typed textures this should not be needed.
  41. #ifdef SIMPLE_LINEAR_GAMMA
  42. float ToLinear1(float c)
  43. {
  44. return c;
  45. }
  46. vec3 ToLinear(vec3 c)
  47. {
  48. return c;
  49. }
  50. vec3 ToSrgb(vec3 c)
  51. {
  52. return pow(c, vec3(1.0 / 2.2));
  53. }
  54. #else
  55. float ToLinear1(float c)
  56. {
  57. if (scaleInLinearGamma == 0)
  58. return c;
  59. return(c<=0.04045) ? c/12.92 : pow((c + 0.055)/1.055, 2.4);
  60. }
  61. vec3 ToLinear(vec3 c)
  62. {
  63. if (scaleInLinearGamma==0)
  64. return c;
  65. return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
  66. }
  67. // Linear to sRGB.
  68. // Assuming using sRGB typed textures this should not be needed.
  69. float ToSrgb1(float c)
  70. {
  71. if (scaleInLinearGamma == 0)
  72. return c;
  73. return(c<0.0031308 ? c*12.92 : 1.055*pow(c, 0.41666) - 0.055);
  74. }
  75. vec3 ToSrgb(vec3 c)
  76. {
  77. if (scaleInLinearGamma == 0)
  78. return c;
  79. return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
  80. }
  81. #endif
  82. // Nearest emulated sample given floating point position and texel offset.
  83. // Also zero's off screen.
  84. vec3 Fetch(vec2 pos,vec2 off){
  85. pos=(floor(pos*sourceSize[0].xy+off)+vec2(0.5,0.5))/sourceSize[0].xy;
  86. #ifdef SIMPLE_LINEAR_GAMMA
  87. return ToLinear(brightBoost * pow(texture(source[1],pos.xy).rgb, vec3(2.2)));
  88. #else
  89. return ToLinear(brightBoost * texture(source[1],pos.xy).rgb);
  90. #endif
  91. }
  92. // Distance in emulated pixels to nearest texel.
  93. vec2 Dist(vec2 pos)
  94. {
  95. pos = pos*sourceSize[0].xy;
  96. return -((pos - floor(pos)) - vec2(0.5));
  97. }
  98. // 1D Gaussian.
  99. float Gaus(float pos, float scale)
  100. {
  101. return exp2(scale*pow(abs(pos), shape));
  102. }
  103. // 3-tap Gaussian filter along horz line.
  104. vec3 Horz3(vec2 pos, float off)
  105. {
  106. vec3 b = Fetch(pos, vec2(-1.0, off));
  107. vec3 c = Fetch(pos, vec2( 0.0, off));
  108. vec3 d = Fetch(pos, vec2( 1.0, off));
  109. float dst = Dist(pos).x;
  110. // Convert distance to weight.
  111. float scale = hardPix;
  112. float wb = Gaus(dst-1.0,scale);
  113. float wc = Gaus(dst+0.0,scale);
  114. float wd = Gaus(dst+1.0,scale);
  115. // Return filtered sample.
  116. return (b*wb+c*wc+d*wd)/(wb+wc+wd);
  117. }
  118. // 5-tap Gaussian filter along horz line.
  119. vec3 Horz5(vec2 pos,float off){
  120. vec3 a = Fetch(pos,vec2(-2.0, off));
  121. vec3 b = Fetch(pos,vec2(-1.0, off));
  122. vec3 c = Fetch(pos,vec2( 0.0, off));
  123. vec3 d = Fetch(pos,vec2( 1.0, off));
  124. vec3 e = Fetch(pos,vec2( 2.0, off));
  125. float dst = Dist(pos).x;
  126. // Convert distance to weight.
  127. float scale = hardPix;
  128. float wa = Gaus(dst - 2.0, scale);
  129. float wb = Gaus(dst - 1.0, scale);
  130. float wc = Gaus(dst + 0.0, scale);
  131. float wd = Gaus(dst + 1.0, scale);
  132. float we = Gaus(dst + 2.0, scale);
  133. // Return filtered sample.
  134. return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);
  135. }
  136. // 7-tap Gaussian filter along horz line.
  137. vec3 Horz7(vec2 pos,float off)
  138. {
  139. vec3 a = Fetch(pos, vec2(-3.0, off));
  140. vec3 b = Fetch(pos, vec2(-2.0, off));
  141. vec3 c = Fetch(pos, vec2(-1.0, off));
  142. vec3 d = Fetch(pos, vec2( 0.0, off));
  143. vec3 e = Fetch(pos, vec2( 1.0, off));
  144. vec3 f = Fetch(pos, vec2( 2.0, off));
  145. vec3 g = Fetch(pos, vec2( 3.0, off));
  146. float dst = Dist(pos).x;
  147. // Convert distance to weight.
  148. float scale = hardBloomPix;
  149. float wa = Gaus(dst - 3.0, scale);
  150. float wb = Gaus(dst - 2.0, scale);
  151. float wc = Gaus(dst - 1.0, scale);
  152. float wd = Gaus(dst + 0.0, scale);
  153. float we = Gaus(dst + 1.0, scale);
  154. float wf = Gaus(dst + 2.0, scale);
  155. float wg = Gaus(dst + 3.0, scale);
  156. // Return filtered sample.
  157. return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);
  158. }
  159. // Return scanline weight.
  160. float Scan(vec2 pos, float off)
  161. {
  162. float dst = Dist(pos).y;
  163. return Gaus(dst + off, hardScan);
  164. }
  165. // Return scanline weight for bloom.
  166. float BloomScan(vec2 pos, float off)
  167. {
  168. float dst = Dist(pos).y;
  169. return Gaus(dst + off, hardBloomScan);
  170. }
  171. // Allow nearest three lines to effect pixel.
  172. vec3 Tri(vec2 pos)
  173. {
  174. vec3 a = Horz3(pos,-1.0);
  175. vec3 b = Horz5(pos, 0.0);
  176. vec3 c = Horz3(pos, 1.0);
  177. float wa = Scan(pos,-1.0);
  178. float wb = Scan(pos, 0.0);
  179. float wc = Scan(pos, 1.0);
  180. return a*wa + b*wb + c*wc;
  181. }
  182. // Small bloom.
  183. vec3 Bloom(vec2 pos)
  184. {
  185. vec3 a = Horz5(pos,-2.0);
  186. vec3 b = Horz7(pos,-1.0);
  187. vec3 c = Horz7(pos, 0.0);
  188. vec3 d = Horz7(pos, 1.0);
  189. vec3 e = Horz5(pos, 2.0);
  190. float wa = BloomScan(pos,-2.0);
  191. float wb = BloomScan(pos,-1.0);
  192. float wc = BloomScan(pos, 0.0);
  193. float wd = BloomScan(pos, 1.0);
  194. float we = BloomScan(pos, 2.0);
  195. return a*wa+b*wb+c*wc+d*wd+e*we;
  196. }
  197. // Distortion of scanlines, and end of screen alpha.
  198. vec2 Warp(vec2 pos)
  199. {
  200. pos = pos*2.0-1.0;
  201. pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY);
  202. return pos*0.5 + 0.5;
  203. }
  204. // Shadow mask.
  205. vec3 Mask(vec2 pos)
  206. {
  207. vec3 mask = vec3(maskDark, maskDark, maskDark);
  208. // Very compressed TV style shadow mask.
  209. if (shadowMask == 1.0)
  210. {
  211. float line = maskLight;
  212. float odd = 0.0;
  213. if (fract(pos.x*0.166666666) < 0.5) odd = 1.0;
  214. if (fract((pos.y + odd) * 0.5) < 0.5) line = maskDark;
  215. pos.x = fract(pos.x*0.333333333);
  216. if (pos.x < 0.333) mask.r = maskLight;
  217. else if (pos.x < 0.666) mask.g = maskLight;
  218. else mask.b = maskLight;
  219. mask*=line;
  220. }
  221. // Aperture-grille.
  222. else if (shadowMask == 2.0)
  223. {
  224. pos.x = fract(pos.x*0.333333333);
  225. if (pos.x < 0.333) mask.r = maskLight;
  226. else if (pos.x < 0.666) mask.g = maskLight;
  227. else mask.b = maskLight;
  228. }
  229. // Stretched VGA style shadow mask (same as prior shaders).
  230. else if (shadowMask == 3.0)
  231. {
  232. pos.x += pos.y*3.0;
  233. pos.x = fract(pos.x*0.166666666);
  234. if (pos.x < 0.333) mask.r = maskLight;
  235. else if (pos.x < 0.666) mask.g = maskLight;
  236. else mask.b = maskLight;
  237. }
  238. // VGA style shadow mask.
  239. else if (shadowMask == 4.0)
  240. {
  241. pos.xy = floor(pos.xy*vec2(1.0, 0.5));
  242. pos.x += pos.y*3.0;
  243. pos.x = fract(pos.x*0.166666666);
  244. if (pos.x < 0.333) mask.r = maskLight;
  245. else if (pos.x < 0.666) mask.g = maskLight;
  246. else mask.b = maskLight;
  247. }
  248. return mask;
  249. }
  250. void main() {
  251. vec2 pos = Warp(vTexCoord);
  252. vec3 outColor = Tri(pos).rgb;
  253. if (shadowMask > 0.0)
  254. outColor.rgb *= Mask(vTexCoord.xy / outputSize.zw * 1.000001);
  255. #ifdef DO_BLOOM
  256. //Add Bloom
  257. outColor.rgb += mix( vec3(0.0), texture(source[0], pos).rgb, bloomAmount);
  258. #endif
  259. FragColor = vec4(ToSrgb(outColor.rgb), 1.0);
  260. }