diff options
author | 2016-01-25 22:02:24 +0100 | |
---|---|---|
committer | 2016-01-25 22:02:24 +0100 | |
commit | d516871e6fcbde11863b927de75a042e8b8ce3e9 (patch) | |
tree | 221386c2586da4b6b080a6593de33baa811171ee /hlsl | |
parent | ded9493cb097ef7e19aa5eb3136fab819b360a24 (diff) |
NTSC Refactoring and Options
- merged YIQ encode and decode pass into one NTSC pass
- added options for most NTSC settings
- reduced sample count to 64
- changed default O value to 0
- fit NTSC signal jitter between a reasonable limit of 0 and 1
- fit A and B value between a reasonable limit of -1 and 1
- fit scanline jitter between a reasonable limit of 0 and 1
- added hum bar simulation based on [MooglyGuy's] GLSL port of the mame
shader pipeline
- added monochrome-chessboard.png
- added slot-mask-aligned.png (to simulate a TFT LCD)
Diffstat (limited to 'hlsl')
-rw-r--r-- | hlsl/artwork_support/post.fx | 30 | ||||
-rw-r--r-- | hlsl/ntsc.fx (renamed from hlsl/yiq_decode.fx) | 142 | ||||
-rw-r--r-- | hlsl/post.fx | 30 | ||||
-rw-r--r-- | hlsl/yiq_encode.fx | 151 |
4 files changed, 134 insertions, 219 deletions
diff --git a/hlsl/artwork_support/post.fx b/hlsl/artwork_support/post.fx index 8da6dabf1ab..f0c883fa69e 100644 --- a/hlsl/artwork_support/post.fx +++ b/hlsl/artwork_support/post.fx @@ -151,6 +151,11 @@ VS_OUTPUT vs_main(VS_INPUT Input) // Scanline, Shadowmask & Distortion Pixel Shader //----------------------------------------------------------------------------- +uniform float HumBarHertzRate = 60.0f / 59.94f - 1.0f; // difference between the 59.94 Hz field rate and 60 Hz line frequency (NTSC) +uniform float HumBarAlpha = 0.0f; + +uniform float TimeMilliseconds = 0.0f; + uniform float2 ScreenScale = float2(1.0f, 1.0f); uniform float2 ScreenOffset = float2(0.0f, 0.0f); @@ -396,7 +401,7 @@ float4 ps_main(PS_INPUT Input) : COLOR } // Mask Simulation (may not affect bloom) - if (!PrepareBloom) + if (!PrepareBloom && ShadowAlpha > 0.0f) { float2 shadowDims = ShadowDims; shadowDims = SwapXY @@ -455,15 +460,24 @@ float4 ps_main(PS_INPUT Input) : COLOR // Scanline Simulation (may not affect bloom) if (!PrepareBloom) { - // Scanline Simulation (disabled for vector) - if (!PrepareVector) + // Scanline Simulation (may not affect vector screen) + if (!PrepareVector && ScanlineAlpha > 0.0f) { - float InnerSine = BaseCoord.y * ScanlineScale * SourceDims.y; - float ScanJitter = ScanlineOffset * SourceDims.y; - float ScanBrightMod = sin(InnerSine * PI + ScanJitter); - float3 ScanColor = lerp(1.0f, (pow(ScanBrightMod * ScanBrightMod, ScanlineHeight) * ScanlineBrightScale + 1.0f + ScanlineBrightOffset) * 0.5f, ScanlineAlpha); + float ScanCoord = BaseCoord.y * SourceDims.y * ScanlineScale * PI; + float ScanCoordJitter = ScanlineOffset * PHI; + float ScanSine = sin(ScanCoord + ScanCoordJitter); + float ScanSineScaled = pow(ScanSine * ScanSine, ScanlineHeight); + float ScanBrightness = ScanSineScaled * ScanlineBrightScale + 1.0f + ScanlineBrightOffset; - BaseColor.rgb *= ScanColor; + BaseColor.rgb *= lerp(1.0f, ScanBrightness * 0.5f, ScanlineAlpha); + } + + // Hum Bar Simulation (may not affect vector screen) + if (!PrepareVector && HumBarAlpha > 0.0f) + { + float HumTimeStep = frac(TimeMilliseconds * HumBarHertzRate); + float HumBrightness = 1.0 - frac(BaseCoord.y / SourceRect.y + HumTimeStep) * HumBarAlpha; + BaseColor.rgb *= HumBrightness; } } diff --git a/hlsl/yiq_decode.fx b/hlsl/ntsc.fx index 288d00b2312..ed07e37a4cc 100644 --- a/hlsl/yiq_decode.fx +++ b/hlsl/ntsc.fx @@ -1,26 +1,13 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz,ImJezze //----------------------------------------------------------------------------- -// YIQ Decode Effect +// NTSC Effect //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Sampler Definitions //----------------------------------------------------------------------------- -texture Composite; - -sampler CompositeSampler = sampler_state -{ - Texture = <Composite>; - MipFilter = POINT; - MinFilter = POINT; - MagFilter = POINT; - AddressU = CLAMP; - AddressV = CLAMP; - AddressW = CLAMP; -}; - texture Diffuse; sampler DiffuseSampler = sampler_state @@ -81,51 +68,90 @@ VS_OUTPUT vs_main(VS_INPUT Input) } //----------------------------------------------------------------------------- -// YIQ Decode Pixel Shader +// NTSC Pixel Shader //----------------------------------------------------------------------------- uniform float AValue = 0.5f; uniform float BValue = 0.5f; -uniform float CCValue = 3.5975454f; +uniform float CCValue = 3.5795454f; uniform float OValue = 0.0f; -uniform float PValue = 1.0f; // unused - +uniform float PValue = 1.0f; uniform float ScanTime = 52.6f; -uniform float FrameOffset = 0.0f; uniform float NotchHalfWidth = 1.0f; uniform float YFreqResponse = 6.0f; uniform float IFreqResponse = 1.2f; uniform float QFreqResponse = 0.6f; +uniform float SignalOffset = 0.0f; + //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- -static const float4 NotchOffset = float4(0.0f, 1.0f, 2.0f, 3.0f); - static const float PI = 3.1415927f; -static const float PI2 = 6.2831855f; +static const float PI2 = PI * 2.0f; -static const float MaxC = 2.1183f; -static const float MinC = -1.1183f; -static const float CRange = 3.2366f; +static const float4 YDot = float4(0.299f, 0.587f, 0.114f, 0.0f); +static const float4 IDot = float4(0.595716f, -0.274453f, -0.321263f, 0.0f); +static const float4 QDot = float4(0.211456f, -0.522591f, 0.311135f, 0.0f); -float4 ps_main(PS_INPUT Input) : COLOR +static const float3 RDot = float3(1.0f, 0.956f, 0.621f); +static const float3 GDot = float3(1.0f, -0.272f, -0.647f); +static const float3 BDot = float3(1.0f, -1.106f, 1.703f); + +static const float4 OffsetX = float4(0.0f, 0.25f, 0.50f, 0.75f); +static const float4 NotchOffset = float4(0.0f, 1.0f, 2.0f, 3.0f); + +static const int SampleCount = 64; +static const int HalfSampleCount = SampleCount / 2; + +float4 GetCompositeYIQ(float2 TexCoord) { - float4 BaseTexel = tex2D(DiffuseSampler, Input.TexCoord.xy); + float2 SourceTexelDims = 1.0f / SourceDims; + float2 SourceRes = SourceDims * SourceRect; - float2 InvDims = 1.0f / SourceDims; + float2 PValueSourceTexel = float2(PValue, 0.0f) * SourceTexelDims; - // YIQ convolution: N coefficients each - float4 YAccum = 0.0f; - float4 IAccum = 0.0f; - float4 QAccum = 0.0f; + float2 C0 = TexCoord + PValueSourceTexel * OffsetX.x; + float2 C1 = TexCoord + PValueSourceTexel * OffsetX.y; + float2 C2 = TexCoord + PValueSourceTexel * OffsetX.z; + float2 C3 = TexCoord + PValueSourceTexel * OffsetX.w; + float4 Cx = float4(C0.x, C1.x, C2.x, C3.x); + float4 Cy = float4(C0.y, C1.y, C2.y, C3.y); + float4 Texel0 = tex2D(DiffuseSampler, C0); + float4 Texel1 = tex2D(DiffuseSampler, C1); + float4 Texel2 = tex2D(DiffuseSampler, C2); + float4 Texel3 = tex2D(DiffuseSampler, C3); + + float4 HPosition = Cx / SourceRect.x; + float4 VPosition = Cy / SourceRect.y; + + float4 Y = float4(dot(Texel0, YDot), dot(Texel1, YDot), dot(Texel2, YDot), dot(Texel3, YDot)); + float4 I = float4(dot(Texel0, IDot), dot(Texel1, IDot), dot(Texel2, IDot), dot(Texel3, IDot)); + float4 Q = float4(dot(Texel0, QDot), dot(Texel1, QDot), dot(Texel2, QDot), dot(Texel3, QDot)); + + float4 W = PI2 * CCValue * ScanTime; + + float4 T = HPosition + + (AValue / 360.0f * SourceRes.y) * VPosition + + (BValue / 360.0f) + + (SignalOffset / 360.0f); + float4 TW = T * W; + + float4 CompositeYIQ = Y + I * cos(TW) + Q * sin(TW); + + return CompositeYIQ; +} + +float4 ps_main(PS_INPUT Input) : COLOR +{ + float4 BaseTexel = tex2D(DiffuseSampler, Input.TexCoord); - float BValueFrameOffset = BValue * FrameOffset; + float2 SourceTexelDims = 1.0f / SourceDims; + float2 SourceRes = SourceDims * SourceRect; - float FrameWidthx4 = SourceDims.x * SourceRect.x * 4.0f; - float TimePerSample = ScanTime / FrameWidthx4; + float TimePerSample = ScanTime / (SourceRes.x * 4.0f); float Fc_y1 = (CCValue - NotchHalfWidth) * TimePerSample; float Fc_y2 = (CCValue + NotchHalfWidth) * TimePerSample; @@ -142,24 +168,34 @@ float4 ps_main(PS_INPUT Input) : COLOR float Fc_y1_pi2 = Fc_y1 * PI2; float Fc_y2_pi2 = Fc_y2 * PI2; float Fc_y3_pi2 = Fc_y3 * PI2; - float PI2Length = PI2 / 82.0f; + float PI2Length = PI2 / SampleCount; float W = PI2 * CCValue * ScanTime; + + float4 YAccum = 0.0f; + float4 IAccum = 0.0f; + float4 QAccum = 0.0f; float4 Cy = Input.TexCoord.y; - float4 VPosition = Cy * (SourceDims.y * SourceRect.y) * 2.0f; + float4 VPosition = Cy / SourceRect.y; - for(float n = -41.0f; n < 42.0f; n += 4.0f) + for (float i = 0; i < SampleCount; i += 4.0f) { + float n = i - HalfSampleCount; + float4 n4 = n + NotchOffset; - float4 Cx = Input.TexCoord.x + InvDims.x * n4 * 0.25f; - float4 HPosition = (Cx / SourceRect.x); + float4 Cx = Input.TexCoord.x + SourceTexelDims.x * (n4 * 0.25f); + float4 HPosition = Cx / SourceRect.x; - float4 C = tex2D(CompositeSampler, float2(Cx.r, Cy.r)) * CRange + MinC; + float4 C = GetCompositeYIQ(float2(Cx.r, Cy.r)); - float4 T = HPosition + AValue * VPosition + BValueFrameOffset; - float4 WT = W * T + OValue; + float4 T = HPosition + + (AValue / 360.0f * SourceRes.y) * VPosition + + (BValue / 360.0f) + + (SignalOffset / 360.0f); + float4 WT = W * T + + OValue; float4 SincKernel = 0.54f + 0.46f * cos(PI2Length * n4); @@ -186,21 +222,21 @@ float4 ps_main(PS_INPUT Input) : COLOR QAccum = QAccum + C * sin(WT) * FilterQ; } - float Y = YAccum.r + YAccum.g + YAccum.b + YAccum.a; - float I = (IAccum.r + IAccum.g + IAccum.b + IAccum.a) * 2.0f; - float Q = (QAccum.r + QAccum.g + QAccum.b + QAccum.a) * 2.0f; + float3 YIQ = float3( + (YAccum.r + YAccum.g + YAccum.b + YAccum.a), + (IAccum.r + IAccum.g + IAccum.b + IAccum.a) * 2.0f, + (QAccum.r + QAccum.g + QAccum.b + QAccum.a) * 2.0f); - float3 YIQ = float3(Y, I, Q); + float3 RGB = float3( + dot(YIQ, RDot), + dot(YIQ, GDot), + dot(YIQ, BDot)); - float3 Decode = float3( - dot(YIQ, float3(1.0f, 0.956f, 0.621f)), - dot(YIQ, float3(1.0f, -0.272f, -0.647f)), - dot(YIQ, float3(1.0f, -1.106f, 1.703f))); - return float4(Decode, BaseTexel.a); + return float4(RGB, BaseTexel.a); } //----------------------------------------------------------------------------- -// YIQ Decode Technique +// NTSC Technique //----------------------------------------------------------------------------- technique DefaultTechnique diff --git a/hlsl/post.fx b/hlsl/post.fx index 4415298a2f8..deec349e2fc 100644 --- a/hlsl/post.fx +++ b/hlsl/post.fx @@ -65,6 +65,7 @@ struct PS_INPUT //----------------------------------------------------------------------------- static const float PI = 3.1415927f; +static const float PHI = 1.618034f; //----------------------------------------------------------------------------- // Scanline & Shadowmask Vertex Shader @@ -118,6 +119,11 @@ VS_OUTPUT vs_main(VS_INPUT Input) // Scanline & Shadowmask Pixel Shader //----------------------------------------------------------------------------- +uniform float HumBarHertzRate = 60.0f / 59.94f - 1.0f; // difference between the 59.94 Hz field rate and 60 Hz line frequency (NTSC) +uniform float HumBarAlpha = 0.0f; + +uniform float TimeMilliseconds = 0.0f; + uniform float2 ScreenScale = float2(1.0f, 1.0f); uniform float2 ScreenOffset = float2(0.0f, 0.0f); @@ -159,6 +165,7 @@ float4 ps_main(PS_INPUT Input) : COLOR { float2 ScreenTexelDims = 1.0f / ScreenDims; float2 SourceTexelDims = 1.0f / SourceDims; + float2 SourceRes = SourceDims * SourceRect; float2 HalfSourceRect = SourceRect * 0.5f; @@ -175,7 +182,7 @@ float4 ps_main(PS_INPUT Input) : COLOR } // Mask Simulation (may not affect bloom) - if (!PrepareBloom) + if (!PrepareBloom && ShadowAlpha > 0.0f) { float2 shadowDims = ShadowDims; shadowDims = SwapXY @@ -235,14 +242,23 @@ float4 ps_main(PS_INPUT Input) : COLOR if (!PrepareBloom) { // Scanline Simulation (may not affect vector screen) - if (!PrepareVector) + if (!PrepareVector && ScanlineAlpha > 0.0f) { - float InnerSine = BaseCoord.y * ScanlineScale * SourceDims.y; - float ScanJitter = ScanlineOffset * SourceDims.y; - float ScanBrightMod = sin(InnerSine * PI + ScanJitter); - float3 ScanColor = lerp(1.0f, (pow(ScanBrightMod * ScanBrightMod, ScanlineHeight) * ScanlineBrightScale + 1.0f + ScanlineBrightOffset) * 0.5f, ScanlineAlpha); + float ScanCoord = BaseCoord.y * SourceDims.y * ScanlineScale * PI; + float ScanCoordJitter = ScanlineOffset * PHI; + float ScanSine = sin(ScanCoord + ScanCoordJitter); + float ScanSineScaled = pow(ScanSine * ScanSine, ScanlineHeight); + float ScanBrightness = ScanSineScaled * ScanlineBrightScale + 1.0f + ScanlineBrightOffset; - BaseColor.rgb *= ScanColor; + BaseColor.rgb *= lerp(1.0f, ScanBrightness * 0.5f, ScanlineAlpha); + } + + // Hum Bar Simulation (may not affect vector screen) + if (!PrepareVector && HumBarAlpha > 0.0f) + { + float HumTimeStep = frac(TimeMilliseconds * HumBarHertzRate); + float HumBrightness = 1.0 - frac(BaseCoord.y / SourceRect.y + HumTimeStep) * HumBarAlpha; + BaseColor.rgb *= HumBrightness; } } diff --git a/hlsl/yiq_encode.fx b/hlsl/yiq_encode.fx deleted file mode 100644 index c0db1cf531d..00000000000 --- a/hlsl/yiq_encode.fx +++ /dev/null @@ -1,151 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ryan Holtz,ImJezze -//----------------------------------------------------------------------------- -// YIQ Encode Effect -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Sampler Definitions -//----------------------------------------------------------------------------- - -texture Diffuse; - -sampler DiffuseSampler = sampler_state -{ - Texture = <Diffuse>; - MipFilter = LINEAR; - MinFilter = LINEAR; - MagFilter = LINEAR; - AddressU = CLAMP; - AddressV = CLAMP; - AddressW = CLAMP; -}; - -//----------------------------------------------------------------------------- -// Vertex Definitions -//----------------------------------------------------------------------------- - -struct VS_OUTPUT -{ - float4 Position : POSITION; - float4 Color : COLOR0; - float2 TexCoord : TEXCOORD0; -}; - -struct VS_INPUT -{ - float4 Position : POSITION; - float4 Color : COLOR0; - float2 TexCoord : TEXCOORD0; -}; - -struct PS_INPUT -{ - float4 Color : COLOR0; - float2 TexCoord : TEXCOORD0; -}; - -//----------------------------------------------------------------------------- -// YIQ Encode Vertex Shader -//----------------------------------------------------------------------------- - -uniform float2 ScreenDims; -uniform float2 SourceDims; -uniform float2 SourceRect; - -VS_OUTPUT vs_main(VS_INPUT Input) -{ - VS_OUTPUT Output = (VS_OUTPUT)0; - - Output.Position = float4(Input.Position.xyz, 1.0f); - Output.Position.xy /= ScreenDims; - Output.Position.y = 1.0f - Output.Position.y; // flip y - Output.Position.xy -= 0.5f; // center - Output.Position.xy *= 2.0f; // zoom - - Output.TexCoord = Input.TexCoord; - Output.TexCoord += 0.5f / SourceDims; // half texel offset correction (DX9) - - Output.Color = Input.Color; - - return Output; -} - -//----------------------------------------------------------------------------- -// YIQ Encode Pixel Shader -//----------------------------------------------------------------------------- - -uniform float AValue = 0.5f; -uniform float BValue = 0.5f; -uniform float CCValue = 3.5975454f; -uniform float OValue = 0.0f; -uniform float PValue = 1.0f; - -uniform float ScanTime = 52.6f; -uniform float FrameOffset = 0.0f; - -uniform float MaxC = 2.1183f; -uniform float MinC = -1.1183f; -uniform float CRange = 3.2366f; - -//----------------------------------------------------------------------------- -// Constants -//----------------------------------------------------------------------------- - -static const float4 YDot = float4(0.299f, 0.587f, 0.114f, 0.0f); -static const float4 IDot = float4(0.595716f, -0.274453f, -0.321263f, 0.0f); -static const float4 QDot = float4(0.211456f, -0.522591f, 0.311135f, 0.0f); -static const float4 OffsetX = float4(0.0f, 0.25f, 0.50f, 0.75f); - -static const float PI = 3.1415927f; -static const float PI2 = 6.2831855f; - -float4 ps_main(PS_INPUT Input) : COLOR -{ - float2 InvDims = 1.0f / SourceDims; - - float2 InvPValue = float2(PValue, 0.0f) * InvDims; - - float2 C0 = Input.TexCoord + InvPValue * OffsetX.x; - float2 C1 = Input.TexCoord + InvPValue * OffsetX.y; - float2 C2 = Input.TexCoord + InvPValue * OffsetX.z; - float2 C3 = Input.TexCoord + InvPValue * OffsetX.w; - float4 Cx = float4(C0.x, C1.x, C2.x, C3.x); - float4 Cy = float4(C0.y, C1.y, C2.y, C3.y); - float4 Texel0 = tex2D(DiffuseSampler, C0); - float4 Texel1 = tex2D(DiffuseSampler, C1); - float4 Texel2 = tex2D(DiffuseSampler, C2); - float4 Texel3 = tex2D(DiffuseSampler, C3); - - float4 Y = float4(dot(Texel0, YDot), dot(Texel1, YDot), dot(Texel2, YDot), dot(Texel3, YDot)); - float4 I = float4(dot(Texel0, IDot), dot(Texel1, IDot), dot(Texel2, IDot), dot(Texel3, IDot)); - float4 Q = float4(dot(Texel0, QDot), dot(Texel1, QDot), dot(Texel2, QDot), dot(Texel3, QDot)); - - float BValueFrameOffset = BValue * FrameOffset; - - float4 HPosition = Cx / SourceRect.x; - float4 VPosition = Cy * (SourceDims.y * SourceRect.y) * 2.0f; - - float4 W = PI2 * CCValue * ScanTime; - float4 T = HPosition + AValue * VPosition + BValueFrameOffset; - float4 TW = T * W + OValue; - - float4 Encoded = Y + I * cos(TW) + Q * sin(TW); - - return (Encoded - MinC) / CRange;; -} - -//----------------------------------------------------------------------------- -// YIQ Encode Technique -//----------------------------------------------------------------------------- - -technique DefaultTechnique -{ - pass Pass0 - { - Lighting = FALSE; - - VertexShader = compile vs_3_0 vs_main(); - PixelShader = compile ps_3_0 ps_main(); - } -} |