Add crt-hyllian-sinc.fx, crt-geo-zfast.fx and update others (#3252)

* Add crt-hyllian-sinc.fx, crt-geo-zfast.fx and update others

- Add crt-hyllian-sinc.fx;
- Add crt-geo-zfast.fx;
- Updated bicubic.fx and lanczos3.fx to allow prescaling;
- Add include folder and mask.fxh and geom.fxh;

* Update psx.jpg

- No logos anymore.
This commit is contained in:
Hyllian 2024-07-15 09:06:02 -03:00 committed by GitHub
parent fae6b7ae86
commit cf15591704
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1073 additions and 133 deletions

View File

@ -0,0 +1,150 @@
#include "ReShade.fxh"
/*
zfast_crt_geo - A simple, fast CRT shader.
Copyright (C) 2017 Greg Hogan (SoltanGris42)
Copyright (C) 2023 Jose Linares (Dogway)
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
Notes: This shader does scaling with a weighted linear filter
based on the algorithm by Iñigo Quilez here:
https://iquilezles.org/articles/texture/
but modified to be somewhat sharper. Then a scanline effect that varies
based on pixel brightness is applied along with a monochrome aperture mask.
This shader runs at ~60fps on the Chromecast HD (10GFlops) on a 1080p display.
(https://forums.libretro.com/t/android-googletv-compatible-shaders-nitpicky)
Dogway: I modified zfast_crt.glsl shader to include screen curvature,
vignetting, round corners and phosphor*temperature. Horizontal pixel is left out
from the Quilez' algo (read above) to provide a more S-Video like horizontal blur.
The scanlines and mask are also now performed in the recommended linear light.
For this to run smoothly on GPU deprived platforms like the Chromecast and
older consoles, I had to remove several parameters and hardcode them into the shader.
Another POV is to run the shader on handhelds like the Switch or SteamDeck so they consume less battery.
*/
uniform float SCANLINE_WEIGHT <
ui_type = "drag";
ui_min = 0.0;
ui_max = 15.0;
ui_step = 0.5;
ui_label = "Scanline Amount";
> = 7.0;
uniform float MASK_DARK <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.05;
ui_label = "Mask Effect Amount";
> = 0.5;
uniform float2 NormalizedNativePixelSize < source = "normalized_native_pixel_size"; >;
uniform float BufferWidth < source = "bufferwidth"; >;
uniform float BufferHeight < source = "bufferheight"; >;
sampler2D sBackBuffer{Texture=ReShade::BackBufferTex;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=LINEAR;MinFilter=LINEAR;};
struct ST_VertexOut
{
float2 invDims : TEXCOORD1;
};
// Vertex shader generating a triangle covering the entire screen
void VS_CRT_Geo_zFast(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD, out ST_VertexOut vVARS)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
vVARS.invDims = NormalizedNativePixelSize;
}
#define MSCL (BufferHeight > 1499.0 ? 0.3333 : 0.5)
// This compensates the scanline+mask embedded gamma from the beam dynamics
#define pwr ((1.0/((-0.0325*SCANLINE_WEIGHT+1.0)*(-0.311*MASK_DARK+1.0))-1.2).xxx)
// NTSC-J (D93) -> Rec709 D65 Joint Matrix (with D93 simulation)
// This is compensated for a linearization hack (RGB*RGB and then sqrt())
static const float3x3 P22D93 = float3x3(
1.00000, 0.00000, -0.06173,
0.07111, 0.96887, -0.01136,
0.00000, 0.08197, 1.07280);
// Returns gamma corrected output, compensated for scanline+mask embedded gamma
float3 inv_gamma(float3 col, float3 power)
{
float3 cir = col-1.0;
cir *= cir;
col = lerp(sqrt(col),sqrt(1.0-cir),power);
return col;
}
float2 Warp(float2 pos)
{
pos = pos*2.0-1.0;
pos *= float2(1.0 + (pos.y*pos.y)*0.0276, 1.0 + (pos.x*pos.x)*0.0414);
return pos*0.5 + 0.5;
}
float4 PS_CRT_Geo_zFast(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD0, in ST_VertexOut vVARS) : SV_Target
{
float2 pos = vTexCoord;
float2 xy = Warp(pos);
float2 corn = min(xy,1.0-xy); // This is used to mask the rounded
corn.x = 0.0001/corn.x; // corners later on
pos *= (1.0 - pos.xy);
float vig = pos.x * pos.y * 46.0;
vig = min(sqrt(vig), 1.0);
// Of all the pixels that are mapped onto the texel we are
// currently rendering, which pixel are we currently rendering?
float ratio_scale = xy.y / NormalizedNativePixelSize.y - 0.5;
// Snap to the center of the underlying texel.
float i = floor(ratio_scale) + 0.5;
// This is just like "Quilez Scaling" but sharper
float f = ratio_scale - i;
float Y = f*f;
float p = (i + 4.0*Y*f)*vVARS.invDims.y;
float whichmask = floor(vTexCoord.x*BufferWidth)*(-MSCL);
float mask = 1.0 + float(frac(whichmask) < MSCL)*(-MASK_DARK);
float3 colour = tex2D(sBackBuffer, float2(xy.x,p)).rgb;
colour = max(mul(P22D93 * vig, colour*colour), 0.0.xxx);
float scanLineWeight = (1.5 - SCANLINE_WEIGHT*(Y - Y*Y));
if (corn.y <= corn.x || corn.x < 0.0001 )
colour = 0.0.xxx;
return float4(inv_gamma(colour.rgb*lerp(scanLineWeight*mask, 1.0, colour.r*0.26667+colour.g*0.26667+colour.b*0.26667),pwr),1.0);
}
technique CRT_Geo_zFast
{
pass
{
VertexShader = VS_CRT_Geo_zFast;
PixelShader = PS_CRT_Geo_zFast;
}
}

View File

