1
Fork 0
This repository has been archived on 2026-03-09. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
MariENB/enbseries/enbeffectpostpass.fx

1062 lines
29 KiB
HLSL

/*
enbeffectpostpass.fx : MariENB3 extra shader.
(C)2016-2017 Marisa Kirisame, UnSX Team.
Part of MariENB3, the personal ENB of Marisa for Fallout 4.
Released under the GNU GPLv3 (or later).
*/
#include "menbglobaldefs.fx"
/* BlockGFX filter, I'm proud of it */
string str_block = "BlockGFX Suite";
bool useblock
<
string UIName = "Enable Block GFX";
string UIWidget = "Checkbox";
> = {false};
/*
emulated resolution:
0 or 1 : real resolution
<1 and >0 : multiple of real resolution (e.g.: 0.5 is half resolution)
>1 : this resolution (e.g.: 320x200 is good ol' Mode 13h)
*/
float2 bres
<
string UIName = "Emulated Resolution";
string UIWidget = "Vector";
float2 UIMin = {0.0,0.0};
> = {0.5,0.5};
/* zooming factors (<=0 for stretch) */
float2 sres
<
string UIName = "Zoom Factor";
string UIWidget = "Vector";
float2 UIMin = {0.0,0.0};
> = {0.0,0.0};
/*
palette type:
-2 : Standard VGA 256-color palette
-1 : disable
0 : CGA (320x200 4-color, or 640x200 monochrome)
1 : EGA (320x200, 16 colors)
2 : RGB2 (64-color quarter VGA palette, used in AOS)
3 : RGB323 (8-bit RGB, I don't think this was a real thing)
4 : VGA (256 colors, standard palette)
5 : Doom (256 colors, does not cover a lot)
6 : Quake I (256 colors, covers even less)
7 : RGB4 (4bpc, I also don't think this was ever used in real hardware)
8 : RGB565 (ol' 16-bit "true color")
9 : RGB6 (typical screens incapable of 8bpc)
*/
int paltype
<
string UIName = "Palette Type";
string UIWidget = "Spinner";
int UIMin = -1;
int UIMax = 9;
> = {1};
/*
CGA palette to use:
0 : black, white.
1 : black, cyan, magenta, white. low contrast
2 : black, cyan, magenta, white. high contrast
3 : black, green, red, brown. low contrast
4 : black, green, red, brown. high contrast
5 : black, cyan, red, white. low contrast
6 : black, cyan, red, white. high contrast
*/
int cgapal
<
string UIName = "CGA Palette";
string UIWidget = "Spinner";
int UIMin = 0;
int UIMax = 6;
> = {1};
/*
EGA palette to use:
0 : Standard EGA
1 : AOS EGA (it's designed for text, but looks well on images too)
*/
int egapal
<
string UIName = "EGA Palette";
string UIWidget = "Spinner";
int UIMin = 0;
int UIMax = 1;
> = {0};
/*
Dithering mode:
-1 : No dithering, just raw banding
0 : 2x2 checkerboard dithering, looks like ass
1 : 2x2 ordered dithering
2 : 3x3 ordered dithering
3 : 4x4 ordered dithering
4 : 8x8 ordered dithering
*/
int dither
<
string UIName = "Dithering Pattern";
string UIWidget = "Spinner";
int UIMin = -1;
int UIMax = 4;
> = {4};
/* gamma modifier for base color, lower values raise midtones and viceversa */
float bgamma
<
string UIName = "Contrast Modifier";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.35};
/* saturation modifier for base color, helps with limited palettes */
float bsaturation
<
string UIName = "Saturation Modifier";
string UIWidget = "Spinner";
> = {1.1};
/* base brightness bump for the dither grid */
float bdbump
<
string UIName = "Dither Offset";
string UIWidget = "Spinner";
> = {-0.1};
/* range multiplier for the dither grid */
float bdmult
<
string UIName = "Dither Range";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.25};
/* ASCII art filter */
string str_ascii = "Luma ASCII Art Filter";
bool asciienable
<
string UIName = "Enable ASCII";
string UIWidget = "Checkbox";
> = {false};
bool asciimono
<
string UIName = "ASCII Monochrome";
string UIWidget = "Checkbox";
> = {true};
float asciiblend
<
string UIName = "ASCII Blend";
string UIWidget = "Spinner";
float UIMin = 0.0;
float UIMax = 1.0;
> = {0.0};
string str_mask = "Depth Chroma Key";
bool maskenable
<
string UIName = "Enable Chroma Key";
string UIWidget = "Checkbox";
> = {false};
float3 mask
<
string UIName = "Chroma Key Red";
string UIWidget = "Color";
> = {0.0,1.0,0.0};
float maskd
<
string UIName = "Chroma Key Depth";
string UIWidget = "Spinner";
float UIMin = 0.0;
float UIMax = 1.0;
> = {0.5};
string str_dot = "RGBI Dot Matrix";
bool dotenable
<
string UIName = "Enable Dot Matrix";
string UIWidget = "Checkbox";
> = {false};
int dotsize
<
string UIName = "Dot Size";
string UIWidget = "Spinner";
int UIMin = 1;
> = {1};
float dotblend
<
string UIName = "Dot Blend";
string UIWidget = "Spinner";
float UIMin = 0.0;
float UIMax = 1.0;
> = {0.4};
float dotmult
<
string UIName = "Dot Intensity";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {1.0};
float dotpow
<
string UIName = "Dot Contrast";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {1.0};
string str_curve = "Lens Curvature";
bool curveenable
<
string UIName = "Enable Curvature";
string UIWidget = "Checkbox";
> = {false};
float chromaab
<
string UIName = "Curve Chromatic Aberration";
string UIWidget = "Spinner";
> = {0.0};
float lenszoom
<
string UIName = "Curve Zooming";
string UIWidget = "Spinner";
> = {50.0};
float lensdist
<
string UIName = "Curve Distortion";
string UIWidget = "Spinner";
> = {0.0};
float curvesoft
<
string UIName = "Curve Sampling Soften";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.0};
/* BlurSharpShift, some people are obsessed with this nonsense */
string str_bss = "BlurSharpShift";
bool bssblurenable
<
string UIName = "Enable Blur";
string UIWidget = "Checkbox";
> = {false};
float bssblurradius
<
string UIName = "Blur Sampling Range";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.25};
bool bsssharpenable
<
string UIName = "Enable Sharp";
string UIWidget = "Checkbox";
> = {false};
float bsssharpradius
<
string UIName = "Sharp Sampling Range";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {1.0};
float bsssharpamount
<
string UIName = "Sharpening Amount";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {6.0};
bool bssshiftenable
<
string UIName = "Enable Shift";
string UIWidget = "Checkbox";
> = {false};
float bssshiftradius
<
string UIName = "Shift Sampling Range";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.75};
/* luma sharpen because of reasons */
string str_lsharp = "Luma Sharpen";
bool lsharpenable
<
string UIName = "Luma Sharpen Enable";
string UIWidget = "Checkbox";
> = {false};
float lsharpradius
<
string UIName = "Luma Sharpen Radius";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.8};
float lsharpclamp
<
string UIName = "Luma Sharpen Clamp";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {0.02};
float lsharpblend
<
string UIName = "Luma Sharpen Blending";
string UIWidget = "Spinner";
float UIMin = 0.0;
> = {1.2};
/*
dithering threshold maps
don't touch unless you know what you're doing
*/
static const float checkers[4] =
{
1.0,0.0,
0.0,1.0
};
#define d(x) x/4.0
static const float ordered2[4] =
{
d(0),d(2),
d(4),d(2)
};
#undef d
#define d(x) x/9.0
static const float ordered3[9] =
{
d(2),d(6),d(3),
d(5),d(0),d(8),
d(1),d(7),d(4)
};
#undef d
#define d(x) x/16.0
static const float ordered4[16] =
{
d( 0),d( 8),d( 2),d(10),
d(12),d( 4),d(14),d( 6),
d( 3),d(11),d( 1),d( 9),
d(15),d( 7),d(13),d( 5)
};
#undef d
#define d(x) x/64.0
static const float ordered8[64] =
{
d( 0),d(48),d(12),d(60),d( 3),d(51),d(15),d(63),
d(32),d(16),d(44),d(28),d(35),d(19),d(47),d(31),
d( 8),d(56),d( 4),d(52),d(11),d(59),d( 7),d(55),
d(40),d(24),d(36),d(20),d(43),d(27),d(39),d(23),
d( 2),d(50),d(14),d(62),d( 1),d(49),d(13),d(61),
d(34),d(18),d(46),d(30),d(33),d(17),d(45),d(29),
d(10),d(58),d( 6),d(54),d( 9),d(57),d( 5),d(53),
d(42),d(26),d(38),d(22),d(41),d(25),d(37),d(21)
};
#undef d
/*
palettes
don't touch unless you know what you're doing
*/
#define d(x) x/3.0
static const float3 cga1l[4] =
{
float3(d(0),d(0),d(0)),
float3(d(0),d(2),d(2)),
float3(d(2),d(0),d(2)),
float3(d(2),d(2),d(2))
};
static const float3 cga1h[4] =
{
float3(d(0),d(0),d(0)),
float3(d(1),d(3),d(3)),
float3(d(3),d(1),d(3)),
float3(d(3),d(3),d(3))
};
static const float3 cga2l[4] =
{
float3(d(0),d(0),d(0)),
float3(d(0),d(2),d(0)),
float3(d(2),d(0),d(0)),
float3(d(2),d(1),d(0))
};
static const float3 cga2h[4] =
{
float3(d(0),d(0),d(0)),
float3(d(1),d(3),d(1)),
float3(d(3),d(1),d(1)),
float3(d(3),d(2),d(1))
};
static const float3 cga3l[4] =
{
float3(d(0),d(0),d(0)),
float3(d(0),d(2),d(2)),
float3(d(2),d(0),d(0)),
float3(d(2),d(2),d(2))
};
static const float3 cga3h[4] =
{
float3(d(0),d(0),d(0)),
float3(d(1),d(3),d(3)),
float3(d(3),d(1),d(1)),
float3(d(3),d(3),d(3))
};
static const float3 stdega[16] =
{
float3(d(0),d(0),d(0)),
float3(d(2),d(0),d(0)),
float3(d(0),d(2),d(0)),
float3(d(2),d(1),d(0)),
float3(d(0),d(0),d(2)),
float3(d(2),d(0),d(2)),
float3(d(0),d(2),d(2)),
float3(d(2),d(2),d(2)),
float3(d(1),d(1),d(1)),
float3(d(3),d(1),d(1)),
float3(d(1),d(3),d(1)),
float3(d(3),d(3),d(1)),
float3(d(1),d(1),d(3)),
float3(d(3),d(1),d(3)),
float3(d(1),d(3),d(3)),
float3(d(3),d(3),d(3))
};
#undef d
#define d(x) x/256.0
static const float3 aosega[16] =
{
float3(d( 0),d( 0),d( 0)),
float3(d(128),d( 0),d( 0)),
float3(d( 32),d(128),d( 0)),
float3(d(160),d( 64),d( 32)),
float3(d( 0),d( 32),d( 88)),
float3(d( 60),d( 0),d( 88)),
float3(d( 16),d(160),d(208)),
float3(d( 88),d( 88),d( 88)),
float3(d( 32),d( 32),d( 32)),
float3(d(256),d( 64),d( 64)),
float3(d( 72),d(256),d( 64)),
float3(d(256),d(224),d( 60)),
float3(d( 48),d(128),d(256)),
float3(d(192),d( 48),d(256)),
float3(d( 72),d(224),d(256)),
float3(d(256),d(256),d(256)),
};
#undef d
/* gauss stuff */
float gauss3[3] =
{
0.444814, 0.239936, 0.037657
};
float4 ScreenSize;
Texture2D TextureOriginal;
Texture2D TextureColor;
Texture2D TextureDepth;
Texture2D TextureFont
<
string ResourceName = "menbvgaluma.png";
>;
Texture2D TextureDots
<
string ResourceName = "menbdots.png";
>;
Texture2D TextureDoom
<
string ResourceName = "menbdoomlut.png";
>;
Texture2D TextureQuake
<
string ResourceName = "menbquakelut.png";
>;
Texture2D TextureVGA
<
string ResourceName = "menbvgalut.png";
>;
SamplerState Sampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Clamp;
AddressV = Clamp;
};
SamplerState SamplerB
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Border;
AddressV = Border;
};
SamplerState SamplerFont
{
Filter = MIN_LINEAR_MAG_MIP_POINT;
AddressU = Wrap;
AddressV = Wrap;
MaxLOD = 0;
MinLOD = 0;
};
SamplerState SamplerDots
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
MaxLOD = 0;
MinLOD = 0;
};
SamplerState SamplerLUT
{
Filter = MIN_MAG_MIP_POINT;
AddressU = Clamp;
AddressV = Clamp;
MaxLOD = 0;
MinLOD = 0;
};
struct VS_INPUT_POST
{
float3 pos : POSITION;
float2 txcoord : TEXCOORD0;
};
struct VS_OUTPUT_POST
{
float4 pos : SV_POSITION;
float2 txcoord : TEXCOORD0;
};
VS_OUTPUT_POST VS_PostProcess(VS_INPUT_POST IN)
{
VS_OUTPUT_POST OUT;
OUT.pos = float4(IN.pos.x,IN.pos.y,IN.pos.z,1.0);
OUT.txcoord.xy = IN.txcoord.xy;
return OUT;
}
/* helpers */
/* photometric */
#define luminance(x) dot(x,float3(0.2126,0.7152,0.0722))
/* CCIR601 */
//#define luminance(x) dot(x,float3(0.299,0.587,0.114))
float3 rgb2hsv( float3 c )
{
float4 K = float4(0.0,-1.0/3.0,2.0/3.0,-1.0);
float4 p = (c.g<c.b)?float4(c.bg,K.wz):float4(c.gb,K.xy);
float4 q = (c.r<p.x)?float4(p.xyw,c.r):float4(c.r,p.yzx);
float d = q.x-min(q.w,q.y);
float e = 1.0e-10;
return float3(abs(q.z+(q.w-q.y)/(6.0*d+e)),d/(q.x+e),q.x);
}
float3 hsv2rgb( float3 c )
{
float4 K = float4(1.0,2.0/3.0,1.0/3.0,3.0);
float3 p = abs(frac(c.x+K.xyz)*6.0-K.w);
return c.z*lerp(K.x,saturate(p-K.x),c.y);
}
/* prepass */
float4 ReducePrepass( in float4 col, in float2 coord )
{
float3 hsv = rgb2hsv(col.rgb);
hsv.y = clamp(hsv.y*bsaturation,0.0,1.0);
hsv.z = pow(max(0,hsv.z),bgamma);
col.rgb = hsv2rgb(saturate(hsv));
if ( dither == 0 )
col += bdbump+checkers[int(coord.x%2)+2*int(coord.y%2)]*bdmult;
else if ( dither == 1 )
col += bdbump+ordered2[int(coord.x%2)+2*int(coord.y%2)]*bdmult;
else if ( dither == 2 )
col += bdbump+ordered3[int(coord.x%3)+3*int(coord.y%3)]*bdmult;
else if ( dither == 3 )
col += bdbump+ordered4[int(coord.x%4)+4*int(coord.y%4)]*bdmult;
else if ( dither == 4 )
col += bdbump+ordered8[int(coord.x%8)+8*int(coord.y%8)]*bdmult;
col = saturate(col);
return col;
}
/*
CGA had seven graphic modes (320x200 modes have low/high contrast versions):
- 640x200 monochrome, which doesn't really need a palette here, as it can
be done procedurally with minimum effort.
- 320x200 black/cyan/magenta/white
- 320x200 black/green/red/brown
- 320x200 black/cyan/red/white
*/
float4 ReduceCGA( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
if ( cgapal == 0 )
{
dac.a = (dac.r+dac.g+dac.b)/3.0;
return (dac.a>0.5);
}
float dist = 2.0;
int idx = 0;
if ( cgapal == 1 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga1l[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga1l[i]);
}
color.rgb = cga1l[idx];
}
else if ( cgapal == 2 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga1h[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga1h[i]);
}
color.rgb = cga1h[idx];
}
else if ( cgapal == 3 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga2l[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga2l[i]);
}
color.rgb = cga2l[idx];
}
else if ( cgapal == 4 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga2h[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga2h[i]);
}
color.rgb = cga2h[idx];
}
else if ( cgapal == 5 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga3l[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga3l[i]);
}
color.rgb = cga3l[idx];
}
else if ( cgapal == 6 )
{
[unroll] for ( int i=0; i<4; i++ )
if ( distance(dac.rgb,cga3h[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,cga3h[i]);
}
color.rgb = cga3h[idx];
}
return color;
}
/*
EGA technically only had the 320x200 16-colour graphic mode, but when VGA
came out, it was possible to tweak the DAC, allowing for custom palettes.
AOS EGA is a palette based on my terminal colour scheme on Linux, which I
also use for AliceOS.
*/
float4 ReduceEGA( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
float dist = 2.0;
int idx = 0;
if ( egapal == 0 )
{
[unroll] for ( int i=0; i<16; i++ )
if ( distance(dac.rgb,stdega[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,stdega[i]);
}
color.rgb = stdega[idx];
}
else
{
[unroll] for ( int i=0; i<16; i++ )
if ( distance(dac.rgb,aosega[i]) < dist )
{
idx = i;
dist = distance(dac.rgb,aosega[i]);
}
color.rgb = aosega[idx];
}
return color;
}
/* A two bits per channel mode that can usually fit VGA mode 13h and mode x */
float4 ReduceRGB2( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
color.rgb = trunc(dac.rgb*4.0)/4.0;
return color;
}
/* Effectively has 256 colours, with a magenta tint due to precision loss */
float4 ReduceRGB323( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
color.rgb = trunc(dac.rgb*float3(8.0,4.0,8.0))/float3(8.0,4.0,8.0);
return color;
}
/* 4096 colours, no actual graphics hardware existed that used 4bpc, though */
float4 ReduceRGB4( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
color.rgb = trunc(dac.rgb*16.0)/16.0;
return color;
}
/*
The classic 16-bit colour mode everyone from my generation would remember,
especially that subtle green tint and the banding due to lack of dithering
in most games and GPUs at that time.
*/
float4 ReduceRGB565( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
color.rgb = trunc(dac.rgb*float3(32.0,64.0,32.0))
/float3(32.0,64.0,32.0);
return color;
}
/*
If you see no difference when using this, then it could be because your
own screen is already 6bpc. This is the case for a lot of LCDs, both old
and modern. 8bpc tends to be the norm on IPS, though. 10bpc is the next
step, but for now it's only used internally in video codecs for more
efficient compression with lower quality loss. I seem to recall that in
most *nix systems such as Linux it's possible to have 10bpc already with
NVIDIA, but it causes compatibility issues with a lot of programs.
*/
float4 ReduceRGB6( in float4 color, in float2 coord )
{
float4 dac = ReducePrepass(color,coord);
color.rgb = trunc(dac.rgb*64.0)/64.0;
return color;
}
/* Various VGA 256-colour palettes: Doom, Quake I, and the standard. */
float4 ReduceDoom( in float4 color, in float2 coord )
{
float4 dac = clamp(ReducePrepass(color,coord)+0.005,0.005,0.995);
float2 lc = float2(dac.r,dac.g/64.0+floor(dac.b*64.0)/64.0);
return TextureDoom.Sample(SamplerLUT,lc);
}
float4 ReduceQuake( in float4 color, in float2 coord )
{
float4 dac = clamp(ReducePrepass(color,coord)+0.005,0.005,0.995);
float2 lc = float2(dac.r,dac.g/64.0+floor(dac.b*64.0)/64.0);
return TextureQuake.Sample(SamplerLUT,lc);
}
float4 ReduceVGA( in float4 color, in float2 coord )
{
float4 dac = clamp(ReducePrepass(color,coord)+0.005,0.005,0.995);
float2 lc = float2(dac.r,dac.g/64.0+floor(dac.b*64.0)/64.0);
return TextureVGA.Sample(SamplerLUT,lc);
}
/* Retro rockets */
float4 PS_Retro( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !useblock ) return res;
float2 rresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float4 tcol;
float2 bresl = rresl;
if ( bres.x <= 0 || bres.y <= 0 ) bresl = rresl;
else
{
if ( bres.x <= 1.0 ) bresl.x = rresl.x*bres.x;
else bresl.x = bres.x;
if ( bres.y <= 1.0 ) bresl.y = rresl.y*bres.y;
else bresl.y = bres.y;
}
float2 sresl = sres;
if ( sres.x <= 0 ) sresl.x = rresl.x/bresl.x;
if ( sres.y <= 0 ) sresl.y = rresl.y/bresl.y;
float2 ncoord = coord*(rresl/bresl);
ncoord = (-0.5/sresl)*(rresl/bresl)+ncoord/sresl+0.5;
ncoord = floor(ncoord*bresl)/bresl;
if ( bres.x <= 0 || bres.y <= 0 ) ncoord = coord;
tcol = TextureOriginal.Sample(Sampler,ncoord);
if ( paltype == 0 ) res = ReduceCGA(tcol,(coord*rresl)/sresl);
else if ( paltype == 1 ) res = ReduceEGA(tcol,(coord*rresl)/sresl);
else if ( paltype == 2 ) res = ReduceRGB2(tcol,(coord*rresl)/sresl);
else if ( paltype == 3 ) res = ReduceRGB323(tcol,(coord*rresl)/sresl);
else if ( paltype == 4 ) res = ReduceVGA(tcol,(coord*rresl)/sresl);
else if ( paltype == 5 ) res = ReduceDoom(tcol,(coord*rresl)/sresl);
else if ( paltype == 6 ) res = ReduceQuake(tcol,(coord*rresl)/sresl);
else if ( paltype == 7 ) res = ReduceRGB4(tcol,(coord*rresl)/sresl);
else if ( paltype == 8 ) res = ReduceRGB565(tcol,(coord*rresl)/sresl);
else if ( paltype == 9 ) res = ReduceRGB6(tcol,(coord*rresl)/sresl);
else res = tcol;
if ( ncoord.x < 0 || ncoord.x >= 1 || ncoord.y < 0 || ncoord.y >= 1 )
res *= 0;
res.a = 1.0;
return res;
}
/* ASCII art (more like CP437 art) */
float4 PS_ASCII( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !asciienable ) return res;
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 fresl = float2(FONT_WIDTH,FONT_HEIGHT);
float2 cresl = float2(GLYPH_WIDTH,GLYPH_HEIGHT);
float2 bscl = floor(bresl/cresl);
/*
Here I use the "cheap" method, based on the overall luminance of each
glyph, rather than attempt to search for the best fitting glyph for
each cell. If you want to know why, take a look at the ASCII filter
bundled with the Dolphin emulator, and be prepared for the resulting
seconds per frame it runs at. The calculations needed for such a filter
are completely insane even for the highest-end GPUs.
*/
float3 col = TextureOriginal.Sample(Sampler,floor(bscl*coord)/bscl).rgb;
int lum = clamp(luminance(col)*FONT_LEVELS,0,FONT_LEVELS);
float2 itx = floor(coord*bresl);
float2 blk = floor(itx/cresl)*cresl;
float2 ofs = itx-blk;
ofs.y += lum*cresl.y;
ofs /= fresl;
float gch = TextureFont.Sample(SamplerFont,ofs).x;
if ( gch < 0.5 ) res.rgb = res.rgb*asciiblend;
else
{
if ( asciimono ) res.rgb = 1.0;
else res.rgb = col;
}
return res;
}
float4 PS_ChromaKey( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !maskenable ) return res;
if ( TextureDepth.Sample(Sampler,coord).x > maskd )
return float4(mask.r,mask.g,mask.b,1.0);
return res;
}
/* 2x2 RGBI dot matrix, not even close to anything that exists IRL but meh */
float4 PS_DotMatrix( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !dotenable ) return res;
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
bresl.xy *= 1.0/(dotsize*2.0);
float4 dac = float4(res.r*0.5,res.g*0.5,res.b*0.5,
(res.r+res.g+res.b)/6.0);
/*
There are two types of CRTs: aperture grille and shadow mask.
The former is blurry and has scanlines (rather big ones, even), but
is cheap to emulate; while the latter is the one most known for its
crisp, square pixels with minimal distortion. Most individuals into
this whole "retro graphics" stuff prefer aperture grille, which
looks like shit, then again, that's the sort of visual quality they
want. The main issue with shadow mask CRTs is that it's impossible
to accurately emulate them unless done on a screen with a HUGE
resolution. After all, the subpixels need to be clearly visible, and
if on top of it you add curvature distortion, you need to reduce
moire patterns that will inevitably show up at low resolutions.
It would be more desirable to eventually have flat panels that can
display arbitrary resolutions using a form of scaling that preserves
square pixels with unnoticeable distortion (typically, with nearest
neighbour you'd get some pixels that are bigger/smaller than others
if the upscale resolution isn't an integer multiple of the real
resolution.
This 2x2 RGBI thing is a rather naïve filter I made many years ago,
it looks unlike any real CRT, but scales well. Its only problem is
moire patterns when using the default size of 2x2.
*/
float4 dots = TextureDots.Sample(SamplerDots,coord*bresl)*dac;
float3 tcol = pow(max(0,dots.rgb+dots.a),dotpow)*dotmult;
res.rgb = res.rgb*(1-dotblend)+tcol*dotblend;
return res;
}
/* that's right, CRT curvature */
float4 PS_Curvature( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !curveenable ) return res;
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 bof = (1.0/bresl)*curvesoft;
float3 eta = float3(1+chromaab*0.009,1+chromaab*0.006,1+chromaab
*0.003);
float2 center = float2(coord.x-0.5,coord.y-0.5);
float zfact = 100.0/lenszoom;
float r2 = center.x*center.x+center.y*center.y;
float f = 1+r2*lensdist*0.01;
float x = f*zfact*center.x+0.5;
float y = f*zfact*center.y+0.5;
float2 rcoord = (f*eta.r)*zfact*(center.xy*0.5)+0.5;
float2 gcoord = (f*eta.g)*zfact*(center.xy*0.5)+0.5;
float2 bcoord = (f*eta.b)*zfact*(center.xy*0.5)+0.5;
int i,j;
float4 idist = float4(0,0,0,0);
/*
sticking a 5x5 gaussian blur with a tweakable radius in here to
attempt to reduce moire patterns in some cases. Supersampling would
be more useful for that, but ENB sucks ass through a crazy straw in
that aspect, so it would be more desirable to use GeDoSaTo (I sure
hope I can port all my stuff to it one day, at least the damn thing
is FOSS).
*/
[unroll] for ( i=-2; i<=2; i++ ) [unroll] for ( j=-2; j<=2; j++ )
{
idist += gauss3[abs(i)]*gauss3[abs(j)]
*float4(TextureColor.Sample(Sampler,rcoord+bof
*float2(i,j)).r,TextureColor.Sample(SamplerB,gcoord+bof
*float2(i,j)).g,TextureColor.Sample(SamplerB,bcoord+bof
*float2(i,j)).b,TextureColor.Sample(SamplerB,float2(x,
y)+bof*float2(i,j)).a);
}
res.rgb = idist.rgb;
return res;
}
/* Why am I doing this */
float4 PS_Blur( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !bssblurenable ) return res;
float2 ofs[16] =
{
float2(1.0,1.0), float2(-1.0,-1.0),
float2(-1.0,1.0), float2(1.0,-1.0),
float2(1.0,0.0), float2(-1.0,0.0),
float2(0.0,1.0), float2(0.0,-1.0),
float2(1.41,0.0), float2(-1.41,0.0),
float2(0.0,1.41), float2(0.0,-1.41),
float2(1.41,1.41), float2(-1.41,-1.41),
float2(-1.41,1.41), float2(1.41,-1.41)
};
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 bof = (1.0/bresl)*bssblurradius;
int i;
[unroll] for ( i=0; i<16; i++ )
res += TextureColor.Sample(Sampler,coord+ofs[i]*bof);
res /= 17.0;
res.a = 1.0;
return res;
}
float4 PS_Sharp( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !bsssharpenable ) return res;
float2 ofs[8] =
{
float2(1.0,1.0), float2(-1.0,-1.0),
float2(-1.0,1.0), float2(1.0,-1.0),
float2(1.41,1.41), float2(-1.41,-1.41),
float2(-1.41,1.41), float2(1.41,-1.41)
};
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 bof = (1.0/bresl)*bsssharpradius;
float4 tcol = res;
int i;
[unroll] for ( i=0; i<8; i++ )
tcol += TextureColor.Sample(Sampler,coord+ofs[i]*bof);
tcol /= 9.0;
float4 orig = res;
res = orig*(1.0+dot(orig.rgb-tcol.rgb,0.333333)*bsssharpamount);
float rg = clamp(pow(orig.b,3.0),0.0,1.0);
res = lerp(res,orig,rg);
res.a = 1.0;
return res;
}
float4 PS_Shift( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !bssshiftenable ) return res;
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 bof = (1.0/bresl)*bssshiftradius;
res.g = TextureColor.Sample(Sampler,coord).g;
res.r = TextureColor.Sample(Sampler,coord+float2(0,-bof.y)).r;
res.b = TextureColor.Sample(Sampler,coord+float2(0,bof.y)).b;
res.a = 1.0;
return res;
}
/* That "luma sharpen" thingy, added just because someone might want it */
float4 PS_LumaSharp( VS_OUTPUT_POST IN, float4 v0 : SV_Position0 ) : SV_Target
{
float2 coord = IN.txcoord.xy;
float4 res = TextureColor.Sample(Sampler,coord);
if ( !lsharpenable ) return res;
float2 bresl = float2(ScreenSize.x,ScreenSize.x*ScreenSize.w);
float2 bof = float2(1.0/bresl.x,1.0/bresl.y)*lsharpradius;
float4 crawling = TextureColor.Sample(Sampler,coord+float2(0,-1)*bof);
crawling += TextureColor.Sample(Sampler,coord+float2(-1,0)*bof);
crawling += TextureColor.Sample(Sampler,coord+float2(1,0)*bof);
crawling += TextureColor.Sample(Sampler,coord+float2(0,1)*bof);
crawling *= 0.25;
float4 inmyskin = res-crawling;
float thesewounds = luminance(inmyskin.rgb);
thesewounds = clamp(thesewounds,-lsharpclamp*0.01,lsharpclamp*0.01);
float4 theywillnotheal = res+thesewounds*lsharpblend;
return theywillnotheal;
}
technique11 ExtraFilters <string UIName="MariENB";>
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_LumaSharp()));
}
}
technique11 ExtraFilters1
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_Blur()));
}
}
technique11 ExtraFilters2
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_Sharp()));
}
}
technique11 ExtraFilters3
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_Shift()));
}
}
technique11 ExtraFilters4
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_ChromaKey()));
}
}
technique11 ExtraFilters5
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_Retro()));
}
}
technique11 ExtraFilters6
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_ASCII()));
}
}
technique11 ExtraFilters7
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_DotMatrix()));
}
}
technique11 ExtraFilters8
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS_PostProcess()));
SetPixelShader(CompileShader(ps_5_0,PS_Curvature()));
}
}