mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 07:35:41 +00:00
cf15591704
* 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.
151 lines
5 KiB
HLSL
151 lines
5 KiB
HLSL
#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;
|
|
}
|
|
}
|