bloompass.fs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. in Vertex {
  30. vec2 vTexCoord;
  31. };
  32. out vec4 FragColor;
  33. //Uncomment to reduce instructions with simpler linearization
  34. //(fixes HD3000 Sandy Bridge IGP)
  35. //#define SIMPLE_LINEAR_GAMMA
  36. #define DO_BLOOM 1
  37. // ------------- //
  38. // sRGB to Linear.
  39. // Assuming using sRGB typed textures this should not be needed.
  40. #ifdef SIMPLE_LINEAR_GAMMA
  41. float ToLinear1(float c)
  42. {
  43. return c;
  44. }
  45. vec3 ToLinear(vec3 c)
  46. {
  47. return c;
  48. }
  49. vec3 ToSrgb(vec3 c)
  50. {
  51. return pow(c, vec3(1.0 / 2.2));
  52. }
  53. #else
  54. float ToLinear1(float c)
  55. {
  56. if (scaleInLinearGamma == 0)
  57. return c;
  58. return(c<=0.04045) ? c/12.92 : pow((c + 0.055)/1.055, 2.4);
  59. }
  60. vec3 ToLinear(vec3 c)
  61. {
  62. if (scaleInLinearGamma==0)
  63. return c;
  64. return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
  65. }
  66. // Linear to sRGB.
  67. // Assuming using sRGB typed textures this should not be needed.
  68. float ToSrgb1(float c)
  69. {
  70. if (scaleInLinearGamma == 0)
  71. return c;
  72. return(c<0.0031308 ? c*12.92 : 1.055*pow(c, 0.41666) - 0.055);
  73. }
  74. vec3 ToSrgb(vec3 c)
  75. {
  76. if (scaleInLinearGamma == 0)
  77. return c;
  78. return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
  79. }
  80. #endif
  81. // Nearest emulated sample given floating point position and texel offset.
  82. // Also zero's off screen.
  83. vec3 Fetch(vec2 pos,vec2 off){
  84. pos=(floor(pos*sourceSize[0].xy+off)+vec2(0.5,0.5))/sourceSize[0].xy;
  85. #ifdef SIMPLE_LINEAR_GAMMA
  86. return ToLinear(brightBoost * pow(texture(source[0],pos.xy).rgb, vec3(2.2)));
  87. #else
  88. return ToLinear(brightBoost * texture(source[0],pos.xy).rgb);
  89. #endif
  90. }
  91. // Distance in emulated pixels to nearest texel.
  92. vec2 Dist(vec2 pos)
  93. {
  94. pos = pos*sourceSize[0].xy;
  95. return -((pos - floor(pos)) - vec2(0.5));
  96. }
  97. // 1D Gaussian.
  98. float Gaus(float pos, float scale)
  99. {
  100. return exp2(scale*pow(abs(pos), shape));
  101. }
  102. // 3-tap Gaussian filter along horz line.
  103. vec3 Horz3(vec2 pos, float off)
  104. {
  105. vec3 b = Fetch(pos, vec2(-1.0, off));
  106. vec3 c = Fetch(pos, vec2( 0.0, off));
  107. vec3 d = Fetch(pos, vec2( 1.0, off));
  108. float dst = Dist(pos).x;
  109. // Convert distance to weight.
  110. float scale = hardPix;
  111. float wb = Gaus(dst-1.0,scale);
  112. float wc = Gaus(dst+0.0,scale);
  113. float wd = Gaus(dst+1.0,scale);
  114. // Return filtered sample.
  115. return (b*wb+c*wc+d*wd)/(wb+wc+wd);
  116. }
  117. // 5-tap Gaussian filter along horz line.
  118. vec3 Horz5(vec2 pos,float off){
  119. vec3 a = Fetch(pos,vec2(-2.0, off));
  120. vec3 b = Fetch(pos,vec2(-1.0, off));
  121. vec3 c = Fetch(pos,vec2( 0.0, off));
  122. vec3 d = Fetch(pos,vec2( 1.0, off));
  123. vec3 e = Fetch(pos,vec2( 2.0, off));
  124. float dst = Dist(pos).x;
  125. // Convert distance to weight.
  126. float scale = hardPix;
  127. float wa = Gaus(dst - 2.0, scale);
  128. float wb = Gaus(dst - 1.0, scale);
  129. float wc = Gaus(dst + 0.0, scale);
  130. float wd = Gaus(dst + 1.0, scale);
  131. float we = Gaus(dst + 2.0, scale);
  132. // Return filtered sample.
  133. return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);
  134. }
  135. // 7-tap Gaussian filter along horz line.
  136. vec3 Horz7(vec2 pos,float off)
  137. {
  138. vec3 a = Fetch(pos, vec2(-3.0, off));
  139. vec3 b = Fetch(pos, vec2(-2.0, off));
  140. vec3 c = Fetch(pos, vec2(-1.0, off));
  141. vec3 d = Fetch(pos, vec2( 0.0, off));
  142. vec3 e = Fetch(pos, vec2( 1.0, off));
  143. vec3 f = Fetch(pos, vec2( 2.0, off));
  144. vec3 g = Fetch(pos, vec2( 3.0, off));
  145. float dst = Dist(pos).x;
  146. // Convert distance to weight.
  147. float scale = hardBloomPix;
  148. float wa = Gaus(dst - 3.0, scale);
  149. float wb = Gaus(dst - 2.0, scale);
  150. float wc = Gaus(dst - 1.0, scale);
  151. float wd = Gaus(dst + 0.0, scale);
  152. float we = Gaus(dst + 1.0, scale);
  153. float wf = Gaus(dst + 2.0, scale);
  154. float wg = Gaus(dst + 3.0, scale);
  155. // Return filtered sample.
  156. return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);
  157. }
  158. // Return scanline weight.
  159. float Scan(vec2 pos, float off)
  160. {
  161. float dst = Dist(pos).y;
  162. return Gaus(dst + off, hardScan);
  163. }
  164. // Return scanline weight for bloom.
  165. float BloomScan(vec2 pos, float off)
  166. {
  167. float dst = Dist(pos).y;
  168. return Gaus(dst + off, hardBloomScan);
  169. }
  170. // Allow nearest three lines to effect pixel.
  171. vec3 Tri(vec2 pos)
  172. {
  173. vec3 a = Horz3(pos,-1.0);
  174. vec3 b = Horz5(pos, 0.0);
  175. vec3 c = Horz3(pos, 1.0);
  176. float wa = Scan(pos,-1.0);
  177. float wb = Scan(pos, 0.0);
  178. float wc = Scan(pos, 1.0);
  179. return a*wa + b*wb + c*wc;
  180. }
  181. // Small bloom.
  182. vec3 Bloom(vec2 pos)
  183. {
  184. vec3 a = Horz5(pos,-2.0);
  185. vec3 b = Horz7(pos,-1.0);
  186. vec3 c = Horz7(pos, 0.0);
  187. vec3 d = Horz7(pos, 1.0);
  188. vec3 e = Horz5(pos, 2.0);
  189. float wa = BloomScan(pos,-2.0);
  190. float wb = BloomScan(pos,-1.0);
  191. float wc = BloomScan(pos, 0.0);
  192. float wd = BloomScan(pos, 1.0);
  193. float we = BloomScan(pos, 2.0);
  194. return a*wa+b*wb+c*wc+d*wd+e*we;
  195. }
  196. // Distortion of scanlines, and end of screen alpha.
  197. vec2 Warp(vec2 pos)
  198. {
  199. pos = pos*2.0-1.0;
  200. pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY);
  201. return pos*0.5 + 0.5;
  202. }
  203. // Shadow mask.
  204. vec3 Mask(vec2 pos)
  205. {
  206. vec3 mask = vec3(maskDark, maskDark, maskDark);
  207. // Very compressed TV style shadow mask.
  208. if (shadowMask == 1.0)
  209. {
  210. float line = maskLight;
  211. float odd = 0.0;
  212. if (fract(pos.x*0.166666666) < 0.5) odd = 1.0;
  213. if (fract((pos.y + odd) * 0.5) < 0.5) line = maskDark;
  214. pos.x = fract(pos.x*0.333333333);
  215. if (pos.x < 0.333) mask.r = maskLight;
  216. else if (pos.x < 0.666) mask.g = maskLight;
  217. else mask.b = maskLight;
  218. mask*=line;
  219. }
  220. // Aperture-grille.
  221. else if (shadowMask == 2.0)
  222. {
  223. pos.x = fract(pos.x*0.333333333);
  224. if (pos.x < 0.333) mask.r = maskLight;
  225. else if (pos.x < 0.666) mask.g = maskLight;
  226. else mask.b = maskLight;
  227. }
  228. // Stretched VGA style shadow mask (same as prior shaders).
  229. else if (shadowMask == 3.0)
  230. {
  231. pos.x += pos.y*3.0;
  232. pos.x = fract(pos.x*0.166666666);
  233. if (pos.x < 0.333) mask.r = maskLight;
  234. else if (pos.x < 0.666) mask.g = maskLight;
  235. else mask.b = maskLight;
  236. }
  237. // VGA style shadow mask.
  238. else if (shadowMask == 4.0)
  239. {
  240. pos.xy = floor(pos.xy*vec2(1.0, 0.5));
  241. pos.x += pos.y*3.0;
  242. pos.x = fract(pos.x*0.166666666);
  243. if (pos.x < 0.333) mask.r = maskLight;
  244. else if (pos.x < 0.666) mask.g = maskLight;
  245. else mask.b = maskLight;
  246. }
  247. return mask;
  248. }
  249. void main() {
  250. FragColor = vec4(Bloom(vTexCoord)*bloomAmount, 1.0);
  251. }