How to Apply Bloom to Specific Materials in Three.js
In Three.js, the UnrealBloomPass normally applies a
glowing effect to the entire canvas based on color intensity. To
restrict this glow to specific materials, you must implement a technique
called selective bloom. This process involves using
camera layers, rendering the glowing objects separately to a dedicated
bloom buffer, and then blending that buffer back over the non-glowing
scene using a custom combination shader.
Step 1: Define Camera Layers
Three.js cameras can filter which objects they render using layers. Assign a specific layer number to represent the glowing objects.
import * as THREE from 'three';
const ENTIRE_SCENE = 0;
const BLOOM_SCENE = 1;
const bloomLayer = new THREE.Layers();
bloomLayer.set(BLOOM_SCENE);To make an object glow, enable this layer on the mesh:
const glowingMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const glowingMesh = new THREE.Mesh(geometry, glowingMaterial);
// Enable the bloom layer on this mesh
glowingMesh.layers.enable(BLOOM_SCENE);
scene.add(glowingMesh);Step 2: Set Up the Composers
You need two separate EffectComposer instances: one to
generate the bloom texture and another to render the final combined
image.
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. Bloom Composer (renders black background + glowing objects)
const renderPass = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
const bloomComposer = new EffectComposer(renderer);
bloomComposer.renderToScreen = false; // We only want the texture
bloomComposer.addPass(renderPass);
bloomComposer.addPass(bloomPass);Step 3: Create the Blend Shader
Next, create a final composer that merges the standard scene render with the bloom texture generated by the bloom composer.
// Custom shader to combine the original scene with the bloom texture
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;
// 2. Final Composer
const finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderPass);
finalComposer.addPass(mixPass);Step 4: Implement the Render Loop
During the render loop, you must temporarily hide non-glowing objects to prevent them from blocking or receiving the bloom effect. This is achieved by swapping all non-glowing materials with a basic black material, rendering the bloom composer, restoring the materials, and then rendering the final composer.
Define a black material and a utility to swap materials:
const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });
const materialsCache = {};
function darkenNonBloomed(obj) {
if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
materialsCache[obj.uuid] = obj.material;
obj.material = darkMaterial;
}
}
function restoreMaterial(obj) {
if (materialsCache[obj.uuid]) {
obj.material = materialsCache[obj.uuid];
delete materialsCache[obj.uuid];
}
}Implement this logic inside your main animation loop:
function animate() {
requestAnimationFrame(animate);
// 1. Prepare scene for bloom pass (darken non-glow objects)
scene.traverse(darkenNonBloomed);
// 2. Render bloom texture
bloomComposer.render();
// 3. Restore original materials
scene.traverse(restoreMaterial);
// 4. Render the final composite image
finalComposer.render();
}
animate();By isolating the glowing materials to a separate layer, temporarily
rendering non-glowing elements as solid black, and combining the passes
via a custom shader, only the specified materials will display the
UnrealBloomPass glow.