Search code examples
javascriptreactjstypescriptthree.jspost-processing

PostProccesing import issues (threejs + reactjs)


I have spent multiple hours trying to figure this out. Basically I have a Threejs scene formatted as a react component (meaning the initialization of the scene is done in ComponentDidMount() and the animation functions and resize are bound to "this". The scene itself is rendered into canvas, and used by the webpage as a component).

The main issue I think I'm having is typescript imports. I get all kinds of errors when trying to import the postprocessing module. Here is what I have tried: (These errors occurred after I installed the official three and postprocessing packages from npm)

1: official documentation from three.js (I got errors from just the importing, meaning I got more errors when trying to use the imports)

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';

this yielded:

Module not found: Can't resolve 'three/examples/jsm/postprocessing/EffectComposer.js' in 'C:\Users\Jan\Documents\GitHub\mm\src\components'

So I tried using the relative path in the node module folder (without webpack)

2: using relative path:

import { EffectComposer } from "../../node_modules/three/examples/js/postprocessing/EffectComposer.js";
import { RenderPass } from "../../node_modules/three/examples/js/postprocessing/RenderPass.js";
import { GlitchPass } from "../../node_modules/three/examples/js/postprocessing/GlitchPass.js";

this imports well, so I tried using the imports in a simple use case on the official threejs site. (this is after everything else has been initialized):

var composer = new EffectComposer(renderer);
var renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

unfortunately, I get this error (I think its a typescript/vanilla issue but I could be wrong):

ReferenceError: THREE is not defined
./node_modules/three/examples/js/postprocessing/EffectComposer.js
C:/Users/Simon/Documents/GitHub/mm/node_modules/three/examples/js/postprocessing/EffectComposer.js:4
  1 | /**
  2 |  * @author alteredq / http://alteredqualia.com/
  3 |  */
