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,
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,
- Supports independent adjustment of vertical scanline and horizontal aperture grille 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,
- Default parameters are suitable for most pixel games scaled up on modern 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;
- For PS1 games:Set to match the original internal resolution for pixel-perfect scanline alignment.
Perceptual Sensitivity Curve:
Sensitivity
@ -30,15 +29,7 @@ Core Features:
* (C) 2025-2026 by crashGG.
*/
// --- UI Uniforms ---
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;
// --- UI Parameters ---
uniform float sinCompY <
ui_type = "drag";
ui_min = 0.0; ui_max = 0.50; ui_step = 0.01;
@ -48,28 +39,30 @@ uniform float sinCompY <
uniform float CompXlevl <
ui_type = "drag";
ui_min = 0.0; ui_max = 20.0; ui_step = 1.0;
ui_label = "Shadow Mask Strength (Horizontal)";
ui_min = 0.0; ui_max = 20.0; ui_step = 0.5;
ui_label = "Aperture Grille Level (Horizontal)";
ui_category = "Scanline Settings";
> = 3.0;
uniform float densY <
ui_type = "slider";
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0;
ui_label = "Scanline Density";
ui_type = "drag";
ui_min = 2.0; ui_max = 10.0; ui_step = 0.5;
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";
> = 2.0;
> = 4.0;
uniform float densX <
ui_type = "slider";
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0;
ui_label = "Shadow Mask Density";
ui_type = "drag";
ui_min = 2.0; ui_max = 10.0; ui_step = 0.5;
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";
> = 3.0;
> = 2.0;
static const float PI = 3.1415926536;
// --- Vertex to Fragment Bridge ---
// --- Structs ---
struct v2f {
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
@ -79,18 +72,17 @@ struct v2f {
// --- Vertex Shader ---
v2f VS_Scanline(uint id : SV_VertexID) {
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.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);
// 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
// Locked to source resolution via scale factor to ensure grid alignment across varying viewports
o.omega = PI * 2.0 * float2(BUFFER_WIDTH * densX, BUFFER_HEIGHT * densY) * scale;
// Compute angular frequency to control sine wave periods.
// Approach: Use exact multi-integer frequencies to guarantee perfect alignment between scanlines and the native pixel grid.
// 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;
}
@ -98,29 +90,31 @@ v2f VS_Scanline(uint id : SV_VertexID) {
// --- Pixel Shader ---
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;
// Sample source texture (backbuffer)
// Center point tap
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;
// Generate periodic luminosity fluctuation [-1.0, 1.0]
// Apply -0.5 * PI phase shift to align wave troughs (dark lines) with pixel boundaries
float2 sine_wave = sin(tex_omega_product - 0.5 * PI);
// Calculate horizontal and vertical sine wave oscillations to generate periodic brightness variations in [-1.0, 1.0].
// Applies a -0.5 * PI (90-degree) phase shift to anchor the wave troughs (-1.0) exactly at pixel boundaries.
// 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
// 2. Linear accumulation of horizontal/vertical waves for final gain scalar
// 1. Modulate sine wave intensity via component-wise multiplication.
// 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);
// Luma-dependent weighting: calculate distance from mid-tone (0.5)
// Scanline depth is maximized at 0.5 luma and attenuated at extremes to simulate CRT bloom
// Core dynamic quantization logic: distance from the mid-gray value (0.5) per channel, yielding vec3 [0.0 - 1.0].
// '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;
// Composite final brightness:
// Apply modulation gain adjusted by local luma distance, then multiply by source
// Composite final scanline lighting effects:
// 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 scanline = texel * final_brightness;

Loading…
Cancel
Save