mame-postproc.fs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #version 150
  2. // This is a port of the NTSC encode/decode shader pair in MAME and MESS, modified to use only
  3. // one pass rather than an encode pass and a decode pass. It accurately emulates the sort of
  4. // signal decimation one would see when viewing a composite signal, though it could benefit from a
  5. // pre-pass to re-size the input content to more accurately reflect the actual size that would
  6. // be incoming from a composite signal source.
  7. //
  8. // To encode the composite signal, I convert the RGB value to YIQ, then subsequently evaluate
  9. // the standard NTSC composite equation. Four composite samples per RGB pixel are generated from
  10. // the incoming linearly-interpolated texels.
  11. //
  12. // The decode pass implements a Fixed Impulse Response (FIR) filter designed by MAME/MESS contributor
  13. // "austere" in matlab (if memory serves correctly) to mimic the behavior of a standard television set
  14. // as closely as possible. The filter window is 83 composite samples wide, and there is an additional
  15. // notch filter pass on the luminance (Y) values in order to strip the color signal from the luminance
  16. // signal prior to processing.
  17. //
  18. // Yes, this code could greatly use some cleaning up.
  19. // ported from UltraMoogleMan's "Full MAME/MESS Shader Pipe" shadertoy: https://www.shadertoy.com/view/ldf3Rf
  20. // license: presumably MAME's license at the time, which was noncommercial
  21. #define scanlines 1.0
  22. #define scandark 0.175
  23. #define deconverge 0.0
  24. #define pincushion 0.0
  25. #define hertzroll 0.0
  26. // Useful Constants
  27. const vec4 Zero = vec4(0.0);
  28. const vec4 Half = vec4(0.5);
  29. const vec4 One = vec4(1.0);
  30. const vec4 Two = vec4(2.0);
  31. const vec3 Gray = vec3(0.3, 0.59, 0.11);
  32. const float Pi = 3.1415926535;
  33. const float Pi2 = 6.283185307;
  34. // NTSC Constants
  35. const vec4 A = vec4(0.5);
  36. const vec4 A2 = vec4(1.0);
  37. const vec4 B = vec4(0.5);
  38. const float P = 1.0;
  39. const float CCFrequency = 3.59754545;
  40. const float NotchUpperFrequency = 5.59754545; //3.59754545 + 2.0;
  41. const float NotchLowerFrequency = 1.59754545; //3.59754545 - 2.0;
  42. const float YFrequency = 6.0;
  43. const float IFrequency = 1.2;
  44. const float QFrequency = 0.6;
  45. const float NotchHalfWidth = 2.0;
  46. const float ScanTime = 52.6;
  47. const float Pi2ScanTime = 330.4955471482;// 6.283185307 * 52.6;
  48. const float MaxC = 2.1183;
  49. const vec4 YTransform = vec4(0.299, 0.587, 0.114, 0.0);
  50. const vec4 ITransform = vec4(0.595716, -0.274453, -0.321263, 0.0);
  51. const vec4 QTransform = vec4(0.211456, -0.522591, 0.311135, 0.0);
  52. const vec3 YIQ2R = vec3(1.0, 0.956, 0.621);
  53. const vec3 YIQ2G = vec3(1.0, -0.272, -0.647);
  54. const vec3 YIQ2B = vec3(1.0, -1.106, 1.703);
  55. const vec4 MinC = vec4(-1.1183);
  56. const vec4 CRange = vec4(3.2366);
  57. const vec4 InvCRange = vec4(1.0/3.2366);
  58. const float Pi2Length = Pi2 / 63.0;
  59. const vec4 NotchOffset = vec4(0.0, 1.0, 2.0, 3.0);
  60. vec4 W = vec4(Pi2 * CCFrequency * ScanTime);
  61. // Color Convolution Constants
  62. const vec3 RedMatrix = vec3(1.0, 0.0, 0.0);
  63. const vec3 GrnMatrix = vec3(0.0, 1.0, 0.0);
  64. const vec3 BluMatrix = vec3(0.0, 0.0, 1.0);
  65. const vec3 DCOffset = vec3(0.0, 0.0, 0.0);
  66. const vec3 ColorScale = vec3(0.95, 0.95, 0.95);
  67. const float Saturation = 1.4;
  68. // Deconverge Constants
  69. const vec3 ConvergeX = vec3(-0.4, 0.0, 0.2);
  70. const vec3 ConvergeY = vec3( 0.0, -0.4, 0.2);
  71. const vec3 RadialConvergeX = vec3(1.0, 1.0, 1.0);
  72. const vec3 RadialConvergeY = vec3(1.0, 1.0, 1.0);
  73. // Scanline/Pincushion Constants
  74. const float PincushionAmount = 0.015;
  75. const float CurvatureAmount = 0.015;
  76. //const float ScanlineAmount = 0.175; <- move to parameter
  77. const float ScanlineScale = 1.0;
  78. const float ScanlineHeight = 1.0;
  79. const float ScanlineBrightScale = 1.0;
  80. const float ScanlineBrightOffset = 0.0;
  81. const float ScanlineOffset = 0.0;
  82. const vec3 Floor = vec3(0.05, 0.05, 0.05);
  83. // 60Hz Bar Constants
  84. const float SixtyHertzRate = (60.0 / 59.97 - 1.0); // Difference between NTSC and line frequency
  85. const float SixtyHertzScale = 0.1;
  86. uniform sampler2D source[];
  87. uniform vec4 sourceSize[];
  88. uniform vec4 targetSize;
  89. uniform int phase;
  90. in Vertex {
  91. vec2 vTexCoord;
  92. };
  93. out vec4 FragColor;
  94. vec4 ColorConvolution(vec2 UV, vec2 InverseRes)
  95. {
  96. vec3 InPixel = texture(source[0], UV).rgb;
  97. // Color Matrix
  98. float RedValue = dot(InPixel, RedMatrix);
  99. float GrnValue = dot(InPixel, GrnMatrix);
  100. float BluValue = dot(InPixel, BluMatrix);
  101. vec3 OutColor = vec3(RedValue, GrnValue, BluValue);
  102. // DC Offset & Scale
  103. OutColor = (OutColor * ColorScale) + DCOffset;
  104. // Saturation
  105. float Luma = dot(OutColor, Gray);
  106. vec3 Chroma = OutColor - Luma;
  107. OutColor = (Chroma * Saturation) + Luma;
  108. return vec4(OutColor, 1.0);
  109. }
  110. vec4 Deconverge(vec2 UV)
  111. {
  112. vec2 InverseRes = 1.0 / sourceSize[0].xy;
  113. vec2 InverseSrcRes = 1.0 / sourceSize[0].xy;
  114. vec3 CoordX = UV.x * RadialConvergeX;
  115. vec3 CoordY = UV.y * RadialConvergeY;
  116. CoordX += ConvergeX * InverseRes.x - (RadialConvergeX - 1.0) * 0.5;
  117. CoordY += ConvergeY * InverseRes.y - (RadialConvergeY - 1.0) * 0.5;
  118. float RedValue = ColorConvolution(vec2(CoordX.x, CoordY.x), InverseSrcRes).r;
  119. float GrnValue = ColorConvolution(vec2(CoordX.y, CoordY.y), InverseSrcRes).g;
  120. float BluValue = ColorConvolution(vec2(CoordX.z, CoordY.z), InverseSrcRes).b;
  121. if (deconverge > 0.5) return vec4(RedValue, GrnValue, BluValue, 1.0);
  122. else return vec4(texture(source[0], UV));
  123. }
  124. vec4 ScanlinePincushion(vec2 UV)
  125. {
  126. vec4 InTexel = Deconverge(UV);
  127. vec2 PinUnitCoord = UV * Two.xy - One.xy;
  128. float PincushionR2 = pow(length(PinUnitCoord), 2.0);
  129. vec2 PincushionCurve = PinUnitCoord * PincushionAmount * PincushionR2;
  130. vec2 BaseCoord = UV;
  131. vec2 ScanCoord = UV;
  132. BaseCoord *= One.xy - PincushionAmount * 0.2; // Warning: Magic constant
  133. BaseCoord += PincushionAmount * 0.1;
  134. BaseCoord += PincushionCurve;
  135. ScanCoord *= One.xy - PincushionAmount * 0.2; // Warning: Magic constant
  136. ScanCoord += PincushionAmount * 0.1;
  137. ScanCoord += PincushionCurve;
  138. vec2 CurveClipUnitCoord = UV * Two.xy - One.xy;
  139. float CurvatureClipR2 = pow(length(CurveClipUnitCoord), 2.0);
  140. vec2 CurvatureClipCurve = CurveClipUnitCoord * CurvatureAmount * CurvatureClipR2;
  141. vec2 ScreenClipCoord = UV;
  142. ScreenClipCoord -= Half.xy;
  143. ScreenClipCoord *= One.xy - CurvatureAmount * 0.2; // Warning: Magic constant
  144. ScreenClipCoord += Half.xy;
  145. ScreenClipCoord += CurvatureClipCurve;
  146. if (pincushion > 0.5){
  147. // -- Alpha Clipping --
  148. if (BaseCoord.x < 0.0) return vec4(0.0, 0.0, 0.0, 1.0);
  149. if (BaseCoord.y < 0.0) return vec4(0.0, 0.0, 0.0, 1.0);
  150. if (BaseCoord.x > 1.0) return vec4(0.0, 0.0, 0.0, 1.0);
  151. if (BaseCoord.y > 1.0) return vec4(0.0, 0.0, 0.0, 1.0);
  152. }
  153. // -- Scanline Simulation --
  154. float InnerSine = ScanCoord.y * sourceSize[0].y * ScanlineScale;
  155. float ScanBrightMod = sin(InnerSine * Pi + ScanlineOffset * sourceSize[0].y);
  156. float ScanBrightness = mix(1.0, (pow(ScanBrightMod * ScanBrightMod, ScanlineHeight) * ScanlineBrightScale + 1.0) * 0.5, scandark);
  157. vec3 ScanlineTexel = InTexel.rgb * ScanBrightness;
  158. // -- Color Compression (increasing the floor of the signal without affecting the ceiling) --
  159. ScanlineTexel = Floor + (One.xyz - Floor) * ScanlineTexel;
  160. if (scanlines > 0.5) return vec4(ScanlineTexel, 1.0);
  161. else return vec4(InTexel);
  162. }
  163. vec4 SixtyHertz(vec2 UV)
  164. {
  165. vec4 InPixel = ScanlinePincushion(UV);
  166. float Milliseconds = float(phase) * 15.0;
  167. float TimeStep = fract(Milliseconds * SixtyHertzRate);
  168. float BarPosition = 1.0 - fract(-UV.y + TimeStep) * SixtyHertzScale;
  169. vec4 OutPixel = InPixel * BarPosition;
  170. if (hertzroll > 0.5) return OutPixel;
  171. else return InPixel;
  172. }
  173. void main() {
  174. vec4 OutPixel = SixtyHertz(vTexCoord.xy);
  175. FragColor = OutPixel;
  176. }