> 4 | THREE.EffectComposer = function (renderer, renderTarget) {
  5 |   this.renderer = renderer;
  6 | 
  7 |   if (renderTarget === undefined) {

Finally, I tried importing the npm module postprocessing in the same way I import threejs:

3: importing postproccesing from postproccesing:

import * as THREE from "three"; //how i usually import three
import * as POSTPROCESSING from "postprocessing";

this yields this error (note: i don't use LinearMipmapLinearFilter anywhere in my code):

./node_modules/postprocessing/build/postprocessing.esm.js
Attempted import error: 'LinearMipmapLinearFilter' is not exported from 'three'.

4: I also tried having the postprocessing.min.js file in my root folder and loading from there but that gave me like thousands of errors because three never gets imported in there.

Any suggestions or examples would be greatly appreciated. Is there a way to get around this, or should I modify the three package to make "2: using relative path" work? Am I just doing something completely wrong? Thank you.

similar issue: https://discourse.threejs.org/t/shaderpass-post-processing-in-effectcomposer-removes-alpha-channel-of-objects-in-the-scene/9481/7

for reference, here is my entire ComponentDidMount function:

  componentDidMount() {
    const width = this.mount.clientWidth;
    const height = this.mount.clientHeight;
    const scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x03544e, 0.001);
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 2000);
    counter = 0;
    camera.position.z = 100;

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    var stColor = new THREE.Color("hsl(201.6, 50%, 50%)");
    renderer.setClearColor(0x111111);
    var orangeLight = new THREE.PointLight(stColor, 50, 450, 2);
    orangeLight.position.set(200, 300, 100);
    scene.add(orangeLight);
    var redLight = new THREE.PointLight(stColor, 50, 450, 2);
    redLight.position.set(100, 300, 100);
    scene.add(redLight);
    var blueLight = new THREE.PointLight(stColor, 50, 450, 6);
    blueLight.position.set(0, 300, 300);
    scene.add(blueLight);
    var greenLight = new THREE.PointLight(stColor, 50, 450, 2);
    greenLight.position.set(200, 300, -300);
    scene.add(greenLight);
    var cloudParticles = [];
    let loader = new THREE.TextureLoader();
    loader.load(smokeTex, function (texture) {
      var cloudGeo = new THREE.PlaneBufferGeometry(500, 500);
      var cloudMaterial = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        depthWrite: false,
        size: 0.2,
      });

      for (let p = 0; p < 40; p++) {
        let cloud = new THREE.Mesh(cloudGeo, cloudMaterial);
        cloud.position.set(
          Math.random() * 600 - 300,
          0,
          Math.random() * 300 - 600
        );
        cloud.rotation.x = 0.02;
        cloud.rotation.z = Math.random() * 2 * Math.PI;
        cloud.material.opacity = 0.55;
        cloudParticles.push(cloud);
        scene.add(cloud);
      }
    });
    var tubePoints = [];

    var incrm = 0;
    for (var i = 0; i < 5; i += 1) {
      var xC = 4 - i;
      incrm = xC * xC - xC;
      tubePoints.push(new THREE.Vector3(0, incrm / 3, 3 * (i / 4)));
    }

    var curve = new THREE.CatmullRomCurve3(tubePoints);
    curve.type = "catmullrom";

    var tubeGeo = new THREE.Geometry();
    tubeGeo.vertices = curve.getPoints(70);
    var tubeTex = loader.load(gal, function (tubeTex) {
      tubeTex.wrapS = THREE.MirroredRepeatWrapping;
      tubeTex.wrapT = THREE.MirroredRepeatWrapping;
      tubeTex.repeat.set(2, 2);
    });
    var tubeMaterial = new THREE.MeshBasicMaterial({
      side: THREE.BackSide,
      map: tubeTex,
      transparent: true,
      opacity: 1,
    });
    var tubeGeometry = new THREE.TubeGeometry(curve, 70, 0.3, 30, false);
    tubeGeometry.verticesNeedUpdate = true;
    tubeGeometry.dynamic = true;
    var tubeMesh = new THREE.Mesh(tubeGeometry, tubeMaterial);
    scene.add(tubeMesh);
    tubeMesh.position.z = 96.9;
    scene.add(tubeMesh);
    var speedMat = -0.001;
    // var composer = new EffectComposer(renderer);
    // var renderPass = new RenderPass(scene, camera);
    // composer.addPass(renderPass);
    // var glitchPass = new GlitchPass();
    // composer.addPass(glitchPass);
    this.speedMat = speedMat;
    this.tubeMesh = tubeMesh;
    this.scene = scene;
    this.camera = camera;
    this.tubeGeometry = tubeGeometry;
    this.cloudParticles = cloudParticles;
    this.tubeMaterial = tubeMaterial;
    this.renderer = renderer;
    this.blueLight = blueLight;
    this.redLight = redLight;
    this.orangeLight = orangeLight;
    this.greenLight = greenLight;
    this.mount.appendChild(this.renderer.domElement);
    this.start();

Solution

  • For anyone trying to figure this out for Angular, here is how I was able to import things.

    import * as EffectComposer from 'three/examples/jsm/postprocessing/EffectComposer';
    import * as RenderPass from 'three/examples/jsm/postprocessing/RenderPass';
    import * as SMAAPass from 'three/examples/jsm/postprocessing/SMAAPass';
    
    const composer = new EffectComposer.EffectComposer(this.renderer);
    composer.addPass(new RenderPass.RenderPass(this.scene, this.camera));
    composer.addPass(new SMAAPass.SMAAPass(this.canvas.clientWidth,this.canvas.clientHeight));
    

    Compared to a non-Angular app

    import * as THREE from 'three';
    import { BloomEffect, EffectComposer, EffectPass, RenderPass, SMAAEffect } from "postprocessing";
    
    const composer = new EffectComposer(renderer);
    composer.addPass(new RenderPass(scene, camera));
    composer.addPass(new EffectPass(camera, new SMAAEffect()));