@@ -15,8 +15,12 @@ const float kPi = 3.1415926536;
1515const float kEuler = 2.718281828459 ;
1616const float kMax = 1.0 ;
1717
18+ #define UNIFIED_SCANLINE_BLOOM
19+
1820const float kBeamWidth = 0.5 ;
1921
22+ const vec3 kLuminanceWeights = vec3 (0.2126 , 0.7152 , 0.0722 );
23+
2024const uint kChannelMask = 3u;
2125const uint kFirstChannelShift = 2u;
2226const 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