2

I'm using ThreeJS to generate a selective bloom effect in order to display glowing parts on an object using an emissive map as filter. Recently I tried enabling my environment map and realized that the bloom affects the scene.background appearance.

This is how I generate the environment map:

function createEnvironment( hdr ) {
    new RGBELoader().setDataType( THREE.UnsignedByteType ).load( hdr, function ( texture ) {        
    
        const pmremGenerator = new THREE.PMREMGenerator( renderer )
        pmremGenerator.compileEquirectangularShader()
        
        const envMap = pmremGenerator.fromEquirectangular( texture ).texture
        scene.background = texture;
        scene.environment = envMap
        
        texture.dispose()
        pmremGenerator.dispose()
    } );
}

At the moment the environement map glows like crazy. I looked a little into it but I couldn't find a solution since scene.background does not use a material and I couldn't find out how to do it any other way.

I'm stuck now. How can I make the selective bloom not affect scene.background?

Here is the selective bloom code I'm using https://stackoverflow.com/a/67113323/9067589

body{
  overflow: hidden;
  margin: 0;
}
<script type="x-shader/x-vertex" id="vertexshader">
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
  uniform sampler2D baseTexture;
  uniform sampler2D bloomTexture;
  varying vec2 vUv;
  void main() {
    gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
  }
</script>
<script type="module">
console.clear();
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
import {GLTFLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/GLTFLoader.js';

import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js';

import { EffectComposer } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/UnrealBloomPass.js';

let model;

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(7.7, 2.5, 7.2);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", e => {console.log(camera.position)})

let light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

let uniforms = {
  globalBloom: {value: 1}
}

let loader = new GLTFLoader();
loader.load( "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/turntable_121.glb", function ( gltf ) {
    model = gltf.scene;
  let emssvTex = new THREE.TextureLoader().load("https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Emission.jpeg", function( texture ) {
    texture.flipY = false
    texture.encoding = THREE.sRGBEncoding
  })    
  model.traverse( function ( child ) {
    if ( child.isMesh ) {
        child.material.emissiveMap = emssvTex;
      child.material.onBeforeCompile = shader => {
        shader.uniforms.globalBloom = uniforms.globalBloom;
        shader.fragmentShader = `
            uniform float globalBloom;
          ${shader.fragmentShader}
        `.replace(
            `#include <dithering_fragment>`,
          `#include <dithering_fragment>
            if (globalBloom > 0.5){
                gl_FragColor = texture2D( emissiveMap, vUv );
            }
          `
        );
        console.log(shader.fragmentShader);
      }
    }
  });
  model.scale.setScalar(40);
  scene.add(model);
});

// bloom
const renderScene = new RenderPass( scene, camera );

const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 2, 0, 0 );

const bloomComposer = new EffectComposer( renderer );
bloomComposer.renderToScreen = false;
bloomComposer.addPass( renderScene );
bloomComposer.addPass( bloomPass );

const finalPass = new ShaderPass(
  new THREE.ShaderMaterial( {
    uniforms: {
      baseTexture: { value: null },
      bloomTexture: { value: bloomComposer.renderTarget2.texture }
    },
    vertexShader: document.getElementById( 'vertexshader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
    defines: {}
  } ), "baseTexture"
);
finalPass.needsSwap = true;

const finalComposer = new EffectComposer( renderer );
finalComposer.addPass( renderScene );
finalComposer.addPass( finalPass );

window.onresize = function () {

  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( innerWidth, innerHeight );

  bloomComposer.setSize( innerWidth, innerHeight );
  finalComposer.setSize( innerWidth, innerHeight );

};

renderer.setAnimationLoop( _ => {
    
  renderer.setClearColor(0x000000);
  uniforms.globalBloom.value = 1;
  
  bloomComposer.render();
  
  renderer.setClearColor(0x404040);
  uniforms.globalBloom.value = 0;
  
    finalComposer.render();
  //renderer.render(scene, camera);
})

</script>

1 Answer 1

3

In the render loop, use scene.background instead of renderer.setClearColor.

Define scene backgrounds

let scback = {
   bloomOn: new THREE.Color(0x000000),
   bloomOff: new THREE.Color(0x404040) // or define something else, a cubetexture, for example.
}

then in the render loop:

renderer.setAnimationLoop( _ => {
    
  scene.background = scback.bloomOn; // must be pure black
  uniforms.globalBloom.value = 1;
  
  bloomComposer.render();
  
  scene.background = scback.bloomOff; // anything else you want to show
  uniforms.globalBloom.value = 0;
  
  finalComposer.render();
})

PS It's just from the scratch, haven't tested. :)

Sign up to request clarification or add additional context in comments.

3 Comments

It works but the environment map is very dark - any idea what I could do about that?
Ah.. setting renderer.toneMappingExposure changes the brightness of the environment map!
Do you have any idea on how to make bloom pass bg transparent? Currently it's black and when it darkened the final composer when texture get merged.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.