diff --git a/data/resources/shaders/reshade/Shaders/scanline-modern-ex.fx b/data/resources/shaders/reshade/Shaders/scanline-modern-ex.fx new file mode 100644 index 000000000..70441661e --- /dev/null +++ b/data/resources/shaders/reshade/Shaders/scanline-modern-ex.fx @@ -0,0 +1,139 @@ +#include "ReShade.fxh" + +/* + +This shader is not designed to simply simulate the scanline + cross grid effect of old CRT monitors. +Instead, it aims to combine the advantages of sharp clarity on modern displays with retro games, +enabling better pixel-level scaling. +The generation intensity of scanlines is dynamically quantized and adjusted based on the human eye's +perceptual curve for chromatic brightness, rather than using rigid stripe overlay. + +Core Features: +- Supports independent adjustment of vertical scanline and horizontal crossline intensity/density, + adapting to different resolutions (1080P/4K / high-magnification scaling); +- Default parameters are suitable for most pixel games scaled up on large modern 4K resolution screens, + with lossless brightness/color; +- Optimized scanline performance based on human eye brightness sensitivity curve: + scanlines are prominent in medium brightness areas and weakened in extreme brightness areas; +- Adjustable color channel quantization attenuation, suitable for games like GBA that have + hardware-encoded gamma bias (over-bright/grayed out) due to lack of backlighting. + It can perfectly restore vivid colors. + + Perceptual Sensitivity Curve: + Sensitivity + ↑ + | Peak Sensitivity (0.4-0.6 Luma) + | / \ + | / \ + | / \____ High-luma roll-off + | / + +--------------------→ Luminance (0.0 - 1.0) + + * (C) 2025-2026 by crashGG. +*/ + +// --- UI Parameters --- + +uniform float sinCompY < + ui_type = "drag"; + ui_min = 0.0; + ui_max = 1.0; + ui_label = "Vertical Scanline Intensity"; + ui_tooltip = "Intensity of horizontal lines (Y-axis oscillation)."; +> = 0.1; + +uniform float sinCompX < + ui_type = "drag"; + ui_min = 0.0; + ui_max = 0.10; + ui_step = 0.01; + ui_label = "Horizontal Grid Intensity"; + ui_tooltip = "Intensity of vertical lines (X-axis oscillation), creating a shadow mask effect."; +> = 0.01; + +uniform float densY < + ui_type = "drag"; + ui_min = 1.0; + ui_max = 6.0; + ui_step = 1.0; + ui_label = "Scanline Density"; + ui_tooltip = "Frequency of scanlines. Recommended: 3.0 for 1080P, 2.0 for 4K."; +> = 3.0; + +uniform float densX < + ui_type = "drag"; + ui_min = 0.5; + ui_max = 1.0; + ui_step = 0.5; + ui_label = "Grid Density"; + ui_tooltip = "Density multiplier for the horizontal grid."; +> = 1.0; + +uniform float colAtten < + ui_type = "drag"; + ui_min = 0.0; + ui_max = 2.0; + ui_label = "Chroma Attenuation"; + ui_tooltip = "Adjusts color quantization depth. Useful for correcting washed-out retro titles (e.g., GBA)."; +> = 0.0; + +#define PI 3.141592654 + +// --- Pixel Shader --- + +float4 PS_SineScanline(float4 vpos : SV_POSITION, float2 texcoord : TEXCOORD0) : SV_Target +{ + // 1. Texture Sampling + // Apply a micro-offset (1.0001) to UVs to prevent edge bleeding on certain hardware + float2 uv = texcoord * 1.0001; + float3 res = tex2D(ReShade::BackBuffer, uv).rgb; + + // 2. Frequency Calculation (Omega) + // Map coordinate space to angular frequency. + // Uses 1.999 factor on Y-axis to avoid integer-multiple aliasing (Moire patterns). + float2 omega = float2( + PI * BUFFER_WIDTH * densX, + 1.999 * PI * BUFFER_HEIGHT / densY + ); + + // 3. Sine Wave Generation + // Project UVs into periodic sine space for smooth transitions + float2 tex_omega_product = uv * omega; + float2 sine_wave = sin(tex_omega_product); + + // 4. Amplitude Modulation + float2 scaled_sine_wave = float2(sinCompX, sinCompY) * sine_wave; + + // 5. Signal Summation + // Combine X and Y oscillations into a single scalar fluctuation value + float total_sine_fluctuation = scaled_sine_wave.x + scaled_sine_wave.y; + + // 6. Luma-Aware Perceptual Weighting (dist) + // Calculates squared distance from neutral gray (0.5). + // This ensures scanlines fade out in pure blacks and pure whites. + float3 dist_linear = abs(res - 0.5) * 2.0; + float3 dist = dist_linear * dist_linear; + + // 7. Final Luma Modulation + // Component A: Chroma attenuation based on grid intensity + // Component B: Dynamic sine oscillation + // Both components are gated by the 'weight' (1.0 - dist) for perceptual balance. + float3 weight = 1.0 - dist; + float3 final_brightness = 1.0 - ((sinCompX + sinCompY) * weight * colAtten) + total_sine_fluctuation * weight; + + // 8. Output Composition + float3 scanline = res * final_brightness; + + return float4(scanline, 1.0); +} + +// --- Technique Definition --- + +technique Modern_Sine_Scanlines +{ + pass P0 + { + VertexShader = PostProcessVS; + PixelShader = PS_SineScanline; + } +}