0

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/postprocessing
  • THREE.ShaderMaterial for background
  • layers for separating bloom targets
  • <Select> from @react-three/postprocessing for 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>
  );
}```
1
  • I ran into the same issue with selective bloom affecting the background and other meshes. Tried a few hacks but none worked reliably. In the end, if there’s no real interaction needed between the “sun” and the rest of the scene, the cleanest solution is to render two scenes/canvases — one with the sun + postprocessing (bloom), and one with the rest. Commented Sep 25 at 9:40

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.