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:
parent
fae6b7ae86
commit
cf15591704
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,90 +189,6 @@ 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;
|
||||
|
@ -287,7 +203,7 @@ float intersect(float2 xy, float2 sinangle, float2 cosangle)
|
|||
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);
|
||||
float2 point = (c.xx*xy + geom_R.xx*sinangle) / geom_R.xx;
|
||||
float2 poc = point/cosangle;
|
||||
float2 tang = sinangle/cosangle;
|
||||
|
||||
|
@ -317,7 +233,7 @@ float2 fwtrans(float2 uv, float2 sinangle, float2 cosangle)
|
|||
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 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;
|
||||
|
@ -329,23 +245,46 @@ float3 maxscale(float2 sinangle, float2 cosangle)
|
|||
|
||||
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 = 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){
|
||||
float fwidth(float value)
|
||||
{
|
||||
return abs(ddx(value)) + abs(ddy(value));
|
||||
}
|
||||
|
||||
|
@ -353,29 +292,24 @@ float fwidth(float 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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 |
Loading…
Reference in New Issue