@ -0,0 +1,375 @@
#include "ReShade.fxh"
/*
Hyllian's CRT-sinc Shader
Copyright (C) 2011-2024 Hyllian
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform int HFILTER_PROFILE <
ui_type = "combo";
ui_items = "Custom\0Composite\0Composite Soft\0";
ui_label = "H-FILTER PROFILE";
> = 0;
uniform float SHP <
ui_type = "drag";
ui_min = 0.50;
ui_max = 1.0;
ui_step = 0.01;
ui_label = "CUSTOM H-FILTER SHARPNESS";
> = 1.0;
uniform bool CRT_ANTI_RINGING <
ui_type = "radio";
ui_label = "ANTI RINGING";
> = true;
uniform bool SHARPNESS_HACK <
ui_type = "radio";
ui_label = "SHARPNESS HACK";
> = false;
uniform float CRT_InputGamma <
ui_type = "drag";
ui_min = 1.0;
ui_max = 5.0;
ui_step = 0.1;
ui_label = "INPUT GAMMA";
> = 2.4;
uniform float CRT_OutputGamma <
ui_type = "drag";
ui_min = 1.0;
ui_max = 5.0;
ui_step = 0.05;
ui_label = "OUTPUT GAMMA";
> = 2.2;
uniform int MASK_LAYOUT <
ui_type = "combo";
ui_items = "0-Off\0"
"1-Aperture Classic\0""2-Aperture1 RGB 1080p\0""3-Aperture2 RGB 1080p\0""4-Aperture1 RGB 4k\0""5-Aperture2 RGB 4k\0""6-Aperture3 RGB 4k\0"
"7-Shadow Classic\0""8-Shadow1 1080p\0""9-Shadow2 1080p\0""10-Shadow1 4k\0"
"11-Slot1 1080p\0""12-Slot2 1080p\0""13-Slot1 4k\0""14-Slot1 4k\0""15-Slot1 8k\0";
ui_category = "CRT Mask";
ui_label = "MASK LAYOUT";
> = 1;
uniform int MONITOR_SUBPIXELS <
ui_type = "combo";
ui_items = "RGB\0BGR\0";
ui_category = "CRT Mask";
ui_label = "MONITOR SUBPIXELS LAYOUT";
> = 0;
uniform float BRIGHTBOOST <
ui_type = "drag";
ui_min = 0.0;
ui_max = 3.0;
ui_step = 0.05;
ui_label = "BRIGHTNESS BOOST";
> = 1.0;
uniform float BEAM_MIN_WIDTH <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.01;
ui_label = "MIN BEAM WIDTH";
> = 0.86;
uniform float BEAM_MAX_WIDTH <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.01;
ui_label = "MAX BEAM WIDTH";
> = 1.0;
uniform float SCANLINES_STRENGTH <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.01;
ui_label = "SCANLINES STRENGTH";
> = 0.72;
uniform int SCANLINES_SHAPE <
ui_type = "combo";
ui_items = "Sinc\0Gaussian\0";
ui_label = "SCANLINES SHAPE";
> = 1.0;
uniform float SCANLINES_CUTOFF <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1000.0;
ui_step = 1.0;
ui_label = "SCANLINES CUTOFF";
ui_tooltip = "Max vertical native resolution above which scanlines are disabled.";
> = 390.0;
uniform bool SCANLINES_HIRES <
ui_type = "radio";
ui_label = "HIGH RESOLUTION SCANLINES";
> = false;
uniform float POST_BRIGHTNESS <
ui_type = "drag";
ui_min = 1.0;
ui_max = 3.0;
ui_step = 0.05;
ui_label = "POST-BRIGHTNESS";
> = 1.00;
uniform bool VSCANLINES <
ui_type = "radio";
ui_label = "VERTICAL SCANLINES";
> = false;
uniform float2 NormalizedNativePixelSize < source = "normalized_native_pixel_size"; >;
uniform float BufferWidth < source = "bufferwidth"; >;
uniform float BufferHeight < source = "bufferheight"; >;
uniform float2 BufferToViewportRatio < source = "buffer_to_viewport_ratio"; >;
uniform float2 ViewportSize < source = "viewportsize"; >;
uniform float ViewportWidth < source = "viewportwidth"; >;
uniform float ViewportHeight < source = "viewportheight"; >;
uniform float UpscaleMultiplier < source = "upscale_multiplier"; >;
#include "../misc/include/mask.fxh"
#include "../misc/include/geom.fxh"
sampler2D sBackBuffer{Texture=ReShade::BackBufferTex;AddressU=BORDER;AddressV=BORDER;AddressW=BORDER;MagFilter=POINT;MinFilter=POINT;};
texture2D tBackBufferLinear{Width=BUFFER_WIDTH;Height=BUFFER_HEIGHT;Format=RGBA16f;};
sampler2D sBackBufferLinear{Texture=tBackBufferLinear;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=POINT;MinFilter=POINT;};
#define GAMMA_IN(color) pow(color, float3(CRT_InputGamma, CRT_InputGamma, CRT_InputGamma))
#define GAMMA_OUT(color) pow(color, float3(1.0 / CRT_OutputGamma, 1.0 / CRT_OutputGamma, 1.0 / CRT_OutputGamma))
#define SCANLINES_STRENGTH (-0.16*SCANLINES_SHAPE+SCANLINES_STRENGTH)
#define CORNER_SMOOTHNESS (80.0*pow(CORNER_SMOOTHNESS,10.0))
#define pi 3.1415926535897932384626433832795
#define RADIUS 2.0 // No need for more than 2-taps
float2 get_hfilter_profile()
{
float2 hf_profile = float2(SHP, RADIUS);
if (HFILTER_PROFILE == 1) hf_profile = float2(0.78, 2.0); // SNES composite
else if (HFILTER_PROFILE == 2) hf_profile = float2(0.65, 2.0); // Genesis composite
return hf_profile;
}
/* Some window functions for tests. */
float4 sinc(float4 x) { return sin(pi*x)*(1.0/(pi*x+0.001.xxxx)); }
float4 hann_window(float4 x) { return 0.5 * ( 1.0 - cos( 0.5 * pi * ( x + 2.0 ) ) ); }
float4 blackman_window(float4 x) { return 0.42 - 0.5*cos(0.5*pi*(x+2.0)) + 0.08*cos(pi*(x+2.0)); }
float4 lanczos(float4 x, float a) { return sinc(x) * sinc(x / a); }
float4 blackman(float4 x, float a) { return sinc(x) * blackman_window(x); }
float4 hann(float4 x, float a) { return sinc(x) * hann_window(x); }
float4 resampler4(float4 x, float2 hfp)
{
return blackman(x * hfp.x, hfp.y);
}
#define wa (0.5*pi)
#define wb (pi)
float3 resampler3(float3 x)
{
float3 res;
res.x = (x.x<=0.001) ? 1.0 : sin(x.x*wa)*sin(x.x*wb)/(wa*wb*x.x*x.x);
res.y = (x.y<=0.001) ? 1.0 : sin(x.y*wa)*sin(x.y*wb)/(wa*wb*x.y*x.y);
res.z = (x.z<=0.001) ? 1.0 : sin(x.z*wa)*sin(x.z*wb)/(wa*wb*x.z*x.z);
return res;
}
float3 get_scanlines(float3 d0, float3 d1, float3 color0, float3 color1)
{
if (SCANLINES_SHAPE > 0.5) {
d0 = exp(-16.0*d0*d0);
d1 = exp(-16.0*d1*d1);
}
else {
d0 = clamp(2.0*d0, 0.0, 1.0);
d1 = clamp(2.0*d1, 0.0, 1.0);
d0 = resampler3(d0);
d1 = resampler3(d1);
}
return (BRIGHTBOOST*(color0*d0+color1*d1));
}
float4 PS_BackBufferLinear(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD) : SV_Target
{
// float2 tc = (floor(vTexCoord / NormalizedNativePixelSize) + 0.5.xx) * NormalizedNativePixelSize;
return float4(GAMMA_IN(tex2D(sBackBuffer, vTexCoord).rgb), 1.0);
}
struct ST_VertexOut
{
float2 sinangle : TEXCOORD1;
float2 cosangle : TEXCOORD2;
float3 stretch : TEXCOORD3;
float2 TextureSize : TEXCOORD4;
};
// Vertex shader generating a triangle covering the entire screen
void VS_CRT_Geom(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD, out ST_VertexOut vVARS)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
// Screen centering
texcoord = texcoord - float2(centerx,centery)/100.0;
float2 SourceSize = 1.0/NormalizedNativePixelSize;
float shp_hack = 1.0 + float(SHARPNESS_HACK);
// Precalculate a bunch of useful values we'll need in the fragment
// shader.
vVARS.sinangle = sin(float2(geom_x_tilt, geom_y_tilt));
vVARS.cosangle = cos(float2(geom_x_tilt, geom_y_tilt));
vVARS.stretch = maxscale(vVARS.sinangle, vVARS.cosangle);
vVARS.TextureSize = lerp(float2(shp_hack*SourceSize.x, SourceSize.y), float2(SourceSize.x, shp_hack*SourceSize.y), VSCANLINES);
}
float4 PS_CRT_Hyllian(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD0, in ST_VertexOut vVARS) : SV_Target
{
float2 OutputSize = float2(BufferWidth, BufferHeight);
float2 TextureSize = vVARS.TextureSize;
float2 dx = lerp(float2(1.0/TextureSize.x, 0.0), float2(0.0, 1.0/TextureSize.y), VSCANLINES);
float2 dy = lerp(float2(0.0, 1.0/TextureSize.y), float2(1.0/TextureSize.x, 0.0), VSCANLINES);
// Texture coordinates of the texel containing the active pixel.
float2 WarpedTexCoord = (geom_curvature == true) ? transform(vTexCoord, vVARS.sinangle, vVARS.cosangle, vVARS.stretch) : vTexCoord;
float cval = corner((WarpedTexCoord-0.5.xx) * BufferToViewportRatio + 0.5.xx);
float2 pix_coord = WarpedTexCoord*TextureSize - 0.5.xx;
float2 tc = ( (SCANLINES_HIRES == true) ? (lerp(float2(floor(pix_coord.x), pix_coord.y), float2(pix_coord.x, floor(pix_coord.y)), VSCANLINES) + float2(0.5, 0.5)) : (floor(pix_coord) + float2(0.5, 0.5)) )/TextureSize;
float2 fp = lerp(frac(pix_coord), frac(pix_coord.yx), VSCANLINES);
float3 c00 = tex2D(sBackBufferLinear, tc - dx).xyz;
float3 c01 = tex2D(sBackBufferLinear, tc ).xyz;
float3 c02 = tex2D(sBackBufferLinear, tc + dx).xyz;
float3 c03 = tex2D(sBackBufferLinear, tc + 2.0*dx).xyz;
float3 c10, c11, c12, c13;
if (SCANLINES_HIRES == false)
{
c10 = tex2D(sBackBufferLinear, tc - dx + dy).xyz;
c11 = tex2D(sBackBufferLinear, tc + dy).xyz;
c12 = tex2D(sBackBufferLinear, tc + dx + dy).xyz;
c13 = tex2D(sBackBufferLinear, tc + 2.0*dx + dy).xyz;
}
else { c10 = c00; c11 = c01; c12 = c02; c13 = c03;}
float4x3 color_matrix0 = float4x3(c00, c01, c02, c03);
float4x3 color_matrix1 = float4x3(c10, c11, c12, c13);
float2 hfp = get_hfilter_profile();
float4 weights = resampler4(float4(1.0+fp.x, fp.x, 1.0-fp.x, 2.0-fp.x), hfp);
float3 color0 = mul(weights, color_matrix0)/dot(weights, 1.0.xxxx);
float3 color1 = mul(weights, color_matrix1)/dot(weights, 1.0.xxxx);
// Get min/max samples
float3 min_sample0 = min(c01,c02);
float3 max_sample0 = max(c01,c02);
float3 min_sample1 = min(c11,c12);
float3 max_sample1 = max(c11,c12);
// Anti-ringing
float3 aux = color0;
color0 = clamp(color0, min_sample0, max_sample0);
color0 = lerp(aux, color0, CRT_ANTI_RINGING);
aux = color1;
color1 = clamp(color1, min_sample1, max_sample1);
color1 = lerp(aux, color1, CRT_ANTI_RINGING);
float pos0 = fp.y;
float pos1 = 1 - fp.y;
float3 lum0 = lerp(BEAM_MIN_WIDTH.xxx, BEAM_MAX_WIDTH.xxx, color0);
float3 lum1 = lerp(BEAM_MIN_WIDTH.xxx, BEAM_MAX_WIDTH.xxx, color1);
float3 d0 = SCANLINES_STRENGTH*pos0/(lum0*lum0+0.0000001.xxx);
float3 d1 = SCANLINES_STRENGTH*pos1/(lum1*lum1+0.0000001.xxx);
float3 color = (vVARS.TextureSize.y <= SCANLINES_CUTOFF) ? get_scanlines(d0, d1, color0, color1) : tex2D(sBackBufferLinear, WarpedTexCoord.xy).xyz;
color *= BRIGHTBOOST;
color = GAMMA_OUT(color);
float2 mask_coords =vTexCoord.xy * OutputSize.xy;
mask_coords = lerp(mask_coords.xy, mask_coords.yx, VSCANLINES);
color.rgb*=GAMMA_OUT(mask_weights(mask_coords, MASK_LAYOUT, MONITOR_SUBPIXELS, MASK_DARK_STRENGTH, MASK_LIGHT_STRENGTH));
float4 res = float4(POST_BRIGHTNESS*color, 1.0);
res.rgb = res.rgb * cval.xxx;
return float4(res.rgb, 1.0);
}
technique CRT_Hyllian
{
pass
{
VertexShader = PostProcessVS;
PixelShader = PS_BackBufferLinear;
RenderTarget = tBackBufferLinear;
}
pass
{
VertexShader = VS_CRT_Geom;
PixelShader = PS_CRT_Hyllian;
}
}

