postfx: scanlines analítiques amb smoothstep a l'estil crtpi, exposant forma per preset YAML

This commit is contained in:
JailDesigner committed 2026-04-20 20:57:14 +02:00
1 parent 4dc2654a66
commit c9e6261cf1
8 files changed
+1287 -901

No files matched your search

@@ -4,6 +4,11 @@
// Fragment shader del shader "postfx": vignette, chroma, scanlines, mask,
// gamma, curvature, bleeding i flicker. Els paràmetres venen via uniforms.
//
// IMPORTANT: mantenir sincronitzat a mà amb data/shaders/postfx.frag. SDL3 GPU
// compila aquest string MSL en runtime; no hi ha generador automàtic. Qualsevol
// canvi a la struct d'uniforms o a la lògica del GLSL cal replicar-lo ací al
// mateix commit. Mida total = 64 bytes (4 × vec4).
namespace Rendering::Msl {
inline constexpr const char* kPostfxFrag = R"(
@@ -28,6 +33,11 @@ struct PostFXUniforms {
float time;
float oversample;
float flicker;
// vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
float scan_dark_ratio;
float scan_dark_floor;
float scan_edge_soft;
float pad3;
};
static float3 rgb_to_ycc(float3 rgb) {
@@ -93,15 +103,19 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
colour = mix(colour, lin, u.gamma_strength);
}
const float SCAN_DARK_RATIO = 0.333f;
const float SCAN_DARK_FLOOR = 0.42f;
// Scanlines — 3 subpíxels per fila lògica (2 brillants + 1 fosca). Transició
// suavitzada amb smoothstep d'ample ≈ 1 píxel físic (estil crtpi: filtratge
// analític continu). scan_edge_soft = 0 recupera el step dur de l'original.
if (u.scanline_strength > 0.0f) {
float ps = max(1.0f, round(u.pixel_scale));
float frac_in_row = fract(uv.y * u.screen_height);
float row_pos = floor(frac_in_row * ps);
float bright_rows = (ps < 2.0f) ? ps : ((ps < 3.0f) ? 1.0f : floor(ps * (1.0f - SCAN_DARK_RATIO)));
float is_dark = step(bright_rows, row_pos);
float scan = mix(1.0f, SCAN_DARK_FLOOR, is_dark);
float ps = max(u.pixel_scale, 1.0f);
float sub = fract(uv.y * u.screen_height);
float dark_center = 1.0f - u.scan_dark_ratio * 0.5f;
float d = abs(sub - dark_center);
d = min(d, 1.0f - d);
float half_width = u.scan_dark_ratio * 0.5f;
float softness = u.scan_edge_soft * 0.5f / ps;
float band = 1.0f - smoothstep(half_width - softness, half_width + softness, d);
float scan = mix(1.0f, u.scan_dark_floor, band);
colour *= mix(1.0f, scan, u.scanline_strength);
}
@@ -204,6 +204,12 @@ namespace Rendering {
const SDL_GPUViewport VP = computeViewport(scaling_mode_, sw, sh, game_width_, game_height_);
SDL_SetGPUViewport(pass, &VP);
// pixel_scale = píxels físics per fila lògica al viewport. El shader l'usa
// per ajustar l'ample del smoothstep de scanlines a 1 píxel físic (estil crtpi).
uniforms_.pixel_scale = (game_height_ > 0)
? VP.h / static_cast<float>(game_height_)
: 1.0F;
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
SDL_GPUTextureSamplerBinding binding{};
@@ -234,6 +240,9 @@ namespace Rendering {
uniforms_.curvature = params.curvature;
uniforms_.bleeding = params.bleeding;
uniforms_.flicker = params.flicker;
uniforms_.scan_dark_ratio = params.scan_dark_ratio;
uniforms_.scan_dark_floor = params.scan_dark_floor;
uniforms_.scan_edge_soft = params.scan_edge_soft;
}
void SDL3GPUShader::setVSync(bool vsync) {
@@ -11,20 +11,28 @@
#include "core/rendering/shader_backend.hpp"
// Uniforms enviats al fragment stage cada frame.
// Han de coincidir amb l'estructura MSL/GLSL (48 bytes, alineats a 16).
// Han de coincidir amb l'estructura MSL/GLSL (64 bytes = 4 × vec4, alineats a 16).
struct PostFXUniforms {
// vec4 #0
float vignette_strength;
float chroma_strength;
float scanline_strength;
float screen_height;
// vec4 #1
float mask_strength;
float gamma_strength;
float curvature;
float bleeding;
// vec4 #2
float pixel_scale;
float time;
float oversample;
float flicker;
// vec4 #3 — paràmetres de scanlines exposats al preset YAML
float scan_dark_ratio;
float scan_dark_floor;
float scan_edge_soft;
float pad3;
};
namespace Rendering {
@@ -96,6 +104,11 @@ namespace Rendering {
.time = 0.0F,
.oversample = 1.0F,
.flicker = 0.0F,
// Defaults canònics: 2/3 brillant + 1/3 fosca, brillantor 42%, suavitzat complet (1 px físic).
.scan_dark_ratio = 0.333F,
.scan_dark_floor = 0.42F,
.scan_edge_soft = 1.0F,
.pad3 = 0.0F,
};
int game_width_{0};
File diff suppressed because it is too large. Load diff