chroma min/max amb mostreig bilinear subpíxel
This commit is contained in:
1 parent
b81a453b32
commit
35d3b359eb
8 files changed
+8612
-6897
No files matched your search
@@ -17,7 +17,7 @@ layout(set = 2, binding = 0) uniform sampler2D scene;
|
|||||||
|
|
||||||
layout(set = 3, binding = 0) uniform PostFXUniforms {
|
layout(set = 3, binding = 0) uniform PostFXUniforms {
|
||||||
float vignette_strength;
|
float vignette_strength;
|
||||||
float chroma_strength;
|
float chroma_min; // intensitat mínima de l'aberració cromàtica
|
||||||
float scanline_strength;
|
float scanline_strength;
|
||||||
float screen_height;
|
float screen_height;
|
||||||
float mask_strength;
|
float mask_strength;
|
||||||
@@ -27,7 +27,7 @@ layout(set = 3, binding = 0) uniform PostFXUniforms {
|
|||||||
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||||
float time; // seconds since SDL init
|
float time; // seconds since SDL init
|
||||||
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz
|
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz
|
||||||
float pad2; // padding per tancar vec4 #2
|
float chroma_max; // intensitat màxima; si == chroma_min → chroma estàtic
|
||||||
// vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
|
// vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
|
||||||
float scan_dark_ratio; // fracció de subfila fosca per fila lògica (1/3 ≈ 0.333)
|
float scan_dark_ratio; // fracció de subfila fosca per fila lògica (1/3 ≈ 0.333)
|
||||||
float scan_dark_floor; // multiplicador de brillantor de la subfila fosca
|
float scan_dark_floor; // multiplicador de brillantor de la subfila fosca
|
||||||
@@ -35,6 +35,19 @@ layout(set = 3, binding = 0) uniform PostFXUniforms {
|
|||||||
float pad3; // padding per tancar a 64 bytes (4 × vec4)
|
float pad3; // padding per tancar a 64 bytes (4 × vec4)
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
|
// Mostreig bilinear horitzontal d'un canal RGB. Evita el "tic-tac" del sampler
|
||||||
|
// NEAREST quan l'offset de chroma és subpíxel: sense interpolar, l'offset
|
||||||
|
// arrodonia entre 1 i 2 píxels i el drift temporal feia un parpelleig discret.
|
||||||
|
float sampleBilinearX(vec2 uv_target, int channel) {
|
||||||
|
vec2 tex_size = vec2(textureSize(scene, 0));
|
||||||
|
float px = uv_target.x * tex_size.x - 0.5;
|
||||||
|
float p_floor = floor(px);
|
||||||
|
float f = px - p_floor;
|
||||||
|
vec4 c0 = texture(scene, vec2((p_floor + 0.5) / tex_size.x, uv_target.y));
|
||||||
|
vec4 c1 = texture(scene, vec2((p_floor + 1.5) / tex_size.x, uv_target.y));
|
||||||
|
return mix(c0[channel], c1[channel], f);
|
||||||
|
}
|
||||||
|
|
||||||
// YCbCr helpers for NTSC bleeding
|
// YCbCr helpers for NTSC bleeding
|
||||||
vec3 rgb_to_ycc(vec3 rgb) {
|
vec3 rgb_to_ycc(vec3 rgb) {
|
||||||
return vec3(
|
return vec3(
|
||||||
@@ -92,10 +105,14 @@ void main() {
|
|||||||
colour = base;
|
colour = base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aberración cromática (drift animado con time para efecto NTSC real)
|
// Aberración cromática — intensitat varia entre chroma_min i chroma_max amb
|
||||||
float ca = u.chroma_strength * 0.005 * (1.0 + 0.15 * sin(u.time * 7.3));
|
// una sinusoidal (si min == max, queda estàtica). Mostreig bilinear horitzontal
|
||||||
colour.r = texture(scene, uv + vec2(ca, 0.0)).r;
|
// per evitar el "tic-tac" del NEAREST sampler quan l'offset és subpíxel.
|
||||||
colour.b = texture(scene, uv - vec2(ca, 0.0)).b;
|
if (u.chroma_min > 0.0 || u.chroma_max > 0.0) {
|
||||||
|
float ca = mix(u.chroma_min, u.chroma_max, 0.5 + 0.5 * sin(u.time * 7.3)) * 0.005;
|
||||||
|
colour.r = sampleBilinearX(uv + vec2(ca, 0.0), 0);
|
||||||
|
colour.b = sampleBilinearX(uv - vec2(ca, 0.0), 2);
|
||||||
|
}
|
||||||
|
|
||||||
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||||
if (u.gamma_strength > 0.0) {
|
if (u.gamma_strength > 0.0) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ struct PostVOut {
|
|||||||
|
|
||||||
struct PostFXUniforms {
|
struct PostFXUniforms {
|
||||||
float vignette_strength;
|
float vignette_strength;
|
||||||
float chroma_strength;
|
float chroma_min;
|
||||||
float scanline_strength;
|
float scanline_strength;
|
||||||
float screen_height;
|
float screen_height;
|
||||||
float mask_strength;
|
float mask_strength;
|
||||||
@@ -32,7 +32,7 @@ struct PostFXUniforms {
|
|||||||
float pixel_scale;
|
float pixel_scale;
|
||||||
float time;
|
float time;
|
||||||
float flicker;
|
float flicker;
|
||||||
float pad2;
|
float chroma_max;
|
||||||
// vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
|
// vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
|
||||||
float scan_dark_ratio;
|
float scan_dark_ratio;
|
||||||
float scan_dark_floor;
|
float scan_dark_floor;
|
||||||
@@ -40,6 +40,18 @@ struct PostFXUniforms {
|
|||||||
float pad3;
|
float pad3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Mostreig bilinear horitzontal d'un canal RGB. Evita el "tic-tac" del sampler
|
||||||
|
// NEAREST quan l'offset de chroma és subpíxel.
|
||||||
|
static float sampleBilinearX(float2 uv_target, int channel, texture2d<float> scene, sampler samp) {
|
||||||
|
float2 tex_size = float2(scene.get_width(), scene.get_height());
|
||||||
|
float px = uv_target.x * tex_size.x - 0.5f;
|
||||||
|
float p_floor = floor(px);
|
||||||
|
float f = px - p_floor;
|
||||||
|
float4 c0 = scene.sample(samp, float2((p_floor + 0.5f) / tex_size.x, uv_target.y));
|
||||||
|
float4 c1 = scene.sample(samp, float2((p_floor + 1.5f) / tex_size.x, uv_target.y));
|
||||||
|
return mix(c0[channel], c1[channel], f);
|
||||||
|
}
|
||||||
|
|
||||||
static float3 rgb_to_ycc(float3 rgb) {
|
static float3 rgb_to_ycc(float3 rgb) {
|
||||||
return float3(
|
return float3(
|
||||||
0.299f*rgb.r + 0.587f*rgb.g + 0.114f*rgb.b,
|
0.299f*rgb.r + 0.587f*rgb.g + 0.114f*rgb.b,
|
||||||
@@ -94,9 +106,14 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
colour = base;
|
colour = base;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ca = u.chroma_strength * 0.005f * (1.0f + 0.15f * sin(u.time * 7.3f));
|
// Chroma — varia entre chroma_min i chroma_max via sinusoidal; si min == max
|
||||||
colour.r = scene.sample(samp, uv + float2(ca, 0.0f)).r;
|
// queda estàtic. Mostreig bilinear horitzontal per evitar el "tic-tac" del
|
||||||
colour.b = scene.sample(samp, uv - float2(ca, 0.0f)).b;
|
// NEAREST sampler amb offsets subpíxel.
|
||||||
|
if (u.chroma_min > 0.0f || u.chroma_max > 0.0f) {
|
||||||
|
float ca = mix(u.chroma_min, u.chroma_max, 0.5f + 0.5f * sin(u.time * 7.3f)) * 0.005f;
|
||||||
|
colour.r = sampleBilinearX(uv + float2(ca, 0.0f), 0, scene, samp);
|
||||||
|
colour.b = sampleBilinearX(uv - float2(ca, 0.0f), 2, scene, samp);
|
||||||
|
}
|
||||||
|
|
||||||
if (u.gamma_strength > 0.0f) {
|
if (u.gamma_strength > 0.0f) {
|
||||||
float3 lin = pow(colour, float3(2.4f));
|
float3 lin = pow(colour, float3(2.4f));
|
||||||
|
|||||||
@@ -234,7 +234,8 @@ namespace Rendering {
|
|||||||
void SDL3GPUShader::setPostFXParams(const PostFXParams& params) {
|
void SDL3GPUShader::setPostFXParams(const PostFXParams& params) {
|
||||||
uniforms_.vignette_strength = params.vignette;
|
uniforms_.vignette_strength = params.vignette;
|
||||||
uniforms_.scanline_strength = params.scanlines;
|
uniforms_.scanline_strength = params.scanlines;
|
||||||
uniforms_.chroma_strength = params.chroma;
|
uniforms_.chroma_min = params.chroma_min;
|
||||||
|
uniforms_.chroma_max = params.chroma_max;
|
||||||
uniforms_.mask_strength = params.mask;
|
uniforms_.mask_strength = params.mask;
|
||||||
uniforms_.gamma_strength = params.gamma;
|
uniforms_.gamma_strength = params.gamma;
|
||||||
uniforms_.curvature = params.curvature;
|
uniforms_.curvature = params.curvature;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
struct PostFXUniforms {
|
struct PostFXUniforms {
|
||||||
// vec4 #0
|
// vec4 #0
|
||||||
float vignette_strength;
|
float vignette_strength;
|
||||||
float chroma_strength;
|
float chroma_min;
|
||||||
float scanline_strength;
|
float scanline_strength;
|
||||||
float screen_height;
|
float screen_height;
|
||||||
// vec4 #1
|
// vec4 #1
|
||||||
@@ -27,7 +27,7 @@ struct PostFXUniforms {
|
|||||||
float pixel_scale;
|
float pixel_scale;
|
||||||
float time;
|
float time;
|
||||||
float flicker;
|
float flicker;
|
||||||
float pad2;
|
float chroma_max;
|
||||||
// vec4 #3 — paràmetres de scanlines exposats al preset YAML
|
// vec4 #3 — paràmetres de scanlines exposats al preset YAML
|
||||||
float scan_dark_ratio;
|
float scan_dark_ratio;
|
||||||
float scan_dark_floor;
|
float scan_dark_floor;
|
||||||
@@ -92,7 +92,7 @@ namespace Rendering {
|
|||||||
|
|
||||||
PostFXUniforms uniforms_{
|
PostFXUniforms uniforms_{
|
||||||
.vignette_strength = 0.0F,
|
.vignette_strength = 0.0F,
|
||||||
.chroma_strength = 0.0F,
|
.chroma_min = 0.0F,
|
||||||
.scanline_strength = 0.0F,
|
.scanline_strength = 0.0F,
|
||||||
.screen_height = 0.0F,
|
.screen_height = 0.0F,
|
||||||
.mask_strength = 0.0F,
|
.mask_strength = 0.0F,
|
||||||
@@ -102,7 +102,7 @@ namespace Rendering {
|
|||||||
.pixel_scale = 1.0F,
|
.pixel_scale = 1.0F,
|
||||||
.time = 0.0F,
|
.time = 0.0F,
|
||||||
.flicker = 0.0F,
|
.flicker = 0.0F,
|
||||||
.pad2 = 0.0F,
|
.chroma_max = 0.0F,
|
||||||
// Defaults canònics: 2/3 brillant + 1/3 fosca, brillantor 42%, suavitzat complet (1 px físic).
|
// Defaults canònics: 2/3 brillant + 1/3 fosca, brillantor 42%, suavitzat complet (1 px físic).
|
||||||
.scan_dark_ratio = 0.333F,
|
.scan_dark_ratio = 0.333F,
|
||||||
.scan_dark_floor = 0.42F,
|
.scan_dark_floor = 0.42F,
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -19,7 +19,10 @@ namespace Rendering {
|
|||||||
struct PostFXParams {
|
struct PostFXParams {
|
||||||
float vignette{0.0F};
|
float vignette{0.0F};
|
||||||
float scanlines{0.0F};
|
float scanlines{0.0F};
|
||||||
float chroma{0.0F};
|
// Aberració cromàtica — varia entre min i max via sinusoidal; si coincideixen
|
||||||
|
// queda estàtica. Permet un "floor" perquè la imatge mai sigui lliure de chroma.
|
||||||
|
float chroma_min{0.0F};
|
||||||
|
float chroma_max{0.0F};
|
||||||
float mask{0.0F};
|
float mask{0.0F};
|
||||||
float gamma{0.0F};
|
float gamma{0.0F};
|
||||||
float curvature{0.0F};
|
float curvature{0.0F};
|
||||||
|
|||||||
+20
-9
@@ -248,12 +248,15 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Presets per defecte de PostFX, portats directament de projecte_2026.
|
// Presets per defecte de PostFX, portats directament de projecte_2026.
|
||||||
|
// chroma_min == chroma_max → chroma estàtic (per defecte). Ajusta'ls al gust
|
||||||
|
// (per exemple, augmentar el rang en CRT Live perquè "pulsi" visiblement).
|
||||||
auto defaultPostFXPresets() -> std::vector<Options::PostFXPreset> {
|
auto defaultPostFXPresets() -> std::vector<Options::PostFXPreset> {
|
||||||
return {
|
return {
|
||||||
{.name = "CRT",
|
{.name = "CRT",
|
||||||
.vignette = 0.6F,
|
.vignette = 0.6F,
|
||||||
.scanlines = 0.7F,
|
.scanlines = 0.7F,
|
||||||
.chroma = 0.15F,
|
.chroma_min = 0.15F,
|
||||||
|
.chroma_max = 0.15F,
|
||||||
.mask = 0.6F,
|
.mask = 0.6F,
|
||||||
.gamma = 0.8F,
|
.gamma = 0.8F,
|
||||||
.curvature = 0.0F,
|
.curvature = 0.0F,
|
||||||
@@ -262,7 +265,8 @@ namespace {
|
|||||||
{.name = "NTSC",
|
{.name = "NTSC",
|
||||||
.vignette = 0.4F,
|
.vignette = 0.4F,
|
||||||
.scanlines = 0.5F,
|
.scanlines = 0.5F,
|
||||||
.chroma = 0.2F,
|
.chroma_min = 0.2F,
|
||||||
|
.chroma_max = 0.2F,
|
||||||
.mask = 0.4F,
|
.mask = 0.4F,
|
||||||
.gamma = 0.5F,
|
.gamma = 0.5F,
|
||||||
.curvature = 0.0F,
|
.curvature = 0.0F,
|
||||||
@@ -271,7 +275,8 @@ namespace {
|
|||||||
{.name = "Curved",
|
{.name = "Curved",
|
||||||
.vignette = 0.5F,
|
.vignette = 0.5F,
|
||||||
.scanlines = 0.6F,
|
.scanlines = 0.6F,
|
||||||
.chroma = 0.1F,
|
.chroma_min = 0.1F,
|
||||||
|
.chroma_max = 0.1F,
|
||||||
.mask = 0.5F,
|
.mask = 0.5F,
|
||||||
.gamma = 0.7F,
|
.gamma = 0.7F,
|
||||||
.curvature = 0.8F,
|
.curvature = 0.8F,
|
||||||
@@ -280,7 +285,8 @@ namespace {
|
|||||||
{.name = "Scanlines",
|
{.name = "Scanlines",
|
||||||
.vignette = 0.0F,
|
.vignette = 0.0F,
|
||||||
.scanlines = 0.8F,
|
.scanlines = 0.8F,
|
||||||
.chroma = 0.0F,
|
.chroma_min = 0.0F,
|
||||||
|
.chroma_max = 0.0F,
|
||||||
.mask = 0.0F,
|
.mask = 0.0F,
|
||||||
.gamma = 0.0F,
|
.gamma = 0.0F,
|
||||||
.curvature = 0.0F,
|
.curvature = 0.0F,
|
||||||
@@ -289,7 +295,8 @@ namespace {
|
|||||||
{.name = "Subtle",
|
{.name = "Subtle",
|
||||||
.vignette = 0.3F,
|
.vignette = 0.3F,
|
||||||
.scanlines = 0.4F,
|
.scanlines = 0.4F,
|
||||||
.chroma = 0.05F,
|
.chroma_min = 0.05F,
|
||||||
|
.chroma_max = 0.05F,
|
||||||
.mask = 0.0F,
|
.mask = 0.0F,
|
||||||
.gamma = 0.3F,
|
.gamma = 0.3F,
|
||||||
.curvature = 0.0F,
|
.curvature = 0.0F,
|
||||||
@@ -298,7 +305,8 @@ namespace {
|
|||||||
{.name = "CRT Live",
|
{.name = "CRT Live",
|
||||||
.vignette = 0.5F,
|
.vignette = 0.5F,
|
||||||
.scanlines = 0.6F,
|
.scanlines = 0.6F,
|
||||||
.chroma = 0.3F,
|
.chroma_min = 0.3F,
|
||||||
|
.chroma_max = 0.3F,
|
||||||
.mask = 0.3F,
|
.mask = 0.3F,
|
||||||
.gamma = 0.4F,
|
.gamma = 0.4F,
|
||||||
.curvature = 0.3F,
|
.curvature = 0.3F,
|
||||||
@@ -311,7 +319,8 @@ namespace {
|
|||||||
readField(node, "name", preset.name);
|
readField(node, "name", preset.name);
|
||||||
readField(node, "vignette", preset.vignette);
|
readField(node, "vignette", preset.vignette);
|
||||||
readField(node, "scanlines", preset.scanlines);
|
readField(node, "scanlines", preset.scanlines);
|
||||||
readField(node, "chroma", preset.chroma);
|
readField(node, "chroma_min", preset.chroma_min);
|
||||||
|
readField(node, "chroma_max", preset.chroma_max);
|
||||||
readField(node, "mask", preset.mask);
|
readField(node, "mask", preset.mask);
|
||||||
readField(node, "gamma", preset.gamma);
|
readField(node, "gamma", preset.gamma);
|
||||||
readField(node, "curvature", preset.curvature);
|
readField(node, "curvature", preset.curvature);
|
||||||
@@ -326,7 +335,8 @@ namespace {
|
|||||||
out << " - name: \"" << preset.name << "\"\n";
|
out << " - name: \"" << preset.name << "\"\n";
|
||||||
out << " vignette: " << preset.vignette << "\n";
|
out << " vignette: " << preset.vignette << "\n";
|
||||||
out << " scanlines: " << preset.scanlines << "\n";
|
out << " scanlines: " << preset.scanlines << "\n";
|
||||||
out << " chroma: " << preset.chroma << "\n";
|
out << " chroma_min: " << preset.chroma_min << "\n";
|
||||||
|
out << " chroma_max: " << preset.chroma_max << "\n";
|
||||||
out << " mask: " << preset.mask << "\n";
|
out << " mask: " << preset.mask << "\n";
|
||||||
out << " gamma: " << preset.gamma << "\n";
|
out << " gamma: " << preset.gamma << "\n";
|
||||||
out << " curvature: " << preset.curvature << "\n";
|
out << " curvature: " << preset.curvature << "\n";
|
||||||
@@ -876,7 +886,8 @@ namespace Options {
|
|||||||
return {
|
return {
|
||||||
.vignette = IT->vignette,
|
.vignette = IT->vignette,
|
||||||
.scanlines = IT->scanlines,
|
.scanlines = IT->scanlines,
|
||||||
.chroma = IT->chroma,
|
.chroma_min = IT->chroma_min,
|
||||||
|
.chroma_max = IT->chroma_max,
|
||||||
.mask = IT->mask,
|
.mask = IT->mask,
|
||||||
.gamma = IT->gamma,
|
.gamma = IT->gamma,
|
||||||
.curvature = IT->curvature,
|
.curvature = IT->curvature,
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ namespace Options {
|
|||||||
std::string name;
|
std::string name;
|
||||||
float vignette{0.0F};
|
float vignette{0.0F};
|
||||||
float scanlines{0.0F};
|
float scanlines{0.0F};
|
||||||
float chroma{0.0F};
|
// Aberració cromàtica entre min i max (si coincideixen, estàtica).
|
||||||
|
float chroma_min{0.0F};
|
||||||
|
float chroma_max{0.0F};
|
||||||
float mask{0.0F};
|
float mask{0.0F};
|
||||||
float gamma{0.0F};
|
float gamma{0.0F};
|
||||||
float curvature{0.0F};
|
float curvature{0.0F};
|
||||||
|
|||||||
Reference in new issue
Block a user