Adding <Bloom> to an otherwise working `EffectComposer` chain fills large parts of the frame with black. The black regions are not static, they move and change shape as the camera orbits Removing `` makes them disappear completely.
**Versions**
- `three` 0.171.0
- `@react-threereact-threereact-threact-threereact-threereact-th@react-three.18.0ee@react-three.18.0ea@react-threet-three/fiber` @react-three.18.0
- `@react-three/postprocessing` 2.19.1
- `postprocessing` 6.37.7
- Chrome, WebGL2, Windows 11
**Symptoms**
- Present immediately on load
- Moves/changes with camera orientation
- **Not** affected by window resize
- Changing Bloom’s `intensity`, `luminanceThreshold`, `luminanceSmoothing` or `radius` does not help at any value
- Disabling the `N8AO` pass makes no difference, so it is not an interaction with that
**What I think is happening (would appreciate a sanity check)**
The scene has a skybox sphere textured with a large HDR `.exr`, using `toneMapped={false}`:
const texture = useLoader(EXRLoader, '/Texture/sky.exr');
<mesh rotation-y={-Math.PI / 2}>
<sphereGeometry args={[radius, 32, 32]} />
<meshBasicMaterial side={THREE.BackSide} map={texture} color={[0.5, 0.5, 0.5]} toneMapped={false} />
</mesh>
The EXR carries raw radiance, and the sun region in it goes well above the 65504 limit of the half-float frame buffer that `EffectComposer` uses by default. Nothing brings it down before the composer: I set `toneMapping: THREE.ACESFilmicToneMapping` on the canvas, but `EffectComposer` sets `gl.toneMapping = NoToneMapping` while mounted and expects a `` effect in the chain instead, which I dont currently have.
So my guess is those texels reach Bloom as `Inf`, and somewhere in the mipmap blur (a `mix`/lerp between mip levels, `Inf - Inf`, or `Inf * 0`) they turn into `NaN`. That would explain the shape of the artifact: the blur chain downsamples to very small mips, so a single bad texel at the bottom smears across a huge screen area when upsampled, with the blocky edges of a low-res mip.
The camera dependence fits too, orbiting brings different (brighter) parts of the sky into view. The resize independence fits, since resizing doesn’t change which texels are in frame.
**Chain**
<EffectComposer multisampling={0} enableNormalPass>
<N8AO {...aoProps} />
<Bloom
intensity={1}
luminanceThreshold={1}
luminanceSmoothing={1}
radius={0.25}
/>
<Vignette eskil={true} offset={0.75} darkness={1} />
<SMAA />
</EffectComposer>
**What I tried**
A small `Effect` inserted immediately before `` to fold non-finite values back into range:
import { forwardRef, useMemo } from 'react';
import { BlendFunction, Effect } from 'postprocessing';
import { Uniform } from 'three';
const fragment = `
uniform float ceiling;
// NaN fails every comparison, so a component that is neither below the ceiling
// nor at/above it can only be NaN. Inf settles at the ceiling.
float sanitize(const in float x) {
return (x < ceiling) ? max(x, 0.0) : ((x >= ceiling) ? ceiling : 0.0);
}
void mainImage(const in vec4 src, const in vec2 uv, out vec4 dst) {
dst = vec4(sanitize(src.r), sanitize(src.g), sanitize(src.b), src.a);
}`;
class ClampHDREffect extends Effect {
constructor(ceiling) {
super('ClampHDREffect', fragment, {
// SRC returns the effect output verbatim. Other blend functions route the
// input through mix(), and mix() with an Inf input yields NaN again.
blendFunction: BlendFunction.SRC,
uniforms: new Map([['ceiling', new Uniform(ceiling)]]),
});
}
}
const ClampHDR = forwardRef(({ ceiling = 64 }, ref) => {
const effect = useMemo(() => new ClampHDREffect(ceiling), [ceiling]);
return <primitive ref={ref} object={effect} />;
});
export default ClampHDR;
Used as:
<ClampHDR />
<Bloom intensity={1} luminanceThreshold={1} luminanceSmoothing={1} radius={0.25} />
Did not work, added this here so that you know that I have tried this already.