Pmndrs post-processing: How to get selective Bloom

Hey :slight_smile: I’ve previously asked about how to get selective bloom to work using Threejs’ post-processing here. Now I am attempting to achieve the same goal but using the pmndrs post-processing library since it promises better performance. I managed to get the whole scene to bloom by writing the following:

import { WebGLRenderer } from 'three';
import {
    BloomEffect,
    EffectComposer, 
    EffectPass, 
    RenderPass 
} from "postprocessing";

const renderer = new WebGLRenderer({
    powerPreference: "high-performance",
    antialias: false,
});

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomEffect = new BloomEffect({
    mipmapBlur: true,
    luminanceThreshold: 0.8,
    radius : 0.05,
    intensity: 1
});
composer.addPass(new EffectPass(camera, bloomEffect));

But id like to get selective bloom, so I tried implementing what @drcmda suggests here and here. His suggestion is to raise the bloom threshold to a value greater than 1 (so that nothing blooms) and then increasing the emissivity intensity (of objects id like to bloom) to very high values so that they bloom. To apply this method, I increase luminanceThreshold 0.8 to 2 and create the following test cube:

    //test cube
    const geometry = new THREE.BoxGeometry( 1, 1, 1 ); 
    const cube = new THREE.Mesh( geometry, 
        new THREE.MeshStandardMaterial({
            toneMapped: false,
            emissive: "red",
            emissiveIntensity: 10

        })   
        ); 
    scene.add( cube );

however the cube does not bloom at all :confused: I tried turning tonemapping off, and still wont work. Does anyone have an idea what Im doing wrong here?

On a side note, the pmndrs library also has a SelectiveBloomEffect, but i also didnt understand how can i specify the mesh which i want to bloom. any help or guidance is highly appreciated!

threejs had some breaking changes, toneMapping has no affect any longer. try this box, it creates select bloom with both jsm and postpro bloom hdr workflow - CodeSandbox i only write react so i don’t have a vanilla example but it’s the same: in essence you should not have tonemapping enabled in your webglrenderer, and you need new passes: outputpass for jsm and tonemapping for postpro. with this selecting bloom by going outside the threshold with color should work again as it did before.

you don’t need SelectiveBloomEffect, using colors should be enough.

Thanks @drcmda for your reply. I looked at your example and tried to recreate something similar; however I still dont have the results im looking for. I will share the code. I create two cubes, one red cube which i would like to bloom, and one green cube, which doenst have bloom. The result is that nothing glows and the background turns white. Let me know your thoughts:

//Imports for standard Threejs
import * as THREE from 'three';

//Imports for pmndrs post processing library
import { WebGLRenderer } from 'three';
import {
    BloomEffect,
    EffectComposer, 
    EffectPass, 
    RenderPass 
} from "postprocessing";

// Create scene
const scene = new THREE.Scene();

// Create red GLOWING cube
const redGeometry = new THREE.BoxGeometry();
const redMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color().setRGB(10, 0, 0) }); 
// color is set to 10 in order to go out of glow threshold 
const redCube = new THREE.Mesh(redGeometry, redMaterial);
redCube.position.x = -2; // Position to the left
scene.add(redCube);

// Create green cube (NON-GLOWING)
const greenGeometry = new THREE.BoxGeometry();
const greenMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const greenCube = new THREE.Mesh(greenGeometry, greenMaterial);
greenCube.position.x = 2; // Position to the right
scene.add(greenCube);

// Create camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;

// Create renderer
const renderer = new WebGLRenderer({
	powerPreference: "high-performance",
	antialias: false,
});

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

//Create Effects
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomEffect = new BloomEffect({
    mipmapBlur: true,
    luminanceThreshold: 2, // threshold is greater than 1 in order to remove glow from scene
    radius : 0.05,
    intensity: 5
});
composer.addPass(new EffectPass(camera, bloomEffect));

// Animation function
const animate = () => {
  requestAnimationFrame(animate);

  // Rotate cubes
  redCube.rotation.x += 0.01;
  redCube.rotation.y += 0.01;
  greenCube.rotation.x += 0.01;
  greenCube.rotation.y += 0.01;

  composer.render(scene, camera);
};

