run method
Returns linear RGBA for attachment zero. Null discards the fragment.
Implementation
@override
Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) {
final src = bloomSource(b);
if (src == null) return Vector4(0.0, 0.0, 0.0, 1.0);
final u = v[0];
final w = v[1];
// A four-tap box at the corners of the source quad. A single tap aliases a
// one-pixel highlight in and out of existence as the camera moves, which
// reads as flickering rather than as bloom.
//
// Weighted by Karis's `1 / (1 + luma)`, as `bloom_threshold.frag`.
var sum = Vector3.zero();
var weights = 0.0;
for (final d in const <List<double>>[
<double>[-0.5, -0.5],
<double>[0.5, -0.5],
<double>[-0.5, 0.5],
<double>[0.5, 0.5],
]) {
final t = src.source.sample(u + src.tx * d[0], w + src.ty * d[1]);
final tap = Vector3(t.x, t.y, t.z);
final weight = 1.0 / (1.0 + _luminance(tap));
sum += tap * weight;
weights += weight;
}
final color = sum..scale(1.0 / weights);
final threshold = src.params.z;
final knee = math.max(src.params.w, 1e-4);
// A soft knee rather than a hard step: a hard cut makes the bloom appear
// along a visible contour as a highlight brightens through the threshold.
final brightness = _luminance(color);
var soft = (brightness - threshold + knee).clamp(0.0, 2.0 * knee);
soft = soft * soft / (4.0 * knee);
final contribution =
math.max(soft, brightness - threshold) / math.max(brightness, 1e-4);
color.scale(contribution);
return Vector4(color.x, color.y, color.z, 1.0);
}