How to Apply Selective Bloom in Three.js
This article explains how to isolate and apply a glowing bloom effect to specific emissive objects in a Three.js scene without affecting the entire environment. You will learn how to configure layers, set up post-processing composers, and use a shader material to merge a selective bloom pass with your main scene.
Step 1: Define Layers and Assign Objects
Three.js uses layers to control which objects are rendered by which camera or pass. Define a specific layer index for your glowing objects, and enable that layer on the mesh you want to glow.
import * as THREE from 'three';
const ENTIRE_SCENE = 0;
const BLOOM_SCENE = 1;
const bloomLayer = new THREE.Layers();
bloomLayer.set(BLOOM_SCENE);
// Create your glowing object
const geometry = new THREE.IcosahedronGeometry(1, 4);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const glowingMesh = new THREE.Mesh(geometry, material);
// Assign the mesh to the bloom layer
glowingMesh.layers.enable(BLOOM_SCENE);
scene.add(glowingMesh);Step 2: Set Up the Post-Processing Composers
You need two EffectComposer instances: one to generate
the bloom effect texture, and another to render the final combined
scene.
You will also need to import the necessary post-processing passes from the Three.js library.
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
// 1. Basic Render Pass
const renderPass = new RenderPass(scene, camera);
// 2. Bloom Composer (renders the glow)
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // Strength
0.4, // Radius
0.85 // Threshold
);
const bloomComposer = new EffectComposer(renderer);
bloomComposer.renderToScreen = false; // We only want the texture output
bloomComposer.addPass(renderPass);
bloomComposer.addPass(bloomPass);Step 3: Create the Blend Shader
To combine the bloomed objects with the rest of the scene, create a
custom ShaderPass. This shader takes the original scene
render and adds the bloom texture on top of it.
const mixShader = {
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: bloomComposer.renderTarget2.texture }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
varying vec2 vUv;
void main() {
gl_FragColor = (texture2D(baseTexture, vUv) + vec4(1.0) * texture2D(bloomTexture, vUv));
}
`
};
const mixPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: mixShader.uniforms,
vertexShader: mixShader.vertexShader,
fragmentShader: mixShader.fragmentShader,
defines: {}
}),
'baseTexture'
);
mixPass.needsSwap = true;
// 3. Final Composer (renders everything together)
const finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderPass);
finalComposer.addPass(mixPass);Step 4: Implement the Material Swap Logic
To prevent non-glowing objects from showing up in the bloom texture, you must temporarily hide them during the bloom pass render.
This is achieved by swapping non-bloomed materials with a completely black basic material before rendering the bloom composer, and then restoring them immediately afterward.
const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });
const materialsStore = {};
function darkenNonBloomed(obj) {
if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
materialsStore[obj.uuid] = obj.material;
obj.material = darkMaterial;
}
}
function restoreMaterial(obj) {
if (materialsStore[obj.uuid]) {
obj.material = materialsStore[obj.uuid];
delete materialsStore[obj.uuid];
}
}Step 5: Update the Animation Loop
In your render/animation loop, execute the material swap, render the bloom composer, restore the original materials, and finally render the main composer.
function animate() {
requestAnimationFrame(animate);
// 1. Prepare scene for bloom (turn non-bloomed objects black)
scene.traverse(darkenNonBloomed);
// 2. Render the bloom texture
bloomComposer.render();
// 3. Restore original materials
scene.traverse(restoreMaterial);
// 4. Render the final blended scene
finalComposer.render();
}
animate();