Artwork Support

- added special post.fx and distortion.fx shader, which support enabled
artworks, but they cannot apply some of the previously implemented
corrections (e.g. bloom staircase artifacts when screen is curved,
elliptic rounded corners when aspect ratio is not 4:3)
This commit is contained in:
ImJezze 2015-10-18 19:16:46 +02:00
parent 2577b29602
commit 7eb83c31a5
5 changed files with 599 additions and 15 deletions

View File

@ -0,0 +1,97 @@
// license:BSD-3-Clause
// copyright-holders:ImJezze
//-----------------------------------------------------------------------------
// Distortion Effect
//-----------------------------------------------------------------------------
texture DiffuseTexture;
sampler DiffuseSampler = sampler_state
{
Texture = <DiffuseTexture>;
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();
}
}

View File

@ -0,0 +1,471 @@
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz,ImJezze
//-----------------------------------------------------------------------------
// Scanline & Shadowmask Effect
//-----------------------------------------------------------------------------
texture DiffuseTexture;
sampler DiffuseSampler = sampler_state
{
Texture = <DiffuseTexture>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
AddressW = CLAMP;
};
texture ShadowTexture;
sampler ShadowSampler = sampler_state
{
Texture = <ShadowTexture>;
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] = <DiffuseSampler>;
VertexShader = compile vs_3_0 vs_main();
PixelShader = compile ps_3_0 ps_main();
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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 =