// Handle window resize
window.addEventListener('resize', () => {
  const newWidth = window.innerWidth;
  const newHeight = window.innerHeight;

  camera.aspect = newWidth / newHeight;
  camera.updateProjectionMatrix();

  
  renderer.setSize(newWidth, newHeight);
});

// Start animation
animate();

can you try, that’s what the abstraction does

const effectComposer = new EffectComposerImpl(gl, {
  multisampling: 8,
  frameBufferType: THREE.HalfFloatType,
})

otherwise you have to make a quick codesandbox, with all the recent changes in three i have no clear idea of how this stuff works any longer.

I have placed the example above in a CodeSandbox

it’s composer.render() not renderer.render(scene, camera) but something is strange in csb today, no matter what i do i don’t see it reflect any of the changes made.

strange, even if you change something simple like the color of the cubes? because for me its working.

Concerning composer.render(): In my main project, im using it like that, but i forgot to apply it aswell to this example with the two cubes. Ive adjusted it now, but the main issue is still there.

I’m noticing a possible issue with the pmndrs post-processing library when it comes to blooming. The issue is that the blooming effect is highly dependent on the color of the object. In my CodeSandbox Example, if i turn the luminanceThreshold down to 0.1, both the red and green boxes glow. As i increase it to 0.4, the red box stops glowing and the green still glows. Increasing it to 0.9, then both of them stop glowing. Shouldnt the threshold of the blooming affect all colors uniformly ?

This is also noticeable in the official bloom example from pmndrs post-processing library. You can see in the example that red objects bloom much much less than green or white objects.

Am i missing something? Hope someone can clarify whats happening there. Thanks in advance! :heart_hands:

Extra info about my endgoal: I want to animate my car designs using three js, and i want to use selective bloom for the glow of the white headlights at the front and the red brake lights at the rear.

I did some more digging and found the right way to implement selective Bloom. The critical line I needed to know about was bloomEffect.selection.add(mesh);

Ill paste the full code here in case anyone in the future faces the same issue and if the codesandbox example becomes no longer available:

//Imports for standard Threejs
import * as THREE from 'three';

//Imports for pmndrs post processing library
import { WebGLRenderer } from 'three';
import {
    SelectiveBloomEffect,
    BlendFunction,
    EffectComposer, 
    EffectPass, 
    RenderPass 
} from "postprocessing";

// Create scene
const scene = new THREE.Scene();

// Create red GLOWING cube BASIC
const redGeometry = new THREE.BoxGeometry();
const redMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color().setRGB(1, 0
  , 0) }); 
// color is set to 10 in order to go out of glow threshold 
const redCube = new THREE.Mesh(redGeometry, redMaterial);
redCube.position.x = -2; // Position to the left
scene.add(redCube);
console.log(redCube)

// Create green cube (NON-GLOWING)
const greenGeometry = new THREE.BoxGeometry();
const greenMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const greenCube = new THREE.Mesh(greenGeometry, greenMaterial);
greenCube.position.x = 2; // Position to the right
scene.add(greenCube);

// Create camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;

// Create renderer
const renderer = new WebGLRenderer({
	powerPreference: "high-performance",
	antialias: false,
});


renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

//Create Effects
const bloomEffect = new SelectiveBloomEffect(scene, camera, {
  intensity: 5,
  mipmapBlur: true,
  luminanceThreshold: 0,
  luminanceSmoothing: 0.2,
  radius : 0.618,
  resolutionScale: 4
});

bloomEffect.selection.add(redCube);

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new EffectPass(camera, bloomEffect));

// Animation function
const animate = () => {
  requestAnimationFrame(animate);

  // Rotate cubes
  redCube.rotation.x += 0.01;
  redCube.rotation.y += 0.01;
  greenCube.rotation.x += 0.01;
  greenCube.rotation.y += 0.01;

  composer.render(scene, camera);
};

// Handle window resize
window.addEventListener('resize', () => {
  const newWidth = window.innerWidth;
  const newHeight = window.innerHeight;

  camera.aspect = newWidth / newHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(newWidth, newHeight);
});

// Start animation
animate();