@neutrinoparticles/bloom-v1.1
v1.0.0
Published
Version-agnostic NeutrinoParticles HDR-glow bloom pipeline for the PIXI runtimes (shared shaders + orchestration)
Maintainers
Readme
@neutrinoparticles/bloom-v1.1
Version-agnostic core of the NeutrinoParticles HDR-glow
bloom add-on for PIXI (JS export format v1.1). It carries the raw-WebGL2 bloom
pipeline (RawBloom) — shader programs, mip-pyramid sizing, and the FBO/quad passes,
a port of the editor's BloomNeutrino — with no PIXI-version-specific code.
You probably don't install this directly
Use the runtime package matching your PIXI major; each depends on this core:
- PIXI v7 →
@neutrinoparticles/bloom-v1.1-pixi7 - PIXI v8 →
@neutrinoparticles/bloom-v1.1-pixi8
Requirements
WebGL2 only. Bloom renders into float (RGBA16F) targets so colour > 1 survives; that
needs a WebGL2 context with a renderable float colour buffer (EXT_color_buffer_float or
EXT_color_buffer_half_float). On WebGL1 the constructor throws a clear error.
Algorithm
A faithful port of the editor's live-preview bloom (libs/gl/postprocess/BloomNeutrino.cpp
- the
NeutrinoPrefilter/NeutrinoPresentshaders). It reuses the fast dual-Kawase pyramid but is scene-preserving: only HDR content above luminance 1.0 glows; the scene ≤ 1 passes through untouched.
- Prefilter at half-res: soft knee over Rec.709 luminance 1.0 —
weight = max(lum-1,0)/max(lum,ε), so luminance ≤ 1 yields zero (no glow) and brighter HDR content glows more, smoothly (no hard step). - Downsample ×3 down a 4-level pyramid: 4-tap box.
- Upsample ×3 back to half-res: dual-Kawase 8-tap.
- Present (3 steps): copy the scene as-is into a full-res HDR composite, additively
blend the half-res bloom over it (
intensitydefault 1.0), then run the hue-preserving- white-core present curve to the destination. For max-channel ≤ 1 the present is exact identity; above 1 the colour is normalized (hue preserved, no ring) and the hottest core desaturates to white.
All intermediate targets are RGBA16F, so particle colours above 1.0 (the runtime's HDR output) bloom with true energy rather than a clamped LDR glow.
Why raw WebGL2
The PIXI Filter API sizes a filter's output to its input frame, so it cannot express a
multi-resolution mip pyramid (upsample writes a larger target from a smaller source).
RawBloom owns its own FBOs/textures and drives explicit per-level viewports, exactly like
the C++ reference. The per-version adapter packages supply the WebGL2RenderingContext, the
scene texture handle, and the PIXI GL-state save/restore that lets the raw passes coexist
with normal PIXI rendering.
API
new RawBloom(gl: WebGL2RenderingContext, { intensity, iterations, flipPresentY })| Method | Description |
|---|---|
| resize(width, height) | (Re)allocate the pyramid + full-res composite for the scene size. No-op when unchanged. |
| processBloom(sceneTexture) | Prefilter → downsample → upsample from the scene texture into the pyramid. |
| renderResult(sceneTexture, destFbo, width, height) | Composite scene + bloom into a full-res HDR target, then present (hue-preserving + white core) to destFbo (null = screen). |
| dispose() | Delete all owned GL objects. |
RawBloomOptions: intensity (additive glow strength, default 1.0), iterations
(blur-pyramid depth, default 3, range 1..6) and flipPresentY (flip the present pass
when the destination is a bottom-left screen framebuffer).
iterations — how far the glow spreads
The glow's radius is set by the depth of the blur pyramid, not by how bright the
content is: the blur kernels' offsets scale with each pyramid level's size, and the
present normalizes by the max channel, so a brighter core saturates (denser, whiter)
rather than spreading wider. Raise iterations for a wide, soft halo; lower it for a
tight glow. Each extra iteration roughly doubles the reach.
This mirrors the editor's Bloom iterations preview setting (Project Settings → Preview, NeutrinoParticles engine), so a value you tune in the editor transfers here unchanged. The default 3 is what the runtime used before the option existed.
Cost scales with depth, but cheaply — each level is a quarter of the previous one's pixels. On a small canvas the depth is capped to what fits: levels below 4 px are not built, since a 1 px level is a single averaged colour smeared over the frame.
Documentation
Full documentation at neutrinoparticles.com.
License
Copyright (c) Yurii Miroshnyk. All rights reserved.
