SelectiveBloom blooming the whole model / emissive texture

Hi guys

I am struggling in getting a selective bloom (maybe I am using the wrong tool for the job or I misunderstood how it works…).

What I want to achieve:
Model blooming the same color as the emissive texture (without manually setting it, so the color is taken straight from the emissive image) AND the rest of the model not blooming.

What I achieved instead:
The full model is blooming and is blooming white not the color of the emissive texture.

As you can see the full model is glowing and is glowing white (I know I set it to 255,255,255 but it seems to make almost no different to set it to another color, not to mention I can’t rely on that I need to rely on the emissive texture color to bloom, what if the texture has two parts where it needs to emit two different colors? setting the emissive color in RGB value won’t be an option…)

telegram-cloud-photo-size-4-5987712112237066141-x

As you can see the emissive texture is black and green as desired:

We have models with some separate 3d meshes so that we can (or we thought we could easily) make things selectively bloom…

What I tried (code is not prod ready is just a messy mess trying to achieve the objective):
I tried setting a userData boolean (isEmissive) and assigning to that a emissiveIntensity

import { useMemo, useRef, useState } from 'react';
import { useGLTF } from '@react-three/drei';
import { Bloom, EffectComposer, SelectiveBloom } from '@react-three/postprocessing';
import { Resizer, KernelSize } from 'postprocessing';

const ModelViewer = ({ model }) => {
  const glb = useGLTF(`./../game/3dModels/${model}.glb`);
  const clone = useMemo(() => glb.scene.clone(), [glb]);
  const bloomRef = useRef();
  const lightRef = useRef();
  const lightRef2 = useRef();

  useMemo(() => {
    const { scene } = glb;

    scene.traverse((node) => {
      if (node.isMesh) {
        const isEmissive = node.userData.isEmissive;

        console.log({
          name: node.name,
          emissive: node.userData.isEmissive,
          userData: node.userData,
        });

        node.castShadow = true;
        node.receiveShadow = true;

        if (isEmissive) {
          node.material.emissiveIntensity = 1;
          // TODO: fix this as it needs to come from the emissive texture
          node.material.emissive = {
            r: 255,
            g: 255,
            b: 255,
          };
        } else {
          node.material.emissiveIntensity = 0;
        }
      }

      console.log({ node });
    });
  }, [glb]);

  return (
    <>
      <hemisphereLight ref={lightRef} intensity={0.5} position={[5, 10, 50]} />
      <ambientLight ref={lightRef2} intensity={0.5} position={[-5, 1, -10]} />
      <primitive object={clone} scale={0.8} ref={bloomRef} />
      <EffectComposer>
        {/* <Bloom
          intensity={1}
          luminanceThreshold={0.9} // luminance threshold. Raise this value to mask out darker elements in the scene.
          luminanceSmoothing={0.05} // smoothness of the luminance threshold. Range is [0, 1]
          height={Resizer.AUTO_SIZE}
          width={Resizer.AUTO_SIZE}
        /> */}
        <SelectiveBloom
          lights={[lightRef, lightRef2]} // ⚠️ REQUIRED! all relevant lights
          // selectionLayer={23} // selection layer
          selections={[bloomRef]}
          intensity={0.2} // The bloom intensity.
          width={Resizer.AUTO_SIZE} // render width
          height={Resizer.AUTO_SIZE} // render height
          kernelSize={KernelSize.SMALL} // blur kernel size
          luminanceThreshold={0.2} // luminance threshold. Raise this value to mask out darker elements in the scene.
          luminanceSmoothing={0.9} // smoothness of the luminance threshold. Range is [0, 1]
        />
      </EffectComposer>
    </>
  );
};

export default ModelViewer;

I also tried to separate the isEmissive meshes and selectively put JUST them in the in the selections prop of the SelectiveBloom but it didn’t work either.

Any help is welcome…

1 Like

Hi!
Maybe this topic will be helpful: https://discourse.threejs.org/t/selective-bloom-not-working-in-separate-files/30289/3

