ThreeJS How to use Bloom Post-Processing Without NPM in VanillaJS

3.4k Views Asked by At

I want a bloom effect for my scene when using an emissive map like shown in this ThreeJS Example.

I've tried to understand the code a little bit but I'm basically stuck. The examples are all made with NPM and I do not use this method for my project. I'm sure it is possible to have the bloom effect without the help of this but I struggle to make sense of it all.

As for what I have already, just a basic setup with StandarMeshMaterial:

scene = new THREE.Scene();
loader = new THREE.TextureLoader()
camera = new THREE.PerspectiveCamera( 47, (window.innerWidth/window.innerHeight) / (windowHeight*heightRatio), 0.01, 10000 );
renderer = new THREE.WebGLRenderer( { canvas: document.getElementById( canvasElement ), antialias: true, alpha: true } );
controls = new THREE.OrbitControls( camera, renderer.domElement );

ect..

function animate() {
    requestAnimationFrame( animate );           
    controls.update();              
    renderer.render( scene, camera );           
};  
        
ect..

I really just want to apply some post-processing effect so my emissive materials actually appear to be glowing, which is not whats happening at the moment but I just cannot figure out how..

What would be the simplest way to get this result?

2

There are 2 best solutions below

4
On BEST ANSWER

First, NPM is not a framework. It is a package manager to install libraries your project depends on without manually downloading and copying the scripts to your project folder. What I read from your question is that you are not familiar with that module approach. You want to insert scripts and all three.js related stuff should be available under the global namespace THREE?

Assuming that you downloaded three.js to a folder named three, you could import the scripts as follows. Ensure to load the scripts from examples/js and not examples/jsm.

<script src="three/build/three.min.js"></script>
<script src="three/examples/js/controls/OrbitControls.js"></script>
<script src="three/examples/js/loaders/GLTFLoader.js"></script>
<script src="three/examples/js/postprocessing/EffectComposer.js"></script>
<script src="three/examples/js/postprocessing/RenderPass.js"></script>
<script src="three/examples/js/postprocessing/UnrealBloomPass.js"></script>

Now, you can use these classes under the THREE namespace.

const renderScene = new THREE.RenderPass( scene, camera );

const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );

Follow the example code, remove the import statements and add THREE where missing.

2
On

The examples are not made with npm.

Here's the example running below. The only thing changed is the paths of the modules and the url of the model.

#info > * {
  max-width: 650px;
  margin-left: auto;
  margin-right: auto;
}
<link type="text/css" rel="stylesheet" href="https://threejs.org/examples/main.css">
<div id="container"></div>

<div id="info">
  <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Bloom pass by <a href="http://eduperiment.com" target="_blank" rel="noopener">Prashant Sharma</a> and <a href="https://clara.io" target="_blank" rel="noopener">Ben Houston</a>
  <br/>
  Model: <a href="https://blog.sketchfab.com/art-spotlight-primary-ion-drive/" target="_blank" rel="noopener">Primary Ion Drive</a> by
  <a href="http://mjmurdock.com/" target="_blank" rel="noopener">Mike Murdock</a>, CC Attribution.
</div>

<script type="module">
  import * as THREE from 'https://threejs.org/build/three.module.js';

  import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';
  import { GUI } from 'https://threejs.org/examples/jsm/libs/dat.gui.module.js';

  import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
  import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
  import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
  import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
  import { UnrealBloomPass } from 'https://threejs.org/examples/jsm/postprocessing/UnrealBloomPass.js';

  let camera, stats;
  let composer, renderer, mixer, clock;

  const params = {
    exposure: 1,
    bloomStrength: 1.5,
    bloomThreshold: 0,
    bloomRadius: 0
  };

  init();

  function init() {

    const container = document.getElementById( 'container' );

    stats = new Stats();
    container.appendChild( stats.dom );

    clock = new THREE.Clock();

    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.toneMapping = THREE.ReinhardToneMapping;
    container.appendChild( renderer.domElement );

    const scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
    camera.position.set( - 5, 2.5, - 3.5 );
    scene.add( camera );

    const controls = new OrbitControls( camera, renderer.domElement );
    controls.maxPolarAngle = Math.PI * 0.5;
    controls.minDistance = 1;
    controls.maxDistance = 10;

    scene.add( new THREE.AmbientLight( 0x404040 ) );

    const pointLight = new THREE.PointLight( 0xffffff, 1 );
    camera.add( pointLight );

    const renderScene = new RenderPass( scene, camera );

    const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
    bloomPass.threshold = params.bloomThreshold;
    bloomPass.strength = params.bloomStrength;
    bloomPass.radius = params.bloomRadius;

    composer = new EffectComposer( renderer );
    composer.addPass( renderScene );
    composer.addPass( bloomPass );

    new GLTFLoader().load( 'https://threejs.org/examples/models/gltf/PrimaryIonDrive.glb', function ( gltf ) {

      const model = gltf.scene;

      scene.add( model );

      mixer = new THREE.AnimationMixer( model );
      const clip = gltf.animations[ 0 ];
      mixer.clipAction( clip.optimize() ).play();

      animate();

    } );

    const gui = new GUI();

    gui.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {

      renderer.toneMappingExposure = Math.pow( value, 4.0 );

    } );

    gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {

      bloomPass.threshold = Number( value );

    } );

    gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function ( value ) {

      bloomPass.strength = Number( value );

    } );

    gui.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {

      bloomPass.radius = Number( value );

    } );

    window.addEventListener( 'resize', onWindowResize );

  }

  function onWindowResize() {

    const width = window.innerWidth;
    const height = window.innerHeight;

    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    renderer.setSize( width, height );
    composer.setSize( width, height );

  }

  function animate() {

    requestAnimationFrame( animate );

    const delta = clock.getDelta();

    mixer.update( delta );

    stats.update();

    composer.render();

  }

</script>

What you need to do is use <script type="module"> so that modern import statements work

You should then copy the three.js files as tree like this

someFolder
 |
 ├-build
 | |
 | +-three.module.js
 |
 +-examples
   |
   +-jsm
     |
     +-controls
     | |
     | +-OrbitControls.js
     | +-TrackballControls.js
     | +-...
     |
     +-loaders
     | |
     | +-GLTFLoader.js
     | +-...
     |
     ...

And adjust your paths as appropriate

See this article