Pmndrs Post Processing: Selective Bloom renders differently on Chrome vs Firefox

This is a question concerning the usage of the pmndrs postprocessing library. In my scene, I have loaded an HDRI and a model of a car. Id like to implement selective bloom in order to mimic the glow of the brake lights. I added the red light bulb mesh to the bloom effect using the following line:

bloomEffect.selection.add(lights_rear_bulb);

This allowed the object to bloom but I noticed that the whole HDRI background is blooming like crazy. I did some digging and people proposed that I increase the threshold to a high value so that nothing blooms, and then push colors on materials I want to bloom over the threshold. So that’s what I did, heres the material of the brake lights which I assign to the mesh “lights_rear_bulb”

    const brakeLightMaterial = new THREE.MeshBasicMaterial( {
          toneMapped: false,
          color: new THREE.Color().setRGB(400, 0, 0)
        });

And heres the setup of the bloom:

    const bloomEffect = new SelectiveBloomEffect(scene, camera, {
        intensity: 10,
        mipmapBlur: true,
        luminanceThreshold: 50, 
        luminanceSmoothing: 0.25,
        radius: 0.2,
        resolutionScale: 4,
    });

So the threshold is 50, but I noticed that if I push the red color to 51 or 60, no bloom was happening, only until I reached 400 was the bloom effect present. Am i doing this right? Or does anyone have another recommended method of achieving what im trying to achieve: basically, having bloom ONLY for the mesh “lights_rear_bulb”
My main question has to do with the fact that im getting different results on different browsers. The mesh “lights_rear_bulb” is behind a mesh called “lights_rear_glass” which represents the glass cover of the rear lights. The bloom effect passes through it when I use Chrome or Edge, but it gets blocked when I use Firefox. I’ve added images for clarification. Any idea whats happening there? Is there other big differences I should be aware of when it comes to these popular browsers?


I created a simple scene to try and find the issue, I would appreciate if people from the community would give me feedback on their thoughts and observations.

The scene has to cubes: one green non-blooming cube and one red blooming cube. The bottom half of the scene is covered by a glass plane. This mimics the scenario of rendering the brake lights of a car.
My goal is to have the blooming of the red cube pass through the glass plane, and also to have the same results across all browsers. At the moment, I am not getting the same results on chrome and firefox, and for both of them, the blooming is not passing through the glass. I have included two ways to define the red cube: either through the emissive method, or through the color method. I have also included a commented line which includes the glass cover in the bloom pass. If you uncomment this line, the bloom passes through the glass on chrome but not on firefox. I am curious to hear your thoughts, thanks in advance! :smiley:

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <title>Three.js Two Cubes</title>
    <style>
      body {
        margin: 0;
      }
    </style>
  </head>
  <body>

        <script type="importmap">
        {
            "imports": {
            "three": "https://unpkg.com/three@0.150.0/build/three.module.js",
            "three/addons/": "https://unpkg.com/three@0.150.0/examples/jsm/",
			"postprocessing": "https://unpkg.com/postprocessing@6.33.3/build/index.js"
            }
        }
        </script>

		<script type="module" src="/pmndrsBloomTest3.js"></script>
        
    </body>
</html>

pmndrsBloomTest3.js:

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

//Imports for pmndrs post processing library
import { 
    WebGLRenderer,
    HalfFloatType 
} from "three";

import {
  SelectiveBloomEffect,
  BlendFunction,
  EffectComposer,
  EffectPass,
  RenderPass,
  ToneMappingEffect,
  ToneMappingMode,
} from "postprocessing";

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

// // // // // Create glass cover 
var coverMaterial = new THREE.MeshPhysicalMaterial( {
    color: 0xffffff, metalness: 0, roughness: 0, transmission: 0.99
  } );
var planeGeometry = new THREE.PlaneGeometry(10, 5); // You can adjust the size as needed
var coverPlane = new THREE.Mesh(planeGeometry, coverMaterial);
coverPlane.position.y = -2.3; // Half of the plane's height
coverPlane.position.z = 1
scene.add(coverPlane);

// // // // // Create red cube (GLOWING)
const redGeometry = new THREE.BoxGeometry();

// // Glowing Material: Color Method
// const redMaterial = new THREE.MeshBasicMaterial( {
//     toneMapped: false,
//     color: new THREE.Color().setRGB(400, 0, 0)
//   });

// // Glowing Material: Emissive Method
const redMaterial = new THREE.MeshStandardMaterial({
    toneMapped: false,
    emissive: "red",
    emissiveIntensity: 400
})

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

// // Bloom
const bloomEffect = new SelectiveBloomEffect(scene, camera, {
    intensity: 10,
    mipmapBlur: true,
    luminanceThreshold: 50, // had to increase in order to keep out blooming from hdri
    luminanceSmoothing: 0.25,
    radius: 0.2,
    resolutionScale: 4,
});


bloomEffect.selection.add(redCube);

// Try adding this line
//bloomEffect.selection.add(coverPlane);

// // Tone-Mapping
const TMeffect = new ToneMappingEffect({
    blendFunction: BlendFunction.NORMAL,
    mode: ToneMappingMode.ACES_FILMIC,
    resolution: 256,
    whitePoint: 4.0,
    middleGrey: 0.6,
    minLuminance: 0.01,
    averageLuminance: 0.01,
    adaptationRate: 1.0
});

const TMeffectPass = new EffectPass(camera, TMeffect);


// // // // // Create Composer
const composer = new EffectComposer(renderer, {
    multisampling: Math.min(4, renderer.capabilities.maxSamples),
    frameBufferType: HalfFloatType
});

composer.addPass(new RenderPass(scene, camera));
composer.addPass(new EffectPass(camera, bloomEffect));
composer.addPass(TMeffectPass);


// // // // // 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();

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

// Start animation
animate();