here’s an example Selective outlines - CodeSandbox but generally postprocessing works a little differently and using an emissivemap with some kind of blur shader is probably best but that’s not what pp does.

Hi Guys thanks for replying, to clarify even further what I want to achieve is a result similar to the one below:

given by the following textures (emissive + color):

In other words what I want to achieve is the following that babylon.js does with a one liner ahah:

as they state, and as I thought three js did:

By default the glow layer will use emissive texture and emissive color to generate the glow color of every active mesh

Hi mate, thx for the reply! It seems the page is 404 for me for some reason

1 Like

Ah, here is the direct link to the example: https://eppkn.csb.app/

Ah, so we would need custom shaders/fragments in order to achieve that kind of bloom/glow effect out of emissives?

Babylon’s seems to have it out of the box, I will play around with those fragment/shaders (and try to understand how they can be integrated into the from @react-three/postprocessing, do a round of prayers and see what happens ahah.

i dont quite understand why this is not in threejs. feed the material that map, blur and overlay it. though i don’t know anything about shaders so there sure must be a catch.

this looks great! would the same be possible without rendering the scene twice? could a generic standardmaterial with some onbeforecompile replace perhaps just multiply a blurred texture? and can shaders affect stuff outside of the mesh region?

Just to clarify as I am quite new, and as you are the lib author who’s better than you to ask ahaha.

Would there be a declarative simple way to achieve what I want using react-postprocessing? @drcmda

i dont quite understand why this is not in threejs. feed the material that map, blur and overlay it.

Just takes someone with a need to build it and make a PR :man_shrugging:

feed the material that map, blur and overlay it.

I think normally what you want for this kind of bloom is HDR rendering, which some (most?) of the three.js postprocessing effects are not set up for. With HDR rendering you can render colors outside the [0, 1] range and then doing something bloom, tonemap, etc values that are brighter that are above 1.0. This would require using float targets for the post processing targets and any internal render targets to the effects. Though rendering just the bits you want bloomed in separate pass would work and save you from using float targets.

In this case the emissive textures would result in particularly bright pixels if they have a high enough intensity (above 1.0) and then the bloom threshold could be set to 1.0 so just those bright pixels have bloom applied.

Taking a quick look at the unreal bloom pass it looks like there are at least a couple things that need to change to make this work including making sure float targets are used in the UnrealBloomPass class.

im not that libraries author i just help out, but in its current form it just naively allows you to use standard pp effects. anything that requires manual work is still better kept manual atm. im not sure selective bloom is really the answer for scalable scenes, i think it must render the parts you want to bloom twice (?).

i think postprocessing (the library) either already uses float targets or can be configured. i never really understood the difference between effects and materials tbh. i imagine it like this: effects is just a screen filling quad with a shader material that allows multiple passes? but then everything that works in the effectcomposer, for instance, could just apply to a common meshstandardmaterial - like it could be retro-fitted to render bloom outside of the mesh it’s covering?

i think postprocessing (the library) either already uses float targets or can be configured.

The three.js post processing can be configured to use float type for the read / write targets, as well. But the internal targets aren’t always necessarily configurable. Doing a quick test on the bloom example for three.js it seems that what I described above already works – you just have to pass float targets into the EffectComposer constructor and then bloom can work on anything brighter than [0, 1]. Looks like this also works for vanruescs lib.

i imagine it like this: effects is just a screen filling quad with a shader material that allows multiple passes? but then everything that works in the effectcomposer, for instance, could just apply to a common meshstandardmaterial - like it could be retro-fitted to render bloom outside of the mesh it’s covering?

GPUs work by transforming triangles, rasterizing pixels for those triangles into the screen, and then generating a color for every pixel. That means that effects like bloom or lens distortion that breach or change the edge of the triangle can’t be with a regular material. Post processing affords looking at previously rendered data and sibling pixels to render effects. And even if an effect can be done with a regular material it can be a lot more expensive to. It’s important to note that with overdraw it means that pixels that aren’t seen behind other objects often still have to run the possibly very expensive operations involved in the effect so just rendering a quad minimizes where these operations have to be run.

2 Likes