I’m working on a solar system project using React, @react-three/fiber, and @react-three/postprocessing. I’m trying to achieve a selective bloom effect where only the Sun glows, and everything else (planets, 3D background shader, etc.) remains unaffected.
I’ve tried various implementations using <Select> and <Selection> from @react-three/postprocessing, setting custom layers for the Sun, and even tried toneMapped={false} on the material. However, the problem is always the same:
Either the entire scene gets affected by bloom, or everything becomes black except for the Sun.
My Sun has its own texture (meshBasicMaterial with toneMapped={false}) and rotates normally. The background is a custom 3D shader using ShaderMaterial on a huge sphere, and planets are using meshStandardMaterial.
No matter what I do, either the entire scene is bloomed, or everything else becomes black. I’ve been debugging this for days with no reliable outcome.
How do I apply selective bloom only to a single object (Sun), without affecting the rest of the scene (especially a 3D shader background)?
I would be super grateful for any help, suggestions, or working examples. I’m open to modifying structure if needed, as long as I can keep the shader background and make the scene performant.
What I’m using:
@react-three/fiber@react-three/drei@react-three/postprocessingTHREE.ShaderMaterialfor backgroundlayersfor separating bloom targets<Select>from@react-three/postprocessingfor bloom assignment
Here's my entire code:
const PLANET_TEXTURES: Record<string, string> = {
Mercury: "/textures/2k_mercury.jpg",
Venus: "/textures/2k_venus.jpg",
Earth: "/textures/2k_earth_daymap.jpg",
Mars: "/textures/2k_mars.jpg",
Jupiter: "/textures/2k_jupiter.jpg",
Saturn: "/textures/saturn.jpg",
Uranus: "/textures/uranus.jpg",
Neptune: "/textures/neptune.jpg",
};
function Sun() {
const sunTexture = useTexture("/textures/2k_sun.jpg");
sunTexture.colorSpace = THREE.SRGBColorSpace;
const sunRef = useRef<THREE.Mesh>(null);
const BLOOM_LAYER = 1;
useFrame((state, delta) => {
if (sunRef.current) {
sunRef.current.rotation.y += 0.001;
sunRef.current.layers.set(BLOOM_LAYER);
}
});
return (
<Select enabled>
<mesh ref={sunRef} scale={7}>
<sphereGeometry args={[1, 32, 32]} />
<meshBasicMaterial map={sunTexture} toneMapped={false} />
</mesh>
</Select>
);
}
function Planet({ name, radius, distance, speed, tilt, ring, moons }: any) {
const texture = useTexture(PLANET_TEXTURES[name]);
texture.colorSpace = THREE.SRGBColorSpace;
const moonTexture = useTexture("/textures/2k_moon.jpg");
moonTexture.colorSpace = THREE.SRGBColorSpace;
const ref = useRef<THREE.Mesh>(null);
const moonRefs = useRef<THREE.Mesh[]>([]);
useFrame((state, delta) => {
if (ref.current) {
ref.current.rotation.y += speed;
ref.current.position.x = Math.sin(ref.current.rotation.y) * distance;
ref.current.position.z = Math.cos(ref.current.rotation.y) * distance;
}
moons?.forEach((moon: any, i: number) => {
const moonMesh = moonRefs.current[i];
if (moonMesh) {
moonMesh.rotation.y += moon.speed;
moonMesh.position.x = Math.sin(moonMesh.rotation.y) * moon.distance;
moonMesh.position.z = Math.cos(moonMesh.rotation.y) * moon.distance;
}
});
});
return (
<mesh ref={ref} rotation={[tilt || 0, 0, 0]}>
<sphereGeometry args={[radius, 32, 32]} />
<meshStandardMaterial map={texture} />
{ring && <PlanetRing name={name} ring={ring} />}
{moons?.map((moon: any, i: number) => (
<mesh key={moon.name} ref={(el) => (moonRefs.current[i] = el as THREE.Mesh)}>
<sphereGeometry args={[moon.radius, 32, 32]} />
<meshStandardMaterial map={moonTexture} />
</mesh>
))}
</mesh>
);
}
function PlanetRing({ name, ring }: any) {
const ringTexture = useTexture(`/textures/${name.toLowerCase()}_ring.png`);
ringTexture.colorSpace = THREE.SRGBColorSpace;
return (
<mesh rotation={[Math.PI / 3, 0, 0]}>
<ringGeometry args={[ring.innerRadius, ring.outerRadius, 64]} />
<meshStandardMaterial
map={ringTexture}
side={THREE.DoubleSide}
transparent
opacity={0.8}
/>
</mesh>
);
}
function Background() {
const meshRef = useRef<THREE.Mesh>(null);
const material = useMemo(
() =>
new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
},
side: THREE.BackSide,
depthWrite: false,
depthTest: true,
}),
[]
);
useFrame((state) => {
material.uniforms.iTime.value = state.clock.getElapsedTime();
if (meshRef.current) {
meshRef.current.position.copy(state.camera.position);
}
});
return (
<mesh ref={meshRef} renderOrder={-1}>
<sphereGeometry args={[1000, 32, 32]} />
<primitive object={material} />
</mesh>
);
}
export default function SolarSystem() {
const planets = [
{ name: "Mercury", radius: 0.4, distance: 12, speed: 0.006, moons: [] },
{ name: "Venus", radius: 0.8, distance: 17, speed: 0.004, moons: [] },
{
name: "Earth",
radius: 0.9,
distance: 22,
speed: 0.002,
moons: [{ name: "Moon", radius: 0.3, distance: 3, speed: 0.009 }],
},
{
name: "Mars",
radius: 0.6,
distance: 28,
speed: 0.0009,
moons: [
{ name: "Phobos", radius: 0.1, distance: 2, speed: 0.008 },
{ name: "Deimos", radius: 0.2, distance: 3, speed: 0.007 },
],
},
{ name: "Jupiter", radius: 1.9, distance: 38, speed: 0.0007, moons: [], tilt: Math.PI / 8 },
{
name: "Saturn",
radius: 1.7,
distance: 45,
speed: 0.0005,
moons: [],
tilt: Math.PI / 6,
ring: { innerRadius: 0.5, outerRadius: 2.5 },
},
{
name: "Uranus",
radius: 1.4,
distance: 55,
speed: 0.0002,
moons: [],
tilt: Math.PI / 6,
ring: { innerRadius: 0, outerRadius: 2 },
},
{ name: "Neptune", radius: 1.3, distance: 63, speed: 0.00008, moons: [], tilt: Math.PI / 8 },
];
return (
<div style={{ position: 'fixed', inset: 0, width: '100vw', height: '100vh', background: 'black' }}>
<Canvas
camera={{ position: [0, 5, 100], fov: 35 }}
style={{ width: '100vw', height: '100vh', display: 'block', background: 'black' }}
gl={{ preserveDrawingBuffer: true }}
onCreated={({ camera }) => camera.layers.enable(1)}
>
<OrbitControls
enableDamping
minDistance={20}
maxDistance={200}
/>
<EffectComposer>
<Bloom luminanceThreshold={0.1} luminanceSmoothing={0.9} intensity={1.5} />
</EffectComposer>
<ambientLight intensity={0.2} color={0xbdb7ee} />
<pointLight intensity={3000} color={0xfffaa7d} />
<Background />
<Sun />
{planets.map((planet) => (
<Planet key={planet.name} {...planet} />
))}
</Canvas>
</div>
);
}```