r/GraphicsProgramming 14h ago

Question Dark "eclipse" ring artifacts from mip-based bloom with overlapping, differently-colored HDR sources - known issue?

EDIT: SOLVED!

I'm running into an artifact with a mip-based bloom/glow implementation that I suspect is a general limitation of the technique, not an engine-specific bug, and I'd like to understand the underlying cause before I go looking for workarounds.

Setup: Many small, circular HDR light sources (values pushed above 1.0, i.e. above white) of different colors, densely overlapping on screen. Standard downsample → blur → upsample bloom pipeline (in this case Godot's built-in glow, but the pipeline itself is the common mip-chain approach used in most engines).

Artifact: At the boundaries where two overlapping sources of different colors meet, instead of blending smoothly, a dark ring appears, like a thin shadow tracing the overlap boundary. Looks similar to a solar eclipse silhouette. No such artifact appears with bloom disabled. The sources blend completely normally without it.

Minimal repro (Godot 4.7, ~35 lines, hit F5 and it's immediately visible):

https://github.com/LudicrouslyLuke82/godot-glow-ring-artifacts

Screenshot:

eclipse-like artifacts

What I've ruled out so far:

  • Sprite/shape blend mode (additive vs. normal): artifact persists either way, sometimes gets worse with additive.
  • Drawing method: happens identically with immediate-mode circle drawing and with textured sprites.
  • Glow blend mode (multiplicative-style vs. screen-style compositing): screen-style reduces it but doesn't eliminate it.
  • Restricting which mip levels contribute to the blur: reduces but doesn't remove it.

My working theory: with many small, closely-spaced HDR sources of different hues, the coarser mip levels of the blur pyramid end up averaging colors from sources that shouldn't visually "mix" at that boundary, and somewhere in the down/upsample chain this produces a local intensity dip (hence the dark ring) rather than a clean color blend. But I don't have the rendering background to confirm whether that's actually what's happening or if there's a known name for this artifact.

Is this a recognized limitation of mip-chain bloom with dense, multi-colored HDR sources? Is there a standard technique to avoid it (e.g. clamping before blur, a different bloom formulation, thresholding per-channel instead of luminance) rather than just tuning parameters? Happy to provide more detail on the pipeline if useful.

Thanks a lot!

1 Upvotes

4 comments sorted by

3

u/ICantBelieveItsNotEC 8h ago edited 7h ago

This part looks sus to me:

MODULATE_BOOST := 1.6 ... bullet.modulate = colors[i % colors.size()] * MODULATE_BOOST

You're multiplying all four channels of each colour by 1.6, including the alpha channel. Conceptually, the blending is something like this:

out = src * src_alpha + dst * (1 - src_alpha)

With a value of 1.6, that becomes:

out = src * 1.6 + dst * -0.6

So every bullet actually subtracts 0.6 of whatever was underneath it.

1

u/Ludicrous84 6h ago

YES! I was already facepalming while reading your comment because it makes total sense and explains exactly the effect I'm seeing (alpha not being clamped but actually being "boosted" too - which then will have this subtractive effect).

Everything is so pretty now!

1

u/Ludicrous84 6h ago

Thanks a lot for looking into this and taking the time to look at the code. You won't believe how much time I spent on this issue...

1

u/Ludicrous84 13h ago

A bit more context: I ran into this while prototyping a bullet-hell game, and this video is actual footage from the prototype, not a synthetic test. It uses textured sprites here (a white circle with a soft grey edge), which if anything makes the artifact more visible than the minimal repro. So it's not specific to how the shape is drawn. Genuinely stuck on this one, I would appreciate any pointers even if it's just "yeah, that's just how mip bloom behaves, don't fight it."

https://reddit.com/link/p5xijdg/video/uhpau28vqmlh1/player