View File

@ -32,15 +32,21 @@ uniform int BICUBIC_FILTER <
ui_tooltip = "Bicubic: balanced. Catmull-Rom: sharp. B-Spline: blurred. Hermite: soft pixelized.";
> = 0;
uniform float B_PRESCALE <
ui_type = "drag";
ui_min = 1.0;
ui_max = 8.0;
ui_step = 1.0;
ui_label = "Prescale factor";
> = 1.0;
uniform bool B_ANTI_RINGING <
ui_type = "radio";
ui_label = "Bicubic Anti-Ringing";
ui_label = "Anti-Ringing";
> = false;
uniform float2 NormalizedNativePixelSize < source = "normalized_native_pixel_size"; >;
uniform float2 BufferToViewportRatio < source = "buffer_to_viewport_ratio"; >;
uniform float2 ViewportSize < source = "viewportsize"; >;
uniform float BufferWidth < source = "bufferwidth"; >;
texture2D tBicubic_P0{Width=BUFFER_WIDTH;Height=BUFFER_HEIGHT;Format=RGBA8;};
sampler2D sBicubic_P0{Texture=tBicubic_P0;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=POINT;MinFilter=POINT;};
@ -89,9 +95,9 @@ float3 bicubic_ar(float fp, float3 C0, float3 C1, float3 C2, float3 C3)
float4 PS_Bicubic_X(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Target
{
// Both dimensions are unfiltered, so it looks for lores pixels.
float2 ps = NormalizedNativePixelSize;
float2 ps = NormalizedNativePixelSize/B_PRESCALE;
float2 pos = uv_tx.xy/ps - float2(0.5, 0.0);
float2 tc = (floor(pos) + float2(0.5, 0.5)) * ps;
float2 tc = (floor(pos) + 0.5.xx) * ps;
float2 fp = frac(pos);
float3 C0 = tex2D(ReShade::BackBuffer, tc + ps*float2(-1.0, 0.0)).rgb;
@ -108,9 +114,9 @@ float4 PS_Bicubic_X(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Targ
float4 PS_Bicubic_Y(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Target
{
// One must be careful here. Horizontal dimension is already filtered, so it looks for x in hires.
float2 ps = float2(1.0/(ViewportSize.x*BufferToViewportRatio.x), NormalizedNativePixelSize.y);
float2 ps = float2(1.0/BufferWidth, NormalizedNativePixelSize.y/B_PRESCALE);
float2 pos = uv_tx.xy/ps - float2(0.0, 0.5);
float2 tc = (floor(pos) + float2(0.5, 0.5)) * ps;
float2 tc = (floor(pos) + 0.5.xx) * ps;
float2 fp = frac(pos);
float3 C0 = tex2D(sBicubic_P0, tc + ps*float2(0.0, -1.0)).rgb;

View File

@ -28,14 +28,22 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
http://www.gnu.org/copyleft/gpl.html
*/
uniform float L3_PRESCALE <
ui_type = "drag";
ui_min = 1.0;
ui_max = 8.0;
ui_step = 1.0;
ui_label = "Prescale factor";
> = 1.0;
uniform bool LANCZOS3_ANTI_RINGING <
ui_type = "radio";
ui_label = "Lanczos3 Anti-Ringing";
> = true;
uniform float2 NormalizedNativePixelSize < source = "normalized_native_pixel_size"; >;
uniform float2 BufferToViewportRatio < source = "buffer_to_viewport_ratio"; >;
uniform float2 ViewportSize < source = "viewportsize"; >;
uniform float BufferWidth < source = "bufferwidth"; >;
texture2D tLanczos3_P0{Width=BUFFER_WIDTH;Height=BUFFER_HEIGHT;Format=RGBA8;};
sampler2D sLanczos3_P0{Texture=tLanczos3_P0;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=POINT;MinFilter=POINT;};
@ -59,7 +67,7 @@ float3 lanczos3ar(float fp, float3 C0, float3 C1, float3 C2, float3 C3, float3 C
float3 w1 = weight3(0.5 - fp * 0.5);
float3 w2 = weight3(1.0 - fp * 0.5);
float sum = dot( w1, float3(1.,1.,1.)) + dot( w2, float3(1.,1.,1.));
float sum = dot(w1, 1.0.xxx) + dot(w2, 1.0.xxx);
w1 /= sum;
w2 /= sum;
@ -83,9 +91,9 @@ float3 lanczos3ar(float fp, float3 C0, float3 C1, float3 C2, float3 C3, float3 C
float4 PS_Lanczos3_X(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Target
{
// Both dimensions are unfiltered, so it looks for lores pixels.
float2 ps = NormalizedNativePixelSize;
float2 ps = NormalizedNativePixelSize/L3_PRESCALE;
float2 pos = uv_tx.xy/ps - float2(0.5, 0.0);
float2 tc = (floor(pos) + float2(0.5, 0.5)) * ps;
float2 tc = (floor(pos) + 0.5.xx) * ps;
float2 fp = frac(pos);
float3 C0 = tex2D(ReShade::BackBuffer, tc + ps*float2(-2.0, 0.0)).rgb;
@ -104,9 +112,9 @@ float4 PS_Lanczos3_X(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Tar
float4 PS_Lanczos3_Y(float4 vpos: SV_Position, float2 uv_tx : TEXCOORD) : SV_Target
{
// One must be careful here. Horizontal dimension is already filtered, so it looks for x in hires.
float2 ps = float2(1.0/(ViewportSize.x*BufferToViewportRatio.x), NormalizedNativePixelSize.y);
float2 ps = float2(1.0/BufferWidth, NormalizedNativePixelSize.y/L3_PRESCALE);
float2 pos = uv_tx.xy/ps - float2(0.0, 0.5);
float2 tc = (floor(pos) + float2(0.5, 0.5)) * ps;
float2 tc = (floor(pos) + 0.5.xx) * ps;
float2 fp = frac(pos);
float3 C0 = tex2D(sLanczos3_P0, tc + ps*float2(0.0, -2.0)).rgb;

View File

@ -170,14 +170,14 @@ sampler2D sBackBuffer{Texture=ReShade::BackBufferTex;AddressU=BORDER;AddressV=BO
#define PI 3.141592653589
#ifdef LINEAR_PROCESSING
# define TEX2D(c) pow(tex2D(sBackBuffer, (c)), float4(geom_target_gamma,geom_target_gamma,geom_target_gamma,geom_target_gamma))
# define TEX2D(c) pow(tex2D(sBackBuffer, (c)), geom_target_gamma.xxxx)
#else
# define TEX2D(c) tex2D(sBackBuffer, (c))
#endif
// aspect ratio
#define aspect (geom_invert_aspect==true?float2(ViewportHeight/ViewportWidth,1.0):float2(1.0,ViewportHeight/ViewportWidth))
#define overscan (float2(1.01,1.01));
#define overscan (1.01.xx);
struct ST_VertexOut
@ -189,105 +189,21 @@ struct ST_VertexOut
};
float vs_intersect(float2 xy, float2 sinangle, float2 cosangle)
{
float A = dot(xy,xy) + geom_d*geom_d;
float B = 2.0*(geom_R*(dot(xy,sinangle)-geom_d*cosangle.x*cosangle.y)-geom_d*geom_d);
float C = geom_d*geom_d + 2.0*geom_R*geom_d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B-4.0*A*C))/(2.0*A);
}
float2 vs_bkwtrans(float2 xy, float2 sinangle, float2 cosangle)
{
float c = vs_intersect(xy, sinangle, cosangle);
float2 point = (float2(c, c)*xy - float2(-geom_R, -geom_R)*sinangle) / float2(geom_R, geom_R);
float2 poc = point/cosangle;
float2 tang = sinangle/cosangle;
float A = dot(tang, tang) + 1.0;
float B = -2.0*dot(poc, tang);
float C = dot(poc, poc) - 1.0;
float a = (-B + sqrt(B*B - 4.0*A*C))/(2.0*A);
float2 uv = (point - a*sinangle)/cosangle;
float r = FIX(geom_R*acos(a));
return uv*r/sin(r/geom_R);
}
float2 vs_fwtrans(float2 uv, float2 sinangle, float2 cosangle)
{
float r = FIX(sqrt(dot(uv,uv)));
uv *= sin(r/geom_R)/r;
float x = 1.0-cos(r/geom_R);
float D = geom_d/geom_R + x*cosangle.x*cosangle.y+dot(uv,sinangle);
return geom_d*(uv*cosangle-x*sinangle)/D;
}
float3 vs_maxscale(float2 sinangle, float2 cosangle)
{
float2 c = vs_bkwtrans(-geom_R * sinangle / (1.0 + geom_R/geom_d*cosangle.x*cosangle.y), sinangle, cosangle);
float2 a = float2(0.5,0.5)*aspect;
float2 lo = float2(vs_fwtrans(float2(-a.x, c.y), sinangle, cosangle).x,
vs_fwtrans(float2( c.x, -a.y), sinangle, cosangle).y)/aspect;
float2 hi = float2(vs_fwtrans(float2(+a.x, c.y), sinangle, cosangle).x,
vs_fwtrans(float2( c.x, +a.y), sinangle, cosangle).y)/aspect;
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
}
// Code snippet borrowed from crt-cyclon. (credits to DariusG)
float2 Warp(float2 pos)
{
pos = pos*2.0 - 1.0;
pos *= float2(1.0 + pos.y*pos.y*0, 1.0 + pos.x*pos.x*0);
pos = pos*0.5 + 0.5;
return pos;
}
// Vertex shader generating a triangle covering the entire screen
void VS_CRT_Geom(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD, out ST_VertexOut vVARS)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
// center screen
texcoord = Warp(texcoord - float2(centerx,centery)/100.0);
float2 SourceSize = 1.0/NormalizedNativePixelSize;
// Precalculate a bunch of useful values we'll need in the fragment
// shader.
vVARS.sinangle = sin(float2(geom_x_tilt, geom_y_tilt));
vVARS.cosangle = cos(float2(geom_x_tilt, geom_y_tilt));
vVARS.stretch = vs_maxscale(vVARS.sinangle, vVARS.cosangle);
vVARS.TextureSize = float2(SourceSize.x, SourceSize.y);
}
float intersect(float2 xy, float2 sinangle, float2 cosangle)
{
float A = dot(xy,xy) + geom_d*geom_d;
float B, C;
B = 2.0*(geom_R*(dot(xy,sinangle) - geom_d*cosangle.x*cosangle.y) - geom_d*geom_d);
C = geom_d*geom_d + 2.0*geom_R*geom_d*cosangle.x*cosangle.y;
B = 2.0*(geom_R*(dot(xy,sinangle) - geom_d*cosangle.x*cosangle.y) - geom_d*geom_d);
C = geom_d*geom_d + 2.0*geom_R*geom_d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B - 4.0*A*C))/(2.0*A);
}
float2 bkwtrans(float2 xy, float2 sinangle, float2 cosangle)
{
float c = intersect(xy, sinangle, cosangle);
float2 point = (float2(c, c)*xy - float2(-geom_R, -geom_R)*sinangle) / float2(geom_R, geom_R);
float c = intersect(xy, sinangle, cosangle);
float2 point = (c.xx*xy + geom_R.xx*sinangle) / geom_R.xx;
float2 poc = point/cosangle;
float2 tang = sinangle/cosangle;
@ -296,7 +212,7 @@ float2 bkwtrans(float2 xy, float2 sinangle, float2 cosangle)
float C = dot(poc, poc) - 1.0;
float a = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A);
float2 uv = (point - a*sinangle) / cosangle;
float2 uv = (point - a*sinangle) / cosangle;
float r = FIX(geom_R*acos(a));
return uv*r/sin(r/geom_R);
@ -309,73 +225,91 @@ float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
float x = 1.0 - cos(r/geom_R);
float D;
D = geom_d/geom_R + x*cosangle.x*cosangle.y + dot(uv,sinangle);
D = geom_d/geom_R + x*cosangle.x*cosangle.y + dot(uv,sinangle);
return geom_d*(uv*cosangle - x*sinangle)/D;
}
float3 maxscale(float2 sinangle, float2 cosangle)
{
float2 c = bkwtrans(-geom_R * sinangle / (1.0 + geom_R/geom_d*cosangle.x*cosangle.y), sinangle, cosangle);
float2 a = float2(0.5, 0.5)*aspect;
float2 c = bkwtrans(-geom_R * sinangle / (1.0 + geom_R/geom_d*cosangle.x*cosangle.y), sinangle, cosangle);
float2 a = 0.5.xx*aspect;
float2 lo = float2(fwtrans(float2(-a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, -a.y), sinangle, cosangle).y)/aspect;
float2 hi = float2(fwtrans(float2(+a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, +a.y), sinangle, cosangle).y)/aspect;
float2 lo = float2(fwtrans(float2(-a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, -a.y), sinangle, cosangle).y)/aspect;
float2 hi = float2(fwtrans(float2(+a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, +a.y), sinangle, cosangle).y)/aspect;
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x, hi.y-lo.y));
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x, hi.y-lo.y));
}
float2 transform(float2 coord, float2 sinangle, float2 cosangle, float3 stretch)
{
coord = (coord - float2(0.5, 0.5))*aspect*stretch.z + stretch.xy;
coord = (coord - 0.5.xx)*aspect*stretch.z + stretch.xy;
return (bkwtrans(coord, sinangle, cosangle) /
float2(geom_overscan_x / 100.0, geom_overscan_y / 100.0)/aspect + float2(0.5, 0.5));
float2(geom_overscan_x / 100.0, geom_overscan_y / 100.0)/aspect + 0.5.xx);
}
// Vertex shader generating a triangle covering the entire screen
void VS_CRT_Geom(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD, out ST_VertexOut vVARS)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
// Screen centering
texcoord = texcoord - float2(centerx,centery)/100.0;
float2 SourceSize = 1.0/NormalizedNativePixelSize;
// Precalculate a bunch of useful values we'll need in the fragment
// shader.
vVARS.sinangle = sin(float2(geom_x_tilt, geom_y_tilt));
vVARS.cosangle = cos(float2(geom_x_tilt, geom_y_tilt));
vVARS.stretch = maxscale(vVARS.sinangle, vVARS.cosangle);
vVARS.TextureSize = float2(SourceSize.x, SourceSize.y);
}
float corner(float2 coord)
{
coord = min(coord, float2(1.0, 1.0) - coord) * aspect;
float2 cdist = float2(geom_cornersize, geom_cornersize);
coord = (cdist - min(coord, cdist));
float dist = sqrt(dot(coord, coord));
coord = min(coord, 1.0.xx - coord) * aspect;
float2 cdist = geom_cornersize.xx;
coord = (cdist - min(coord, cdist));
float dist = sqrt(dot(coord, coord));
return clamp((cdist.x - dist)*geom_cornersmooth, 0.0, 1.0);
return clamp((cdist.x - dist)*geom_cornersmooth, 0.0, 1.0);
}
float fwidth(float value){
return abs(ddx(value)) + abs(ddy(value));
float fwidth(float value)
{
return abs(ddx(value)) + abs(ddy(value));
}
float4 PS_CRT_Geom(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD, in ST_VertexOut vVARS) : SV_Target
{
// Texture coordinates of the texel containing the active pixel.
float2 xy;
float2 xy = (geom_curvature == true) ? transform(vTexCoord, vVARS.sinangle, vVARS.cosangle, vVARS.stretch) : vTexCoord;
if (geom_curvature == true)
xy = transform(vTexCoord, vVARS.sinangle, vVARS.cosangle, vVARS.stretch);
else
xy = vTexCoord;
float cval = corner((xy-0.5.xx) * BufferToViewportRatio + 0.5.xx);
float cval = corner((xy-float2(0.5,0.5)) * BufferToViewportRatio + float2(0.5,0.5));
float2 uv_ratio = frac((xy * vVARS.TextureSize - float2(0.5, 0.5)) / vVARS.TextureSize);
float2 uv_ratio = frac((xy * vVARS.TextureSize - 0.5.xx) / vVARS.TextureSize);
float4 col = TEX2D(xy);
#ifndef LINEAR_PROCESSING
col = pow(col , float4(geom_target_gamma, geom_target_gamma, geom_target_gamma, geom_target_gamma));
col = pow(col, geom_target_gamma.xxxx);
#endif
col.rgb *= (geom_lum * step(0.0, uv_ratio.y));
float3 mul_res = col.rgb * float3(cval, cval, cval);
float3 mul_res = col.rgb * cval.xxx;
// Convert the image gamma for display on our output device.
mul_res = pow(mul_res, float3(1.0 / geom_monitor_gamma, 1.0 / geom_monitor_gamma, 1.0 / geom_monitor_gamma));
mul_res = pow(mul_res, 1.0 / geom_monitor_gamma.xxx);
return float4(mul_res, 1.0);
}

View File

@ -0,0 +1,225 @@
#ifndef GEOM_PARAMS_H
#define GEOM_PARAMS_H
/*
Geom Shader - a modified CRT-Geom without CRT features made to be appended/integrated
into any other shaders and provide curvature/warping/oversampling features.
Adapted by Hyllian (2024).
*/
/*
CRT-interlaced
Copyright (C) 2010-2012 cgwg, Themaister and DOLLS
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
(cgwg gave their consent to have the original version of this shader
distributed under the GPL in this message:
http://board.byuu.org/viewtopic.php?p=26075#p26075
"Feel free to distribute my shaders under the GPL. After all, the
barrel distortion code was taken from the Curvature shader, which is
under the GPL."
)
This shader variant is pre-configured with screen curvature
*/
uniform bool geom_curvature <
ui_type = "radio";
ui_category = "Geom Curvature";
ui_label = "Geom Curvature Toggle";
> = 1.0;
uniform float geom_R <
ui_type = "drag";
ui_min = 0.1;
ui_max = 10.0;
ui_step = 0.1;
ui_category = "Geom Curvature";
ui_label = "Geom Curvature Radius";
> = 2.0;
uniform float geom_d <
ui_type = "drag";
ui_min = 0.1;
ui_max = 3.0;
ui_step = 0.1;
ui_category = "Geom Curvature";
ui_label = "Geom Distance";
> = 1.5;
uniform bool geom_invert_aspect <
ui_type = "radio";
ui_category = "Geom Curvature";
ui_label = "Geom Curvature Aspect Inversion";
> = 0.0;
uniform float geom_cornersize <
ui_type = "drag";
ui_min = 0.001;
ui_max = 1.0;
ui_step = 0.005;
ui_category = "Geom Curvature";
ui_label = "Geom Corner Size";
> = 0.03;
uniform float geom_cornersmooth <
ui_type = "drag";
ui_min = 80.0;
ui_max = 2000.0;
ui_step = 100.0;
ui_category = "Geom Curvature";
ui_label = "Geom Corner Smoothness";
> = 1000.0;
uniform float geom_x_tilt <
ui_type = "drag";
ui_min = -1.0;
ui_max = 1.0;
ui_step = 0.05;
ui_category = "Geom Curvature";
ui_label = "Geom Horizontal Tilt";
> = 0.0;
uniform float geom_y_tilt <
ui_type = "drag";
ui_min = -1.0;
ui_max = 1.0;
ui_step = 0.05;
ui_category = "Geom Curvature";
ui_label = "Geom Vertical Tilt";
> = 0.0;
uniform float geom_overscan_x <
ui_type = "drag";
ui_min = -125.0;
ui_max = 125.0;
ui_step = 0.5;
ui_category = "Geom Curvature";
ui_label = "Geom Horiz. Overscan %";
> = 100.0;
uniform float geom_overscan_y <
ui_type = "drag";
ui_min = -125.0;
ui_max = 125.0;
ui_step = 0.5;
ui_category = "Geom Curvature";
ui_label = "Geom Vert. Overscan %";
> = 100.0;
uniform float centerx <
ui_type = "drag";
ui_min = -100.0;
ui_max = 100.0;
ui_step = 0.1;
ui_category = "Geom Curvature";
ui_label = "Image Center X";
> = 0.00;
uniform float centery <
ui_type = "drag";
ui_min = -100.0;
ui_max = 100.0;
ui_step = 0.1;
ui_category = "Geom Curvature";
ui_label = "Image Center Y";
> = 0.00;
// Macros.
#define FIX(c) max(abs(c), 1e-5);
// aspect ratio
#define aspect (geom_invert_aspect==true?float2(ViewportHeight/ViewportWidth,1.0):float2(1.0,ViewportHeight/ViewportWidth))
float intersect(float2 xy, float2 sinangle, float2 cosangle)
{
float A = dot(xy,xy) + geom_d*geom_d;
float B, C;
B = 2.0*(geom_R*(dot(xy,sinangle) - geom_d*cosangle.x*cosangle.y) - geom_d*geom_d);
C = geom_d*geom_d + 2.0*geom_R*geom_d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B - 4.0*A*C))/(2.0*A);
}
float2 bkwtrans(float2 xy, float2 sinangle, float2 cosangle)
{
float c = intersect(xy, sinangle, cosangle);
float2 point = (c.xx*xy + geom_R.xx*sinangle) / geom_R.xx;
float2 poc = point/cosangle;
float2 tang = sinangle/cosangle;
float A = dot(tang, tang) + 1.0;
float B = -2.0*dot(poc, tang);
float C = dot(poc, poc) - 1.0;
float a = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A);
float2 uv = (point - a*sinangle) / cosangle;
float r = FIX(geom_R*acos(a));
return uv*r/sin(r/geom_R);
}
float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
{
float r = FIX(sqrt(dot(uv, uv)));
uv *= sin(r/geom_R)/r;
float x = 1.0 - cos(r/geom_R);
float D;
D = geom_d/geom_R + x*cosangle.x*cosangle.y + dot(uv,sinangle);
return geom_d*(uv*cosangle - x*sinangle)/D;
}
float3 maxscale(float2 sinangle, float2 cosangle)
{
float2 c = bkwtrans(-geom_R * sinangle / (1.0 + geom_R/geom_d*cosangle.x*cosangle.y), sinangle, cosangle);
float2 a = 0.5.xx*aspect;
float2 lo = float2(fwtrans(float2(-a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, -a.y), sinangle, cosangle).y)/aspect;
float2 hi = float2(fwtrans(float2(+a.x, c.y), sinangle, cosangle).x,
fwtrans(float2( c.x, +a.y), sinangle, cosangle).y)/aspect;
return float3((hi+lo)*aspect*0.5,max(hi.x-lo.x, hi.y-lo.y));
}
float2 transform(float2 coord, float2 sinangle, float2 cosangle, float3 stretch)
{
coord = (coord - 0.5.xx)*aspect*stretch.z + stretch.xy;
return (bkwtrans(coord, sinangle, cosangle) /
float2(geom_overscan_x / 100.0, geom_overscan_y / 100.0)/aspect + 0.5.xx);
}
float corner(float2 coord)
{
coord = min(coord, 1.0.xx - coord) * aspect;
float2 cdist = geom_cornersize.xx;
coord = (cdist - min(coord, cdist));
float dist = sqrt(dot(coord, coord));
return clamp((cdist.x - dist)*geom_cornersmooth, 0.0, 1.0);
}
float fwidth(float value)
{
return abs(ddx(value)) + abs(ddy(value));
}
#endif // GEOM_PARAMS_H

