feat(bloom): glow separable two-pass amb composite preserve-core i paleta neon

This commit is contained in:
JailDesigner committed 2026-05-21 18:39:16 +02:00
1 parent 8b4683b77b
commit ae946b578e
17 files changed
+3683 -2159

No files matched your search

@@ -0,0 +1,72 @@
// bloom_frag.msl.h - Metal Shading Language del fragment shader del bloom
// © 2026 JailDesigner
//
// IMPORTANT: mantenir sincronitzat a mà amb shaders/bloom.frag.glsl. SDL3 GPU
// compila aquest string MSL en runtime; qualsevol canvi al GLSL o al struct
// BloomUniforms (gpu_bloom_pipeline.hpp) cal replicar-lo aquí al mateix commit.
//
// Pass 1D del bloom separable: high-pass + gaussiana en una direcció.
// Recursos:
// - texture2d<float> src [[texture(0)]] + sampler [[sampler(0)]]
// - constant BloomUBO& ubo [[buffer(0)]]
#pragma once
#ifdef __APPLE__
namespace Rendering::GPU::Msl {
inline constexpr const char* BLOOM_FRAG_MSL = R"(
#include <metal_stdlib>
using namespace metal;
struct PostVOut {
float4 pos [[position]];
float2 uv;
};
struct BloomUBO {
float2 texel_size;
float2 direction;
float threshold;
float extract;
float sigma;
float pad_a;
};
fragment float4 bloom_fs(PostVOut in [[stage_in]],
texture2d<float> src [[texture(0)]],
sampler samp [[sampler(0)]],
constant BloomUBO& ubo [[buffer(0)]]) {
float3 sum = float3(0.0);
float total_weight = 0.0;
constexpr int RADIUS = 7;
constexpr float TWO_SIGMA_SQ_FACTOR = 2.0;
for (int i = -RADIUS; i <= RADIUS; ++i) {
float2 offset = ubo.direction * float(i) * ubo.texel_size;
float3 c = src.sample(samp, in.uv + offset).rgb;
if (ubo.extract > 0.5) {
float luma = max(c.r, max(c.g, c.b));
float high_pass = max(0.0, luma - ubo.threshold);
c *= high_pass;
}
float fi = float(i);
float w = exp(-(fi * fi) / (TWO_SIGMA_SQ_FACTOR * ubo.sigma * ubo.sigma));
sum += c * w;
total_weight += w;
}
if (total_weight > 0.0) {
sum /= total_weight;
}
return float4(sum, 1.0);
}
)";
} // namespace Rendering::GPU::Msl
#endif // __APPLE__
+20 -35
View File
@@ -1,4 +1,4 @@
// postfx_frag.msl.h - Metal Shading Language del fragment shader del postpro
// postfx_frag.msl.h - Metal Shading Language del fragment shader del composite
// © 2026 JailDesigner
//
// IMPORTANT: mantenir sincronitzat a mà amb shaders/postfx.frag.glsl. SDL3 GPU
@@ -6,19 +6,18 @@
// canvi al struct PostFxUniforms (gpu_postfx_pipeline.hpp), al GLSL o al MSL
// cal replicar-lo a totes tres al mateix commit.
//
// Composició final: bloom 5×5 amb high-pass, flicker sinusoidal global,
// background pulse sumat. Recursos:
// Composite final: llegeix escena + bloom pre-calculat (per bloom.frag.glsl en
// separable two-pass) i aplica flicker + background pulse. Recursos:
// - texture2d<float> scene [[texture(0)]] + sampler [[sampler(0)]]
// - texture2d<float> bloom_tex [[texture(1)]] + sampler [[sampler(1)]]
// - constant PostFxUBO& ubo [[buffer(0)]] (slot 0 SDL → buffer(0) MSL)
//
// L'struct PostFxUBO té layout idèntic a PostFxUniforms (5×vec4 = 80 bytes).
#pragma once
#ifdef __APPLE__
namespace Rendering::GPU::Msl {
inline constexpr const char* POSTFX_FRAG_MSL = R"(
inline constexpr const char* POSTFX_FRAG_MSL = R"(
#include <metal_stdlib>
using namespace metal;
@@ -30,46 +29,28 @@ struct PostVOut {
struct PostFxUBO {
float time;
float bloom_intensity;
float bloom_threshold;
float bloom_radius_px;
float flicker_amplitude;
float flicker_frequency_hz;
float background_pulse_freq_hz;
float pad_a;
float pad_b;
float pad_c;
float4 background_min;
float4 background_max;
float2 texel_size;
float2 pad_b;
};
constant float TAU = 6.28318530718;
fragment float4 postfx_fs(PostVOut in [[stage_in]],
texture2d<float> scene [[texture(0)]],
sampler samp [[sampler(0)]],
texture2d<float> scene [[texture(0)]],
sampler samp_s [[sampler(0)]],
texture2d<float> bloom_tex [[texture(1)]],
sampler samp_b [[sampler(1)]],
constant PostFxUBO& ubo [[buffer(0)]]) {
// === BLOOM ===
float3 src = scene.sample(samp, in.uv).rgb;
float3 bloom = float3(0.0);
float total_weight = 0.0;
for (int dy = -2; dy <= 2; ++dy) {
for (int dx = -2; dx <= 2; ++dx) {
float2 offset = float2(float(dx), float(dy)) * ubo.texel_size * ubo.bloom_radius_px;
float3 c = scene.sample(samp, in.uv + offset).rgb;
float luma = max(c.r, max(c.g, c.b));
float high_pass = max(0.0, luma - ubo.bloom_threshold);
float w = exp(-float(dx * dx + dy * dy) / 4.0);
bloom += c * high_pass * w;
total_weight += w;
}
}
if (total_weight > 0.0) {
bloom /= total_weight;
}
bloom *= ubo.bloom_intensity;
float3 src = scene.sample(samp_s, in.uv).rgb;
float3 bloom = bloom_tex.sample(samp_b, in.uv).rgb * ubo.bloom_intensity;
// === FLICKER ===
float pulse = (sin(ubo.time * ubo.flicker_frequency_hz * TAU) * 0.5) + 0.5;
@@ -79,8 +60,12 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
float bg_pulse = (sin(ubo.time * ubo.background_pulse_freq_hz * TAU) * 0.5) + 0.5;
float3 background = mix(ubo.background_min.rgb, ubo.background_max.rgb, bg_pulse);
// === COMPOSICIÓ ===
float3 lines_and_glow = (src + bloom) * flicker;
// === COMPOSICIÓ (preserve-core) ===
// Bloom additiu atenuat per (1 - luma_src) — manté el color del core
// i posa el halo intens només als píxels foscos del voltant.
float src_luma = max(src.r, max(src.g, src.b));
float3 bloom_contribution = bloom * (1.0 - src_luma);
float3 lines_and_glow = (src + bloom_contribution) * flicker;
return float4(background + lines_and_glow, 1.0);
}
)";