Skip to content

Commit 8a641f1

Browse files
MajorPainTheCactus龙虾机器人
authored andcommitted
Vulkan HDR: unified scanline bloom to prevent colour shifts
Use luminance-based envelope for scanline bloom instead of per-channel bloom, preventing hue shifts caused by independent beam width calculations per phosphor channel. Per-channel bloom retained behind #ifdef.
1 parent a64a2ca commit 8a641f1

1 file changed

Lines changed: 34 additions & 6 deletions

File tree

gfx/drivers/vulkan_shaders/hdr.frag

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ const float kPi = 3.1415926536;
1515
const float kEuler = 2.718281828459;
1616
const float kMax = 1.0;
1717

18+
#define UNIFIED_SCANLINE_BLOOM
19+
1820
const float kBeamWidth = 0.5;
1921

22+
const vec3 kLuminanceWeights = vec3(0.2126, 0.7152, 0.0722);
23+
2024
const uint kChannelMask = 3u;
2125
const uint kFirstChannelShift = 2u;
2226
const uint kSecondChannelShift = 4u;
@@ -249,19 +253,42 @@ vec3 ScanlineColour(const vec2 tex_coord,
249253

250254
vec3 result = vec3(0.0);
251255

256+
float beam_width_adjustment = (kBeamWidth / scanline_size);
257+
float raw_distance = abs(distance_to_line) - beam_width_adjustment;
258+
float distance_adjusted = max(0.0, raw_distance);
259+
float effective_distance = distance_adjusted * 2.0;
260+
261+
#ifdef UNIFIED_SCANLINE_BLOOM
262+
/* Unified bloom: compute a single horizontal interpolation from luminance
263+
* so the scanline envelope is shared across all channels in the phosphor
264+
* triad, preventing colour shifts from per-channel bloom differences. */
265+
float lum_0 = dot(signal_0, kLuminanceWeights);
266+
float lum_1 = dot(signal_1, kLuminanceWeights);
267+
float lum_horiz_interp = Bezier(narrowed_source_pixel_offset, BeamControlPoints(beam_attack, lum_0 > lum_1));
268+
float beam_intensity = clamp(mix(lum_0, lum_1, lum_horiz_interp), 0.0, 1.0);
269+
270+
float beam_width = mix(scanline_min, scanline_max, beam_intensity);
271+
float scanline_dist = clamp(effective_distance / beam_width, 0.0, 1.0);
272+
vec4 control_points = vec4(1.0, 1.0, beam_intensity * scanline_attack, 0.0);
273+
float envelope = Bezier(scanline_dist, control_points);
274+
275+
for(int ch = 0; ch < 3; ch++)
276+
{
277+
float horiz_interp = Bezier(narrowed_source_pixel_offset, BeamControlPoints(beam_attack, signal_0[ch] > signal_1[ch]));
278+
float signal_channel = mix(signal_0[ch], signal_1[ch], horiz_interp);
279+
280+
result[ch] = envelope * signal_channel;
281+
}
282+
#else
283+
/* Per-channel bloom: each channel computes its own scanline envelope based on
284+
* its individual brightness (models independent electron gun bloom). */
252285
for(int ch = 0; ch < 3; ch++)
253286
{
254287
float horiz_interp = Bezier(narrowed_source_pixel_offset, BeamControlPoints(beam_attack, signal_0[ch] > signal_1[ch]));
255288
float signal_channel = mix(signal_0[ch], signal_1[ch], horiz_interp);
256289

257290
float signal_strength = clamp(signal_channel, 0.0, 1.0);
258291

259-
float beam_width_adjustment = (kBeamWidth / scanline_size);
260-
float raw_distance = abs(distance_to_line) - beam_width_adjustment;
261-
float distance_adjusted = max(0.0, raw_distance);
262-
263-
float effective_distance = distance_adjusted * 2.0;
264-
265292
float beam_width = mix(scanline_min, scanline_max, signal_strength);
266293

267294
float channel_scanline_distance = clamp(effective_distance / beam_width, 0.0f, 1.0f);
@@ -271,6 +298,7 @@ vec3 ScanlineColour(const vec2 tex_coord,
271298

272299
result[ch] = luminance * signal_channel;
273300
}
301+
#endif
274302

275303
return result;
276304
}

0 commit comments

Comments
 (0)