crt-hyllian-sinc.fx (12221B)
1 #include "ReShade.fxh" 2 3 /* 4 Hyllian's CRT-sinc Shader 5 6 Copyright (C) 2011-2024 Hyllian 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 */ 26 27 28 29 uniform int HFILTER_PROFILE < 30 ui_type = "combo"; 31 ui_items = "Custom\0Composite\0Composite Soft\0"; 32 ui_label = "H-FILTER PROFILE"; 33 > = 0; 34 35 uniform float SHP < 36 ui_type = "drag"; 37 ui_min = 0.50; 38 ui_max = 1.0; 39 ui_step = 0.01; 40 ui_label = "CUSTOM H-FILTER SHARPNESS"; 41 > = 1.0; 42 43 uniform bool CRT_ANTI_RINGING < 44 ui_type = "radio"; 45 ui_label = "ANTI RINGING"; 46 > = true; 47 48 uniform bool SHARPNESS_HACK < 49 ui_type = "radio"; 50 ui_label = "SHARPNESS HACK"; 51 > = false; 52 53 uniform float CRT_InputGamma < 54 ui_type = "drag"; 55 ui_min = 1.0; 56 ui_max = 5.0; 57 ui_step = 0.1; 58 ui_label = "INPUT GAMMA"; 59 > = 2.4; 60 61 uniform float CRT_OutputGamma < 62 ui_type = "drag"; 63 ui_min = 1.0; 64 ui_max = 5.0; 65 ui_step = 0.05; 66 ui_label = "OUTPUT GAMMA"; 67 > = 2.2; 68 69 uniform int MASK_LAYOUT < 70 ui_type = "combo"; 71 ui_items = "0-Off\0" 72 "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" 73 "7-Shadow Classic\0""8-Shadow1 1080p\0""9-Shadow2 1080p\0""10-Shadow1 4k\0" 74 "11-Slot1 1080p\0""12-Slot2 1080p\0""13-Slot1 4k\0""14-Slot1 4k\0""15-Slot1 8k\0"; 75 ui_category = "CRT Mask"; 76 ui_label = "MASK LAYOUT"; 77 > = 1; 78 79 uniform int MONITOR_SUBPIXELS < 80 ui_type = "combo"; 81 ui_items = "RGB\0BGR\0"; 82 ui_category = "CRT Mask"; 83 ui_label = "MONITOR SUBPIXELS LAYOUT"; 84 > = 0; 85 86 uniform float BRIGHTBOOST < 87 ui_type = "drag"; 88 ui_min = 0.0; 89 ui_max = 3.0; 90 ui_step = 0.05; 91 ui_label = "BRIGHTNESS BOOST"; 92 > = 1.0; 93 94 uniform float BEAM_MIN_WIDTH < 95 ui_type = "drag"; 96 ui_min = 0.0; 97 ui_max = 1.0; 98 ui_step = 0.01; 99 ui_label = "MIN BEAM WIDTH"; 100 > = 0.86; 101 102 uniform float BEAM_MAX_WIDTH < 103 ui_type = "drag"; 104 ui_min = 0.0; 105 ui_max = 1.0; 106 ui_step = 0.01; 107 ui_label = "MAX BEAM WIDTH"; 108 > = 1.0; 109 110 uniform float SCANLINES_STRENGTH < 111 ui_type = "drag"; 112 ui_min = 0.0; 113 ui_max = 1.0; 114 ui_step = 0.01; 115 ui_label = "SCANLINES STRENGTH"; 116 > = 0.72; 117 118 uniform int SCANLINES_SHAPE < 119 ui_type = "combo"; 120 ui_items = "Sinc\0Gaussian\0"; 121 ui_label = "SCANLINES SHAPE"; 122 > = 1.0; 123 124 uniform float SCANLINES_CUTOFF < 125 ui_type = "drag"; 126 ui_min = 0.0; 127 ui_max = 1000.0; 128 ui_step = 1.0; 129 ui_label = "SCANLINES CUTOFF"; 130 ui_tooltip = "Max vertical native resolution above which scanlines are disabled."; 131 > = 390.0; 132 133 uniform bool SCANLINES_HIRES < 134 ui_type = "radio"; 135 ui_label = "HIGH RESOLUTION SCANLINES"; 136 > = false; 137 138 uniform float POST_BRIGHTNESS < 139 ui_type = "drag"; 140 ui_min = 1.0; 141 ui_max = 3.0; 142 ui_step = 0.05; 143 ui_label = "POST-BRIGHTNESS"; 144 > = 1.00; 145 146 uniform bool VSCANLINES < 147 ui_type = "radio"; 148 ui_label = "VERTICAL SCANLINES"; 149 > = false; 150 151 152 uniform float2 NormalizedNativePixelSize < source = "normalized_native_pixel_size"; >; 153 uniform float BufferWidth < source = "bufferwidth"; >; 154 uniform float BufferHeight < source = "bufferheight"; >; 155 uniform float2 BufferToViewportRatio < source = "buffer_to_viewport_ratio"; >; 156 uniform float2 ViewportSize < source = "viewportsize"; >; 157 uniform float ViewportWidth < source = "viewportwidth"; >; 158 uniform float ViewportHeight < source = "viewportheight"; >; 159 uniform float UpscaleMultiplier < source = "upscale_multiplier"; >; 160 161 162 #include "../misc/include/mask.fxh" 163 #include "../misc/include/geom.fxh" 164 165 166 sampler2D sBackBuffer{Texture=ReShade::BackBufferTex;AddressU=BORDER;AddressV=BORDER;AddressW=BORDER;MagFilter=POINT;MinFilter=POINT;}; 167 168 texture2D tBackBufferLinear{Width=BUFFER_WIDTH;Height=BUFFER_HEIGHT;Format=RGBA16f;}; 169 sampler2D sBackBufferLinear{Texture=tBackBufferLinear;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=POINT;MinFilter=POINT;}; 170 171 #define GAMMA_IN(color) pow(color, float3(CRT_InputGamma, CRT_InputGamma, CRT_InputGamma)) 172 #define GAMMA_OUT(color) pow(color, float3(1.0 / CRT_OutputGamma, 1.0 / CRT_OutputGamma, 1.0 / CRT_OutputGamma)) 173 174 #define SCANLINES_STRENGTH (-0.16*SCANLINES_SHAPE+SCANLINES_STRENGTH) 175 #define CORNER_SMOOTHNESS (80.0*pow(CORNER_SMOOTHNESS,10.0)) 176 177 #define pi 3.1415926535897932384626433832795 178 179 #define RADIUS 2.0 // No need for more than 2-taps 180 181 float2 get_hfilter_profile() 182 { 183 float2 hf_profile = float2(SHP, RADIUS); 184 185 if (HFILTER_PROFILE == 1) hf_profile = float2(0.78, 2.0); // SNES composite 186 else if (HFILTER_PROFILE == 2) hf_profile = float2(0.65, 2.0); // Genesis composite 187 188 return hf_profile; 189 } 190 191 /* Some window functions for tests. */ 192 float4 sinc(float4 x) { return sin(pi*x)*(1.0/(pi*x+0.001.xxxx)); } 193 float4 hann_window(float4 x) { return 0.5 * ( 1.0 - cos( 0.5 * pi * ( x + 2.0 ) ) ); } 194 float4 blackman_window(float4 x) { return 0.42 - 0.5*cos(0.5*pi*(x+2.0)) + 0.08*cos(pi*(x+2.0)); } 195 float4 lanczos(float4 x, float a) { return sinc(x) * sinc(x / a); } 196 float4 blackman(float4 x, float a) { return sinc(x) * blackman_window(x); } 197 float4 hann(float4 x, float a) { return sinc(x) * hann_window(x); } 198 199 float4 resampler4(float4 x, float2 hfp) 200 { 201 return blackman(x * hfp.x, hfp.y); 202 } 203 204 205 #define wa (0.5*pi) 206 #define wb (pi) 207 208 float3 resampler3(float3 x) 209 { 210 float3 res; 211 212 res.x = (x.x<=0.001) ? 1.0 : sin(x.x*wa)*sin(x.x*wb)/(wa*wb*x.x*x.x); 213 res.y = (x.y<=0.001) ? 1.0 : sin(x.y*wa)*sin(x.y*wb)/(wa*wb*x.y*x.y); 214 res.z = (x.z<=0.001) ? 1.0 : sin(x.z*wa)*sin(x.z*wb)/(wa*wb*x.z*x.z); 215 216 return res; 217 } 218 219 float3 get_scanlines(float3 d0, float3 d1, float3 color0, float3 color1) 220 { 221 if (SCANLINES_SHAPE > 0.5) { 222 d0 = exp(-16.0*d0*d0); 223 d1 = exp(-16.0*d1*d1); 224 } 225 else { 226 d0 = clamp(2.0*d0, 0.0, 1.0); 227 d1 = clamp(2.0*d1, 0.0, 1.0); 228 d0 = resampler3(d0); 229 d1 = resampler3(d1); 230 } 231 232 return (BRIGHTBOOST*(color0*d0+color1*d1)); 233 } 234 235 float4 PS_BackBufferLinear(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD) : SV_Target 236 { 237 // float2 tc = (floor(vTexCoord / NormalizedNativePixelSize) + 0.5.xx) * NormalizedNativePixelSize; 238 239 return float4(GAMMA_IN(tex2D(sBackBuffer, vTexCoord).rgb), 1.0); 240 } 241 242 struct ST_VertexOut 243 { 244 float2 sinangle : TEXCOORD1; 245 float2 cosangle : TEXCOORD2; 246 float3 stretch : TEXCOORD3; 247 float2 TextureSize : TEXCOORD4; 248 }; 249 250 251 // Vertex shader generating a triangle covering the entire screen 252 void VS_CRT_Geom(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD, out ST_VertexOut vVARS) 253 { 254 texcoord.x = (id == 2) ? 2.0 : 0.0; 255 texcoord.y = (id == 1) ? 2.0 : 0.0; 256 position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); 257 258 // Screen centering 259 texcoord = texcoord - float2(centerx,centery)/100.0; 260 261 float2 SourceSize = 1.0/NormalizedNativePixelSize; 262 float shp_hack = 1.0 + float(SHARPNESS_HACK); 263 264 265 // Precalculate a bunch of useful values we'll need in the fragment 266 // shader. 267 vVARS.sinangle = sin(float2(geom_x_tilt, geom_y_tilt)); 268 vVARS.cosangle = cos(float2(geom_x_tilt, geom_y_tilt)); 269 vVARS.stretch = maxscale(vVARS.sinangle, vVARS.cosangle); 270 vVARS.TextureSize = lerp(float2(shp_hack*SourceSize.x, SourceSize.y), float2(SourceSize.x, shp_hack*SourceSize.y), VSCANLINES); 271 } 272 273 274 float4 PS_CRT_Hyllian(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD0, in ST_VertexOut vVARS) : SV_Target 275 { 276 float2 OutputSize = float2(BufferWidth, BufferHeight); 277 278 float2 TextureSize = vVARS.TextureSize; 279 280 float2 dx = lerp(float2(1.0/TextureSize.x, 0.0), float2(0.0, 1.0/TextureSize.y), VSCANLINES); 281 float2 dy = lerp(float2(0.0, 1.0/TextureSize.y), float2(1.0/TextureSize.x, 0.0), VSCANLINES); 282 283 // Texture coordinates of the texel containing the active pixel. 284 float2 WarpedTexCoord = (geom_curvature == true) ? transform(vTexCoord, vVARS.sinangle, vVARS.cosangle, vVARS.stretch) : vTexCoord; 285 286 float cval = corner((WarpedTexCoord-0.5.xx) * BufferToViewportRatio + 0.5.xx); 287 288 float2 pix_coord = WarpedTexCoord*TextureSize - 0.5.xx; 289 290 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; 291 292 float2 fp = lerp(frac(pix_coord), frac(pix_coord.yx), VSCANLINES); 293 294 float3 c00 = tex2D(sBackBufferLinear, tc - dx).xyz; 295 float3 c01 = tex2D(sBackBufferLinear, tc ).xyz; 296 float3 c02 = tex2D(sBackBufferLinear, tc + dx).xyz; 297 float3 c03 = tex2D(sBackBufferLinear, tc + 2.0*dx).xyz; 298 299 float3 c10, c11, c12, c13; 300 301 if (SCANLINES_HIRES == false) 302 { 303 c10 = tex2D(sBackBufferLinear, tc - dx + dy).xyz; 304 c11 = tex2D(sBackBufferLinear, tc + dy).xyz; 305 c12 = tex2D(sBackBufferLinear, tc + dx + dy).xyz; 306 c13 = tex2D(sBackBufferLinear, tc + 2.0*dx + dy).xyz; 307 } 308 else { c10 = c00; c11 = c01; c12 = c02; c13 = c03;} 309 310 float4x3 color_matrix0 = float4x3(c00, c01, c02, c03); 311 float4x3 color_matrix1 = float4x3(c10, c11, c12, c13); 312 313 float2 hfp = get_hfilter_profile(); 314 315 float4 weights = resampler4(float4(1.0+fp.x, fp.x, 1.0-fp.x, 2.0-fp.x), hfp); 316 317 float3 color0 = mul(weights, color_matrix0)/dot(weights, 1.0.xxxx); 318 float3 color1 = mul(weights, color_matrix1)/dot(weights, 1.0.xxxx); 319 320 // Get min/max samples 321 float3 min_sample0 = min(c01,c02); 322 float3 max_sample0 = max(c01,c02); 323 float3 min_sample1 = min(c11,c12); 324 float3 max_sample1 = max(c11,c12); 325 326 // Anti-ringing 327 float3 aux = color0; 328 color0 = clamp(color0, min_sample0, max_sample0); 329 color0 = lerp(aux, color0, CRT_ANTI_RINGING); 330 aux = color1; 331 color1 = clamp(color1, min_sample1, max_sample1); 332 color1 = lerp(aux, color1, CRT_ANTI_RINGING); 333 334 float pos0 = fp.y; 335 float pos1 = 1 - fp.y; 336 337 float3 lum0 = lerp(BEAM_MIN_WIDTH.xxx, BEAM_MAX_WIDTH.xxx, color0); 338 float3 lum1 = lerp(BEAM_MIN_WIDTH.xxx, BEAM_MAX_WIDTH.xxx, color1); 339 340 float3 d0 = SCANLINES_STRENGTH*pos0/(lum0*lum0+0.0000001.xxx); 341 float3 d1 = SCANLINES_STRENGTH*pos1/(lum1*lum1+0.0000001.xxx); 342 343 float3 color = (vVARS.TextureSize.y <= SCANLINES_CUTOFF) ? get_scanlines(d0, d1, color0, color1) : tex2D(sBackBufferLinear, WarpedTexCoord.xy).xyz; 344 345 color *= BRIGHTBOOST; 346 347 color = GAMMA_OUT(color); 348 349 float2 mask_coords =vTexCoord.xy * OutputSize.xy; 350 351 mask_coords = lerp(mask_coords.xy, mask_coords.yx, VSCANLINES); 352 353 color.rgb*=GAMMA_OUT(mask_weights(mask_coords, MASK_LAYOUT, MONITOR_SUBPIXELS, MASK_DARK_STRENGTH, MASK_LIGHT_STRENGTH)); 354 355 float4 res = float4(POST_BRIGHTNESS*color, 1.0); 356 357 res.rgb = res.rgb * cval.xxx; 358 359 return float4(res.rgb, 1.0); 360 } 361 362 technique CRT_Hyllian 363 { 364 pass 365 { 366 VertexShader = PostProcessVS; 367 PixelShader = PS_BackBufferLinear; 368 RenderTarget = tBackBufferLinear; 369 } 370 pass 371 { 372 VertexShader = VS_CRT_Geom; 373 PixelShader = PS_CRT_Hyllian; 374 } 375 }