diff --git a/hlsl/artwork_support/distortion.fx b/hlsl/artwork_support/distortion.fx new file mode 100644 index 00000000000..65896bceb96 --- /dev/null +++ b/hlsl/artwork_support/distortion.fx @@ -0,0 +1,97 @@ +// license:BSD-3-Clause +// copyright-holders:ImJezze +//----------------------------------------------------------------------------- +// Distortion Effect +//----------------------------------------------------------------------------- + +texture DiffuseTexture; + +sampler DiffuseSampler = sampler_state +{ + Texture = ; + MipFilter = LINEAR; + MinFilter = LINEAR; + MagFilter = LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + AddressW = CLAMP; +}; + +//----------------------------------------------------------------------------- +// Vertex Definitions +//----------------------------------------------------------------------------- + +struct VS_INPUT +{ + float4 Position : POSITION; + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 Position : POSITION; + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; +}; + +struct PS_INPUT +{ + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; +}; + +//----------------------------------------------------------------------------- +// Distortion Vertex Shader +//----------------------------------------------------------------------------- + +uniform float2 ScreenDims; // size of the window or fullscreen +uniform float2 TargetDims; + +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.Color = Input.Color; + + Output.TexCoord = Input.Position.xy / ScreenDims; + Output.TexCoord += 0.5f / TargetDims; // half texel offset correction (DX9) + + return Output; +} + +//----------------------------------------------------------------------------- +// Post-Processing Pixel Shader +//----------------------------------------------------------------------------- + +float4 ps_main(PS_INPUT Input) : COLOR +{ + float2 BaseCoord = Input.TexCoord; + + // Color + float4 BaseColor = tex2D(DiffuseSampler, BaseCoord); + BaseColor.a = 1.0f; + + return BaseColor; +} + +//----------------------------------------------------------------------------- +// Distortion Effect +//----------------------------------------------------------------------------- + +technique DistortionTechnique +{ + pass Pass0 + { + Lighting = FALSE; + + VertexShader = compile vs_3_0 vs_main(); + PixelShader = compile ps_3_0 ps_main(); + } +} \ No newline at end of file diff --git a/hlsl/artwork_support/post.fx b/hlsl/artwork_support/post.fx new file mode 100644 index 00000000000..ed6c7ce51aa --- /dev/null +++ b/hlsl/artwork_support/post.fx @@ -0,0 +1,471 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz,ImJezze +//----------------------------------------------------------------------------- +// Scanline & Shadowmask Effect +//----------------------------------------------------------------------------- + +texture DiffuseTexture; + +sampler DiffuseSampler = sampler_state +{ + Texture = ; + MipFilter = LINEAR; + MinFilter = LINEAR; + MagFilter = LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + AddressW = CLAMP; +}; + +texture ShadowTexture; + +sampler ShadowSampler = sampler_state +{ + Texture = ; + MipFilter = LINEAR; + MinFilter = LINEAR; + MagFilter = LINEAR; + AddressU = WRAP; + AddressV = WRAP; + AddressW = WRAP; +}; + +//----------------------------------------------------------------------------- +// Vertex Definitions +//----------------------------------------------------------------------------- + +struct VS_INPUT +{ + float4 Position : POSITION; + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; +}; + +struct VS_OUTPUT +{ + float4 Position : POSITION; + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; + float2 ScreenCoord : TEXCOORD1; +}; + +struct PS_INPUT +{ + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; + float2 ScreenCoord : TEXCOORD1; +}; + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +static const float Epsilon = 1.0e-7f; +static const float PI = 3.1415927f; +static const float E = 2.7182817f; +static const float Gelfond = 23.140692f; // e^pi (Gelfond constant) +static const float GelfondSchneider = 2.6651442f; // 2^sqrt(2) (Gelfond-Schneider constant) + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +bool xor(bool a, bool b) +{ + return (a || b) && !(a && b); +} + +// www.stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader/ +float random(float2 seed) +{ + // irrationals for pseudo randomness + float2 i = float2(Gelfond, GelfondSchneider); + + return frac(cos(dot(seed, i)) * 123456.0f); +} + +// www.dinodini.wordpress.com/2010/04/05/normalized-tunable-sigmoid-functions/ +float normalizedSigmoid(float n, float k) +{ + // valid for n and k in range of -1.0 and 1.0 + return (n - n * k) / (k - abs(n) * 2.0f * k + 1); +} + +// www.iquilezles.org/www/articles/distfunctions/distfunctions.htm +float roundBox(float2 p, float2 b, float r) +{ + return length(max(abs(p) - b + r, 0.0f)) - r; +} + +//----------------------------------------------------------------------------- +// Scanline & Shadowmask Vertex Shader +//----------------------------------------------------------------------------- + +uniform float2 ScreenDims; // size of the window or fullscreen +uniform float2 SourceDims; // size of the texture in power-of-two size +uniform float2 SourceRect; // size of the uv rectangle +uniform float2 TargetDims; // size of the target surface +uniform float2 QuadDims; // size of the screen quad + +uniform float2 ShadowDims = float2(32.0f, 32.0f); // size of the shadow texture (extended to power-of-two size) +uniform float2 ShadowUVOffset = float2(0.0f, 0.0f); + +uniform bool OrientationSwapXY = false; // false landscape, true portrait for default screen orientation +uniform bool RotationSwapXY = false; // swapped default screen orientation due to screen rotation + +uniform bool PrepareBloom = false; // disables some effects for rendering bloom textures +uniform bool PrepareVector = false; + +VS_OUTPUT vs_main(VS_INPUT Input) +{ + VS_OUTPUT Output = (VS_OUTPUT)0; + + float2 shadowUVOffset = ShadowUVOffset; + shadowUVOffset = xor(OrientationSwapXY, RotationSwapXY) + ? shadowUVOffset.yx + : shadowUVOffset.xy; + + float2 ScreenCoordOffset = 0.0f; + ScreenCoordOffset += shadowUVOffset; + + Output.ScreenCoord = Input.Position.xy; + Output.ScreenCoord += ScreenCoordOffset; + + 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 = PrepareVector + ? Input.Position.xy / ScreenDims + : Input.TexCoord; + Output.TexCoord += 0.5f / TargetDims; // half texel offset correction (DX9) + + Output.Color = Input.Color; + + return Output; +} + +//----------------------------------------------------------------------------- +// Post-Processing Pixel Shader +//----------------------------------------------------------------------------- + +uniform float ScanlineAlpha = 1.0f; +uniform float ScanlineScale = 1.0f; +uniform float ScanlineBrightScale = 1.0f; +uniform float ScanlineBrightOffset = 1.0f; +uniform float ScanlineOffset = 1.0f; +uniform float ScanlineHeight = 1.0f; + +uniform float CurvatureAmount = 1.0f; +uniform float RoundCornerAmount = 0.0f; +uniform float SmoothBorderAmount = 0.0f; +uniform float VignettingAmount = 0.0f; +uniform float ReflectionAmount = 0.0f; + +uniform float ShadowAlpha = 0.0f; +uniform float2 ShadowCount = float2(6.0f, 6.0f); +uniform float2 ShadowUV = float2(0.25f, 0.25f); + +uniform float3 Power = float3(1.0f, 1.0f, 1.0f); +uniform float3 Floor = float3(0.0f, 0.0f, 0.0f); + +float2 GetRatioCorrection() +{ + if (PrepareVector) + { + float ScreenRatio = ScreenDims.x / ScreenDims.y; + float QuadRatio = QuadDims.x / QuadDims.y; + float ScreenQuadRatio = QuadRatio / ScreenRatio; + + return ScreenQuadRatio > 1.0f + ? float2(1.0, 1.0f / ScreenQuadRatio) + : float2(ScreenQuadRatio, 1.0); + } + else + { + return SourceRect; + } +} + +float GetNoiseFactor(float n, float random) +{ + // smaller n become more noisy + return 1.0f + random * max(0.0f, 0.25f * pow(E, -8 * n)); +} + +float GetVignetteFactor(float2 coord, float amount) +{ + float2 VignetteCoord = coord; + + float VignetteLength = length(VignetteCoord); + float VignetteBlur = (amount * 0.75f) + 0.25; + + // 0.5 full screen fitting circle + float VignetteRadius = 1.0f - (amount * 0.25f); + float Vignette = smoothstep(VignetteRadius, VignetteRadius - VignetteBlur, VignetteLength); + + return saturate(Vignette); +} + +float GetSpotAddend(float2 coord, float amount) +{ + float2 RatioCorrection = GetRatioCorrection(); + + // normalized screen canvas ratio + float2 CanvasRatio = PrepareVector + ? float2(1.0f, QuadDims.y / QuadDims.x) + : 1.0f / float2(1.0f, SourceRect.y / SourceRect.x); + + // upper right quadrant + float2 spotOffset = OrientationSwapXY + ? float2(0.25f, 0.25f) + : float2(-0.25f, 0.25f); + + float2 SpotCoord = coord; + SpotCoord += spotOffset * RatioCorrection; + SpotCoord *= CanvasRatio; + SpotCoord /= RatioCorrection; + + float SpotBlur = amount; + + // 0.5 full screen fitting circle + float SpotRadius = amount * 0.75f; + float Spot = smoothstep(SpotRadius, SpotRadius - SpotBlur, length(SpotCoord)); + + float SigmoidSpot = normalizedSigmoid(Spot, 0.75) * amount; + + // increase strength by 100% + SigmoidSpot = SigmoidSpot * 2.0f; + + return saturate(SigmoidSpot); +} + +float GetRoundCornerFactor(float2 coord, float radiusAmount, float smoothAmount) +{ + float2 RatioCorrection = GetRatioCorrection(); + + // reduce smooth amount down to radius amount + smoothAmount = min(smoothAmount, radiusAmount); + + float2 CanvasDims = PrepareVector + ? ScreenDims + : SourceDims; + + // hack: alignment correction + if (!PrepareVector) + { + float2 SourceArea = 1.0f / SourceRect; + float2 SourceTexelDims = 1.0f / CanvasDims; + + coord -= SourceTexelDims * SourceArea * 0.75; + coord *= SourceTexelDims * SourceArea * 0.25 + 1.0f; + } + + float range = min(CanvasDims.x, CanvasDims.y) * 0.5; + float radius = range * max(radiusAmount, 0.01f); + float smooth = 1.0 / (range * max(smoothAmount, 0.01f)); + + // compute box + float box = roundBox(CanvasDims * (coord * 2.0f), CanvasDims * RatioCorrection, radius); + + // apply smooth + box *= smooth; + box += 1.0f - pow(smooth * 0.5f, 0.5f); + + float border = smoothstep(1.0f, 0.0f, box); + + return saturate(border); +} + +// www.francois-tarlier.com/blog/cubic-lens-distortion-shader/ +float2 GetDistortedCoords(float2 centerCoord, float amount) +{ + amount *= 0.25f; // reduced amount + + // lens distortion coefficient + float k = amount; + + // cubic distortion value + float kcube = amount * 2.0f; + + // compute cubic distortion factor + float r2 = centerCoord.x * centerCoord.x + centerCoord.y * centerCoord.y; + float f = kcube == 0.0f + ? 1.0f + r2 * k + : 1.0f + r2 * (k + kcube * sqrt(r2)); + + // fit screen bounds + f /= 1.0f + amount * 0.5f; + + // apply cubic distortion factor + centerCoord *= f; + + return centerCoord; +} + +float2 GetCoords(float2 coord, float2 centerOffset, float distortionAmount) +{ + float2 RatioCorrection = GetRatioCorrection(); + + // center coordinates + coord -= centerOffset; + + // apply ratio difference between screen and quad + coord /= RatioCorrection; + + // distort coordinates + coord = GetDistortedCoords(coord, distortionAmount); + + // revert ratio difference between screen and quad + coord *= RatioCorrection; + + // un-center coordinates + coord += centerOffset; + + return coord; +} + +float4 ps_main(PS_INPUT Input) : COLOR +{ + float2 ScreenTexelDims = 1.0f / ScreenDims; + float2 SourceTexelDims = 1.0f / SourceDims; + + float2 HalfSourceRect = PrepareVector + ? float2(0.5f, 0.5f) + : SourceRect * 0.5f; + + float2 ScreenCoord = Input.ScreenCoord / ScreenDims; + ScreenCoord = GetCoords(ScreenCoord, float2(0.5f, 0.5f), CurvatureAmount); + + float2 BaseCoord = Input.TexCoord; + BaseCoord = GetCoords(BaseCoord, HalfSourceRect, CurvatureAmount); + + float2 BaseCoordCentered = BaseCoord; + BaseCoordCentered -= HalfSourceRect; + + float4 BaseColor = tex2D(DiffuseSampler, BaseCoord); + BaseColor.a = 1.0f; + + // Mask Simulation (may not affect bloom) + if (!PrepareBloom) + { + float2 shadowDims = ShadowDims; + shadowDims = xor(OrientationSwapXY, RotationSwapXY) + ? shadowDims.yx + : shadowDims.xy; + + float2 shadowUV = ShadowUV; + // shadowUV = xor(OrientationSwapXY, RotationSwapXY) + // ? shadowUV.yx + // : shadowUV.xy; + + float2 screenCoord = ScreenCoord; + screenCoord = xor(OrientationSwapXY, RotationSwapXY) + ? screenCoord.yx + : screenCoord.xy; + + float2 shadowCount = ShadowCount; + shadowCount = xor(OrientationSwapXY, RotationSwapXY) + ? shadowCount.yx + : shadowCount.xy; + + float2 shadowTile = (ScreenTexelDims * shadowCount); + shadowTile = xor(OrientationSwapXY, RotationSwapXY) + ? shadowTile.yx + : shadowTile.xy; + + float2 ShadowFrac = frac(screenCoord / shadowTile); + float2 ShadowCoord = (ShadowFrac * shadowUV); + ShadowCoord += 0.5f / shadowDims; // half texel offset + // ShadowCoord = xor(OrientationSwapXY, RotationSwapXY) + // ? ShadowCoord.yx + // : ShadowCoord.xy; + + float3 ShadowColor = tex2D(ShadowSampler, ShadowCoord).rgb; + ShadowColor = lerp(1.0f, ShadowColor, ShadowAlpha); + + BaseColor.rgb *= ShadowColor; + } + + // Color Compression (may not affect bloom) + if (!PrepareBloom) + { + // increasing the floor of the signal without affecting the ceiling + BaseColor.rgb = Floor + (1.0f - Floor) * BaseColor.rgb; + } + + // Color Power (may affect bloom) + BaseColor.r = pow(BaseColor.r, Power.r); + BaseColor.g = pow(BaseColor.g, Power.g); + BaseColor.b = pow(BaseColor.b, Power.b); + + // Scanline Simulation (may not affect bloom) + if (!PrepareBloom) + { + // Scanline Simulation (disabled for vector) + if (!PrepareVector) + { + 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); + + BaseColor.rgb *= ScanColor; + } + } + + // Output + float4 Output = PrepareVector + ? BaseColor * (Input.Color + float4(1.0f, 1.0f, 1.0f, 0.0f)) + : BaseColor * Input.Color; + Output.a = 1.0f; + + // Vignetting Simulation (may not affect bloom) + if (!PrepareBloom) + { + float2 VignetteCoord = BaseCoordCentered; + + float VignetteFactor = GetVignetteFactor(VignetteCoord, VignettingAmount); + Output.rgb *= VignetteFactor; + } + + // Light Reflection Simulation (may not affect bloom) + if (!PrepareBloom) + { + float3 LightColor = float3(1.0f, 0.90f, 0.80f); + + float2 SpotCoord = BaseCoordCentered; + float2 NoiseCoord = BaseCoordCentered; + + float SpotAddend = GetSpotAddend(SpotCoord, ReflectionAmount); + float NoiseFactor = GetNoiseFactor(SpotAddend, random(NoiseCoord)); + Output.rgb += SpotAddend * NoiseFactor * LightColor; + } + + // Round Corners Simulation (may affect bloom) + float2 RoundCornerCoord = BaseCoordCentered; + + float roundCornerFactor = GetRoundCornerFactor(RoundCornerCoord, RoundCornerAmount, SmoothBorderAmount); + Output.rgb *= roundCornerFactor; + + return Output; +} + +//----------------------------------------------------------------------------- +// Scanline & Shadowmask Effect +//----------------------------------------------------------------------------- + +technique ScanMaskTechnique +{ + pass Pass0 + { + Lighting = FALSE; + + //Sampler[0] = ; + + VertexShader = compile vs_3_0 vs_main(); + PixelShader = compile ps_3_0 ps_main(); + } +} \ No newline at end of file diff --git a/hlsl/distortion.fx b/hlsl/distortion.fx index 17cb4b6a9e6..b864d72348f 100644 --- a/hlsl/distortion.fx +++ b/hlsl/distortion.fx @@ -87,7 +87,7 @@ float roundBox(float2 p, float2 b, float r) //----------------------------------------------------------------------------- uniform float2 ScreenDims; // size of the window or fullscreen -uniform float2 TargetDims; +uniform float2 TargetDims; // size of the target surface uniform float2 QuadDims; // size of the screen quad VS_OUTPUT vs_main(VS_INPUT Input) diff --git a/hlsl/post.fx b/hlsl/post.fx index 1a49ab55d4d..70b374179c4 100644 --- a/hlsl/post.fx +++ b/hlsl/post.fx @@ -56,6 +56,16 @@ struct PS_INPUT float2 ScreenCoord : TEXCOORD1; }; +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +static const float PI = 3.1415927f; + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + bool xor(bool a, bool b) { return (a || b) && !(a && b); @@ -127,8 +137,6 @@ uniform float2 ShadowUV = float2(0.25f, 0.25f); uniform float3 Power = float3(1.0f, 1.0f, 1.0f); uniform float3 Floor = float3(0.0f, 0.0f, 0.0f); -static const float PI = 3.1415927f; - float4 ps_main(PS_INPUT Input) : COLOR { float2 ScreenTexelDims = 1.0f / ScreenDims; diff --git a/src/osd/modules/render/d3d/d3dhlsl.c b/src/osd/modules/render/d3d/d3dhlsl.c index 771a30b930c..7b600d3c56f 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.c +++ b/src/osd/modules/render/d3d/d3dhlsl.c @@ -1090,8 +1090,16 @@ int shaders::create_resources(bool reset) bloom_effect->add_uniform("TargetDims", uniform::UT_VEC2, uniform::CU_TARGET_DIMS); post_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); + post_effect->add_uniform("SourceRect", uniform::UT_VEC2, uniform::CU_SOURCE_RECT); // backward compatibility post_effect->add_uniform("ScreenDims", uniform::UT_VEC2, uniform::CU_SCREEN_DIMS); post_effect->add_uniform("TargetDims", uniform::UT_VEC2, uniform::CU_TARGET_DIMS); + post_effect->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); // backward compatibility + + post_effect->add_uniform("VignettingAmount", uniform::UT_FLOAT, uniform::CU_POST_VIGNETTING); // backward compatibility + post_effect->add_uniform("CurvatureAmount", uniform::UT_FLOAT, uniform::CU_POST_CURVATURE); // backward compatibility + post_effect->add_uniform("RoundCornerAmount", uniform::UT_FLOAT, uniform::CU_POST_ROUND_CORNER); // backward compatibility + post_effect->add_uniform("SmoothBorderAmount", uniform::UT_FLOAT, uniform::CU_POST_SMOOTH_BORDER); // backward compatibility + post_effect->add_uniform("ReflectionAmount", uniform::UT_FLOAT, uniform::CU_POST_REFLECTION); // backward compatibility post_effect->add_uniform("ShadowAlpha", uniform::UT_FLOAT, uniform::CU_POST_SHADOW_ALPHA); post_effect->add_uniform("ShadowCount", uniform::UT_VEC2, uniform::CU_POST_SHADOW_COUNT); @@ -1585,9 +1593,19 @@ int shaders::distortion_pass(render_target *rt, int source_index, poly_info *pol { int next_index = source_index; + // skip distortion if no influencing settings + if (options->reflection == 0 && + options->vignetting == 0 && + options->curvature == 0 && + options->round_corner == 0 && + options->smooth_border == 0) + { + return next_index; + } + int screen_count = d3d->window().target()->current_view()->screens().count(); - // todo: currently only one screen is supported + // only one screen is supported if (screen_count > 1) { return next_index; @@ -1596,7 +1614,7 @@ int shaders::distortion_pass(render_target *rt, int source_index, poly_info *pol render_bounds bounds = d3d->window().target()->current_view()->bounds(); render_bounds screen_bounds = d3d->window().target()->current_view()->screen_bounds(); - // todo: cuccently artworks are not supported + // artworks are not supported if (bounds.x0 != screen_bounds.x0 || bounds.y0 != screen_bounds.y0 || bounds.x1 != screen_bounds.x1 || @@ -1605,16 +1623,6 @@ int shaders::distortion_pass(render_target *rt, int source_index, poly_info *pol return next_index; } - // skip distortion if no influencing settings - if (options->reflection == 0 && - options->vignetting == 0 && - options->curvature == 0 && - options->round_corner == 0 && - options->smooth_border == 0) - { - return next_index; - } - bool orientation_swap_xy = (d3d->window().machine().system().flags & ORIENTATION_SWAP_XY) == ORIENTATION_SWAP_XY; bool rotation_swap_xy =