diff --git a/data/shaders/postfx.frag b/data/shaders/postfx.frag index 9d13c36..9a66a9f 100644 --- a/data/shaders/postfx.frag +++ b/data/shaders/postfx.frag @@ -23,9 +23,9 @@ layout(set = 3, binding = 0) uniform PostFXUniforms { float curvature; float bleeding; float pixel_scale; // physical pixels per logical pixel (vh / tex_height_) - float time; // seconds since SDL init (for future animated effects) + float time; // seconds since SDL init float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS) - float pad1; // padding — 48 bytes total (3 × 16) + float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — 48 bytes total (3 × 16) } u; // YCbCr helpers for NTSC bleeding @@ -85,8 +85,8 @@ void main() { colour = base; } - // Aberración cromática - float ca = u.chroma_strength * 0.005; + // Aberración cromática (drift animado con time para efecto NTSC real) + float ca = u.chroma_strength * 0.005 * (1.0 + 0.15 * sin(u.time * 7.3)); colour.r = texture(scene, uv + vec2(ca, 0.0)).r; colour.b = texture(scene, uv - vec2(ca, 0.0)).b; @@ -134,5 +134,11 @@ void main() { colour = mix(colour, colour * mask, u.mask_strength); } + // Parpadeo de fósforo CRT (~50 Hz) + if (u.flicker > 0.0) { + float flicker_wave = sin(u.time * 100.0) * 0.5 + 0.5; + colour *= 1.0 - u.flicker * 0.04 * flicker_wave; + } + out_color = vec4(colour, 1.0); } diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index 8bd31cb..74c8680 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -475,7 +475,7 @@ void Screen::applyCurrentPostFXPreset() { // setOversample primero: puede recrear texturas antes de que setPostFXParams // decida si hornear scanlines en CPU o aplicarlas en GPU. shader_backend_->setOversample(Options::video.supersampling ? 3 : 1); - Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding}; + Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding, .flicker = p.flicker}; shader_backend_->setPostFXParams(params); } } diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp index 492dd0a..498c066 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp @@ -57,7 +57,7 @@ struct PostFXUniforms { float pixel_scale; float time; float oversample; // 1.0 = sin SS, 3.0 = 3× supersampling - float pad1; + float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz }; // YCbCr helpers for NTSC bleeding @@ -119,8 +119,8 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]], colour = base; } - // Aberración cromática - float ca = u.chroma_strength * 0.005f; + // Aberración cromática (drift animado con time para efecto NTSC real) + float ca = u.chroma_strength * 0.005f * (1.0f + 0.15f * sin(u.time * 7.3f)); colour.r = scene.sample(samp, uv + float2(ca, 0.0f)).r; colour.b = scene.sample(samp, uv - float2(ca, 0.0f)).b; @@ -165,6 +165,12 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]], colour = mix(colour, colour * mask, u.mask_strength); } + // Parpadeo de fósforo CRT (~50 Hz) + if (u.flicker > 0.0f) { + float flicker_wave = sin(u.time * 100.0f) * 0.5f + 0.5f; + colour *= 1.0f - u.flicker * 0.04f * flicker_wave; + } + return float4(colour, 1.0f); } )"; @@ -627,6 +633,7 @@ namespace Rendering { uniforms_.gamma_strength = p.gamma; uniforms_.curvature = p.curvature; uniforms_.bleeding = p.bleeding; + uniforms_.flicker = p.flicker; // Con supersampling las scanlines se hornean en CPU (uploadPixels). // El shader recibe strength=0 para no aplicarlas de nuevo en GPU. diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp index df9d704..c1fb4ca 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp @@ -20,7 +20,7 @@ struct PostFXUniforms { float pixel_scale; // physical pixels per logical pixel (vh / tex_height_) float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f) float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS) - float pad1; // padding — keep struct at 48 bytes (3 × 16) + float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16) }; namespace Rendering { diff --git a/source/core/rendering/shader_backend.hpp b/source/core/rendering/shader_backend.hpp index 85c3dae..1e2e768 100644 --- a/source/core/rendering/shader_backend.hpp +++ b/source/core/rendering/shader_backend.hpp @@ -18,6 +18,7 @@ namespace Rendering { float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full) float curvature = 0.0F; // Curvatura barrel CRT float bleeding = 0.0F; // Sangrado de color NTSC + float flicker = 0.0F; // Parpadeo de fósforo CRT ~50 Hz }; /** diff --git a/source/game/options.cpp b/source/game/options.cpp index 9e6bf3d..a87b704 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -735,6 +735,7 @@ namespace Options { parseFloatField(p, "gamma", preset.gamma); parseFloatField(p, "curvature", preset.curvature); parseFloatField(p, "bleeding", preset.bleeding); + parseFloatField(p, "flicker", preset.flicker); // Nota: 'supersampling' era un campo por-preset (eliminado). Si existe // en el fichero del usuario se ignora silenciosamente (compatible). postfx_presets.push_back(preset); @@ -786,6 +787,7 @@ namespace Options { file << "# gamma: gamma correction input 2.4 / output 2.2\n"; file << "# curvature: CRT barrel distortion\n"; file << "# bleeding: NTSC horizontal colour bleeding\n"; + file << "# flicker: phosphor CRT flicker ~50 Hz (0.0 = off, 1.0 = max)\n"; file << "# Note: supersampling is a global toggle in config.yaml, not per-preset.\n"; file << "\n"; file << "presets:\n"; @@ -797,6 +799,7 @@ namespace Options { file << " gamma: 0.8\n"; file << " curvature: 0.0\n"; file << " bleeding: 0.0\n"; + file << " flicker: 0.0\n"; file << " - name: \"NTSC\"\n"; file << " vignette: 0.4\n"; file << " scanlines: 0.5\n"; @@ -805,6 +808,7 @@ namespace Options { file << " gamma: 0.5\n"; file << " curvature: 0.0\n"; file << " bleeding: 0.6\n"; + file << " flicker: 0.0\n"; file << " - name: \"CURVED\"\n"; file << " vignette: 0.5\n"; file << " scanlines: 0.6\n"; @@ -813,6 +817,7 @@ namespace Options { file << " gamma: 0.7\n"; file << " curvature: 0.8\n"; file << " bleeding: 0.0\n"; + file << " flicker: 0.0\n"; file << " - name: \"SCANLINES\"\n"; file << " vignette: 0.0\n"; file << " scanlines: 0.8\n"; @@ -821,6 +826,7 @@ namespace Options { file << " gamma: 0.0\n"; file << " curvature: 0.0\n"; file << " bleeding: 0.0\n"; + file << " flicker: 0.0\n"; file << " - name: \"SUBTLE\"\n"; file << " vignette: 0.3\n"; file << " scanlines: 0.4\n"; @@ -829,6 +835,16 @@ namespace Options { file << " gamma: 0.3\n"; file << " curvature: 0.0\n"; file << " bleeding: 0.0\n"; + file << " flicker: 0.0\n"; + file << " - name: \"CRT LIVE\"\n"; + file << " vignette: 0.5\n"; + file << " scanlines: 0.6\n"; + file << " chroma: 0.3\n"; + file << " mask: 0.3\n"; + file << " gamma: 0.4\n"; + file << " curvature: 0.3\n"; + file << " bleeding: 0.4\n"; + file << " flicker: 0.8\n"; file.close(); @@ -838,11 +854,12 @@ namespace Options { // Cargar los presets recién creados postfx_presets.clear(); - postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F, 0.0F, 0.0F}); - postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F}); - postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F, 0.0F}); - postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}); - postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F, 0.0F, 0.0F}); + postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F, 0.0F, 0.0F, 0.0F}); + postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F, 0.0F}); + postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F, 0.0F, 0.0F}); + postfx_presets.push_back({"SCANLINES",0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}); + postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F, 0.0F, 0.0F, 0.0F}); + postfx_presets.push_back({"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F}); current_postfx_preset = 0; return true; diff --git a/source/game/options.hpp b/source/game/options.hpp index c7bbcd1..f692a46 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -125,6 +125,7 @@ namespace Options { float gamma{0.0F}; // Corrección gamma input 2.4 / output 2.2 (0.0 = off, 1.0 = plena) float curvature{0.0F}; // Distorsión barrel CRT (0.0 = plana, 1.0 = máxima curvatura) float bleeding{0.0F}; // Sangrado de color NTSC horizontal Y/C (0.0 = off, 1.0 = máximo) + float flicker{0.0F}; // Parpadeo de fósforo CRT ~50 Hz (0.0 = off, 1.0 = máximo) }; // --- Variables globales ---