View File

@ -0,0 +1,242 @@
#ifndef MASK_PARAMS_H
#define MASK_PARAMS_H
uniform float MASK_DARK_STRENGTH <
ui_type = "drag";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.01;
ui_category = "CRT Mask";
ui_label = "MASK DARK SUBPIXEL STRENGTH";
> = 0.5;
uniform float MASK_LIGHT_STRENGTH <
ui_type = "drag";
ui_min = 0.0;
ui_max = 6.0;
ui_step = 0.01;
ui_category = "CRT Mask";
ui_label = "MASK LIGHT SUBPIXEL STRENGTH";
> = 0.5;
/* Mask code pasted from subpixel_masks.h. Masks 3 and 4 added. */
float3 mask_weights(float2 coord, int phosphor_layout, float monitor_subpixels, float mask_light_str, float mask_dark_str){
float3 weights = float3(1.,1.,1.);
float on = 1.+mask_light_str;
// float on = 1.;
float off = 1.-mask_dark_str;
float3 red = monitor_subpixels==1.0 ? float3(on, off, off) : float3(off, off, on );
float3 green = float3(off, on, off);
float3 blue = monitor_subpixels==1.0 ? float3(off, off, on ) : float3(on, off, off);
float3 magenta = float3(on, off, on );
float3 yellow = monitor_subpixels==1.0 ? float3(on, on, off) : float3(off, on, on );
float3 cyan = monitor_subpixels==1.0 ? float3(off, on, on ) : float3(on, on, off);
float3 black = float3(off, off, off);
float3 white = float3(on, on, on );
int w, z = 0;
// This pattern is used by a few layouts, so we'll define it here
float3 aperture_weights = lerp(magenta, green, floor(coord.x % 2.0));
if(phosphor_layout == 0) return weights;
else if(phosphor_layout == 1){
// classic aperture for RGB panels; good for 1080p, too small for 4K+
// aka aperture_1_2_bgr
weights = aperture_weights;
return weights;
}
else if(phosphor_layout == 2){
// Classic RGB layout; good for 1080p and lower
float3 bw3[3] = {red, green, blue};
// float3 bw3[3] = float3[](black, yellow, blue);
z = int(floor(coord.x % 3.0));
weights = bw3[z];
return weights;
}
else if(phosphor_layout == 3){
// black and white aperture; good for weird subpixel layouts and low brightness; good for 1080p and lower
float3 bw3[3] = {black, white, black};
z = int(floor(coord.x % 3.0));
weights = bw3[z];
return weights;
}
else if(phosphor_layout == 4){
// reduced TVL aperture for RGB panels. Good for 4k.
// aperture_2_4_rgb
float3 big_ap_rgb[4] = {red, yellow, cyan, blue};
w = int(floor(coord.x % 4.0));
weights = big_ap_rgb[w];
return weights;
}
else if(phosphor_layout == 5){
// black and white aperture; good for weird subpixel layouts and low brightness; good for 4k
float3 bw4[4] = {black, black, white, white};
z = int(floor(coord.x % 4.0));
weights = bw4[z];
return weights;
}
else if(phosphor_layout == 6){
// aperture_1_4_rgb; good for simulating lower
float3 ap4[4] = {red, green, blue, black};
z = int(floor(coord.x % 4.0));
weights = ap4[z];
return weights;
}
else if(phosphor_layout == 7){
// 2x2 shadow mask for RGB panels; good for 1080p, too small for 4K+
// aka delta_1_2x1_bgr
float3 inverse_aperture = lerp(green, magenta, floor(coord.x % 2.0));
weights = lerp(aperture_weights, inverse_aperture, floor(coord.y % 2.0));
return weights;
}
else if(phosphor_layout == 8){
// delta_2_4x1_rgb
float3 delta[8] = {
red, yellow, cyan, blue,
cyan, blue, red, yellow
};
w = int(floor(coord.y % 2.0));
z = int(floor(coord.x % 4.0));
weights = delta[4*w+z];
return weights;
}
else if(phosphor_layout == 9){
// delta_1_4x1_rgb; dunno why this is called 4x1 when it's obviously 4x2 /shrug
float3 delta1[8] = {
red, green, blue, black,
blue, black, red, green
};
w = int(floor(coord.y % 2.0));
z = int(floor(coord.x % 4.0));
weights = delta1[4*w+z];
return weights;
}
else if(phosphor_layout == 10){
// delta_2_4x2_rgb
float3 delta[16] = {
red, yellow, cyan, blue,
red, yellow, cyan, blue,
cyan, blue, red, yellow,
cyan, blue, red, yellow
};
w = int(floor(coord.y % 4.0));
z = int(floor(coord.x % 4.0));
weights = delta[4*w+z];
return weights;
}
else if(phosphor_layout == 11){
// slot mask for RGB panels; looks okay at 1080p, looks better at 4K
float3 slotmask[24] = {
red, green, blue, red, green, blue,
red, green, blue, black, black, black,
red, green, blue, red, green, blue,
black, black, black, red, green, blue,
};
w = int(floor(coord.y % 4.0));
z = int(floor(coord.x % 6.0));
// use the indexes to find which color to apply to the current pixel
weights = slotmask[6*w+z];
return weights;
}
else if(phosphor_layout == 12){
// slot mask for RGB panels; looks okay at 1080p, looks better at 4K
float3 slotmask[24] = {
black, white, black, black, white, black,
black, white, black, black, black, black,
black, white, black, black, white, black,
black, black, black, black, white, black
};
w = int(floor(coord.y % 4.0));
z = int(floor(coord.x % 6.0));
// use the indexes to find which color to apply to the current pixel
weights = slotmask[6*w+z];
return weights;
}
else if(phosphor_layout == 13){
// based on MajorPainInTheCactus' HDR slot mask
float3 slot[32] = {
red, green, blue, black, red, green, blue, black,
red, green, blue, black, black, black, black, black,
red, green, blue, black, red, green, blue, black,
black, black, black, black, red, green, blue, black
};
w = int(floor(coord.y % 4.0));
z = int(floor(coord.x % 8.0));
weights = slot[8*w+z];
return weights;
}
else if(phosphor_layout == 14){
// same as above but for RGB panels
float3 slot2[40] = {
red, yellow, green, blue, blue, red, yellow, green, blue, blue ,
black, green, green, blue, blue, red, red, black, black, black,
red, yellow, green, blue, blue, red, yellow, green, blue, blue ,
red, red, black, black, black, black, green, green, blue, blue
};
w = int(floor(coord.y % 4.0));
z = int(floor(coord.x % 10.0));
weights = slot2[10*w+z];
return weights;
}
else if(phosphor_layout == 15){
// slot_3_7x6_rgb
float3 slot[84] = {
red, red, yellow, green, cyan, blue, blue, red, red, yellow, green, cyan, blue, blue,
red, red, yellow, green, cyan, blue, blue, red, red, yellow, green, cyan, blue, blue,
red, red, yellow, green, cyan, blue, blue, black, black, black, black, black, black, black,
red, red, yellow, green, cyan, blue, blue, red, red, yellow, green, cyan, blue, blue,
red, red, yellow, green, cyan, blue, blue, red, red, yellow, green, cyan, blue, blue,
black, black, black, black, black, black, black, black, red, red, yellow, green, cyan, blue
};
w = int(floor(coord.y % 6.0));
z = int(floor(coord.x % 14.0));
weights = slot[14*w+z];
return weights;
}
else return weights;
}
#endif // MASK_PARAMS_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

After

Width:  |  Height:  |  Size: 470 KiB