optimization scanline-modern shader (#3742)

optimization scanline-modern shader
pull/3743/head
crashGG 2 months ago committed by GitHub
parent 1758f43d93
commit 32af8280ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,20 +2,19 @@
/* /*
This shader is not designed to simply simulate the scanline + cross grid effect of old CRT monitors. This shader is not designed to simply simulate the scanline + aperture grille effect of old CRT monitors.
Instead, it aims to combine the advantages of sharp clarity on modern displays with retro games, Instead, it aims to combine the advantages of sharp clarity on modern displays with retro games,
enabling better pixel-level scaling. enabling better pixel-level scaling.
The generation intensity of scanlines is dynamically quantized and adjusted based on the human eye's 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. perceptual curve for chromatic brightness, rather than using rigid stripe overlay.
Core Features: Core Features:
- Supports independent adjustment of vertical scanline and horizontal crossline intensity/density, - Supports independent adjustment of vertical scanline and horizontal aperture grille intensity/density,
adapting to different resolutions (1080P/4K / high-magnification scaling); 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, - Default parameters are suitable for most pixel games scaled up on modern resolution screens,
with lossless brightness/color; with lossless brightness/color;
- Optimized scanline performance based on human eye brightness sensitivity curve: - Optimized scanline performance based on human eye brightness sensitivity curve:
scanlines are prominent in medium brightness areas and weakened in extreme brightness areas; scanlines are prominent in medium brightness areas and weakened in extreme brightness areas;
- For PS1 games:Set to match the original internal resolution for pixel-perfect scanline alignment.
Perceptual Sensitivity Curve: Perceptual Sensitivity Curve:
Sensitivity Sensitivity
@ -30,15 +29,7 @@ Core Features:
* (C) 2025-2026 by crashGG. * (C) 2025-2026 by crashGG.
*/ */
// --- UI Uniforms --- // --- UI Parameters ---
uniform float oriVert <
ui_type = "drag";
ui_min = 192.0; ui_max = 288.0; ui_step = 8.0;
ui_label = "Source Vertical Resolution";
ui_tooltip = "Set to match the original internal resolution for pixel-perfect scanline alignment.";
ui_category = "Scanline Settings";
> = 240.0;
uniform float sinCompY < uniform float sinCompY <
ui_type = "drag"; ui_type = "drag";
ui_min = 0.0; ui_max = 0.50; ui_step = 0.01; ui_min = 0.0; ui_max = 0.50; ui_step = 0.01;
@ -48,28 +39,30 @@ uniform float sinCompY <
uniform float CompXlevl < uniform float CompXlevl <
ui_type = "drag"; ui_type = "drag";
ui_min = 0.0; ui_max = 20.0; ui_step = 1.0; ui_min = 0.0; ui_max = 20.0; ui_step = 0.5;
ui_label = "Shadow Mask Strength (Horizontal)"; ui_label = "Aperture Grille Level (Horizontal)";
ui_category = "Scanline Settings"; ui_category = "Scanline Settings";
> = 3.0; > = 3.0;
uniform float densY < uniform float densY <
ui_type = "slider"; ui_type = "drag";
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0; ui_min = 2.0; ui_max = 10.0; ui_step = 0.5;
ui_label = "Scanline Density"; ui_label = "Scanline Period (Vertical Pixels)";
ui_tooltip = "Number of physical screen pixels per full scanline cycle. Integer values yield better results.";
ui_category = "Scanline Settings"; ui_category = "Scanline Settings";
> = 2.0; > = 4.0;
uniform float densX < uniform float densX <
ui_type = "slider"; ui_type = "drag";
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0; ui_min = 2.0; ui_max = 10.0; ui_step = 0.5;
ui_label = "Shadow Mask Density"; ui_label = "Aperture Grille Period (Horizontal Pixels)";
ui_tooltip = "Number of physical screen pixels per full aperture grille cycle. Integer values yield better results.";
ui_category = "Scanline Settings"; ui_category = "Scanline Settings";
> = 3.0; > = 2.0;
static const float PI = 3.1415926536; static const float PI = 3.1415926536;
// --- Vertex to Fragment Bridge --- // --- Structs ---
struct v2f { struct v2f {
float4 pos : SV_Position; float4 pos : SV_Position;
float2 uv : TEXCOORD0; float2 uv : TEXCOORD0;
@ -79,18 +72,17 @@ struct v2f {
// --- Vertex Shader --- // --- Vertex Shader ---
v2f VS_Scanline(uint id : SV_VertexID) { v2f VS_Scanline(uint id : SV_VertexID) {
v2f o; v2f o;
// Standard full-screen triangle generation // Standard full-screen triangle logic for ReShade.fxh
o.uv.x = (id == 2) ? 2.0 : 0.0; o.uv.x = (id == 2) ? 2.0 : 0.0;
o.uv.y = (id == 1) ? 2.0 : 0.0; o.uv.y = (id == 1) ? 2.0 : 0.0;
o.pos = float4(o.uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); o.pos = float4(o.uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
// Calculate vertical scaling factor based on source vs. output height
// BUFFER_RCP_HEIGHT is a pre-calculated constant (1.0 / Height) to avoid runtime division
float scale = oriVert * BUFFER_RCP_HEIGHT;
// Calculate angular frequency (omega) to control sine wave cycles // Compute angular frequency to control sine wave periods.
// Locked to source resolution via scale factor to ensure grid alignment across varying viewports // Approach: Use exact multi-integer frequencies to guarantee perfect alignment between scanlines and the native pixel grid.
o.omega = PI * 2.0 * float2(BUFFER_WIDTH * densX, BUFFER_HEIGHT * densY) * scale; // Logic: Angular Frequency = 2 * PI * (Screen Resolution / Target Pixel Period)
o.omega = PI * 2.0 * float2(BUFFER_WIDTH, BUFFER_HEIGHT) / float2(densX, densY);
return o; return o;
} }
@ -98,29 +90,31 @@ v2f VS_Scanline(uint id : SV_VertexID) {
// --- Pixel Shader --- // --- Pixel Shader ---
float4 PS_Scanline(v2f i) : SV_Target { float4 PS_Scanline(v2f i) : SV_Target {
// Step size for horizontal mask intensity // Granularity step of 0.005 per level
float sinCompX = CompXlevl * 0.005; float sinCompX = CompXlevl * 0.005;
// Sample source texture (backbuffer) // Center point tap
float3 texel = tex2D(ReShade::BackBuffer, i.uv).rgb; float3 texel = tex2D(ReShade::BackBuffer, i.uv).rgb;
// Map texture coordinates to sine wave phase // Signal shaping: Map UV coordinates to sine wave phase
float2 tex_omega_product = i.uv * i.omega; float2 tex_omega_product = i.uv * i.omega;
// Generate periodic luminosity fluctuation [-1.0, 1.0] // Calculate horizontal and vertical sine wave oscillations to generate periodic brightness variations in [-1.0, 1.0].
// Apply -0.5 * PI phase shift to align wave troughs (dark lines) with pixel boundaries // Applies a -0.5 * PI (90-degree) phase shift to anchor the wave troughs (-1.0) exactly at pixel boundaries.
float2 sine_wave = sin(tex_omega_product - 0.5 * PI);
// Note: Adjusts phase when density is 2.0 to prevent scanline cancellation caused by Nyquist spatial sampling dead zones.
float2 sine_wave = sin(tex_omega_product - 0.5 * PI * float2(densX>2.0,densY>2.0));
// 1. Modulate sine wave intensity per axis // 1. Modulate sine wave intensity via component-wise multiplication.
// 2. Linear accumulation of horizontal/vertical waves for final gain scalar // 2. Linearly blend horiz/vert sine waves to synthesize the final scalar gain for the luminance oscillation.
float total_sine_fluctuation = (sinCompX * sine_wave.x) + (sinCompY * sine_wave.y); float total_sine_fluctuation = (sinCompX * sine_wave.x) + (sinCompY * sine_wave.y);
// Luma-dependent weighting: calculate distance from mid-tone (0.5) // Core dynamic quantization logic: distance from the mid-gray value (0.5) per channel, yielding vec3 [0.0 - 1.0].
// Scanline depth is maximized at 0.5 luma and attenuated at extremes to simulate CRT bloom // 'dist' approaches 0 near mid-tones (0.5) where attenuation is maximum; oscillation dampens at extreme brightness levels.
float3 dist = abs(texel - 0.5) * 2.0; float3 dist = abs(texel - 0.5) * 2.0;
// Composite final brightness: // Composite final scanline lighting effects:
// Apply modulation gain adjusted by local luma distance, then multiply by source // Modulate base brightness (1.0) with scanline oscillation, then multiply back into the original texel.
float3 final_brightness = 1.0 + total_sine_fluctuation * (1.0 - dist); float3 final_brightness = 1.0 + total_sine_fluctuation * (1.0 - dist);
float3 scanline = texel * final_brightness; float3 scanline = texel * final_brightness;

Loading…
Cancel
Save