Accumulative Shadow from r3f to vanilla Threejs

I am trying to convert react-three-fiber Accumulative Shadow into a vanila threejs Code.
I am not sure what I miss and why its not working.

the ProgressiveLightMap class by @drcmda

import * as THREE from 'three'
function isLight(object) {
    return object.isLight;
  }
  
  function isGeometry(object) {
    return !!object.geometry;
  }
  
  const DiscardMaterial = new THREE.ShaderMaterial({
    uniforms: {},
    vertexShader: `void main() { gl_Position = vec4((uv - 0.5) * 2.0, 1.0, 1.0); }`,
    fragmentShader: `void main() { discard; }`,
  });
class ProgressiveLightMap {
    constructor(renderer, scene, res = 1024) {
      this.renderer = renderer
      this.res = res
      this.scene = scene
      this.buffer1Active = false
      this.lights = []
      this.meshes = []
      this.object = null
  
      // Create the Progressive LightMap Texture
      const format = /(Android|iPad|iPhone|iPod)/g.test(navigator.userAgent) ? THREE.HalfFloatType : THREE.FloatType
      this.progressiveLightMap1 = new THREE.WebGLRenderTarget(this.res, this.res, {
        type: format,
        encoding: renderer.outputEncoding,
      })
      this.progressiveLightMap2 = new THREE.WebGLRenderTarget(this.res, this.res, {
        type: format,
        encoding: renderer.outputEncoding,
      })
  
      // Inject some spicy new logic into a standard phong material
      this.discardMat =  DiscardMaterial
      this.targetMat = new THREE.MeshPhongMaterial({ shininess: 0 })
      this.previousShadowMap = { value: this.progressiveLightMap1.texture }
      this.averagingWindow = { value: 100 }
      this.targetMat.onBeforeCompile = (shader) => {
        // Vertex Shader: Set Vertex Positions to the Unwrapped UV Positions
        shader.vertexShader =
          'varying vec2 vUv;\n' +
          shader.vertexShader.slice(0, -1) +
          'vUv = uv; gl_Position = vec4((uv - 0.5) * 2.0, 1.0, 1.0); }'
  
        // Fragment Shader: Set Pixels to average in the Previous frame's Shadows
        const bodyStart = shader.fragmentShader.indexOf('void main() {')
        shader.fragmentShader = shader.fragmentShader.replace(
          '#include <clipping_planes_pars_fragment>',
          '#include <clipping_planes_pars_fragment>\n#include <shadowmask_pars_fragment>\n'
        )
        shader.fragmentShader =
          'varying vec2 vUv;\n' +
          shader.fragmentShader.slice(0, bodyStart) +
          '	uniform sampler2D previousShadowMap;\n	uniform float averagingWindow;\n' +
          shader.fragmentShader.slice(bodyStart - 1, -1) +
          `\nvec3 texelOld = texture2D(previousShadowMap, vUv).rgb;
          gl_FragColor.rgb = mix(texelOld, gl_FragColor.rgb, 1.0/averagingWindow);
        }`
  
        // Set the Previous Frame's Texture Buffer and Averaging Window
        shader.uniforms.previousShadowMap = this.previousShadowMap
        shader.uniforms.averagingWindow = this.averagingWindow
      }
    }
  
    clear() {
      this.renderer.setRenderTarget(this.progressiveLightMap1)
      this.renderer.clear()
      this.renderer.setRenderTarget(this.progressiveLightMap2)
      this.renderer.clear()
  
      this.lights = []
      this.meshes = []
      this.scene.traverse((object) => {
        if (isGeometry(object)) {
          this.meshes.push({ object, material: object.material })
        } else if (isLight(object)) {
          this.lights.push({ object, intensity: object.intensity })
        }
      })
    }
  
    prepare() {
      this.lights.forEach((light) => (light.object.intensity = 0))
      this.meshes.forEach((mesh) => (mesh.object.material = this.discardMat))
    }
  
    finish() {
        
      this.lights.forEach((light) => (light.object.intensity = light.intensity))
      this.meshes.forEach((mesh) => (mesh.object.material = mesh.material))
    }
  
    configure(object) {
      this.object = object
    }
  
    update(camera, blendWindow = 100) {
      if (!this.object) return
  
      // Set each object's material to the UV Unwrapped Surface Mapping Version
      this.averagingWindow.value = blendWindow
      this.object.material = this.targetMat
      // Ping-pong two surface buffers for reading/writing
      const activeMap = this.buffer1Active ? this.progressiveLightMap1 : this.progressiveLightMap2
      const inactiveMap = this.buffer1Active ? this.progressiveLightMap2 : this.progressiveLightMap1
      // Render the object's surface maps
      this.renderer.setRenderTarget(activeMap)
      this.previousShadowMap.value = inactiveMap.texture
      this.buffer1Active = !this.buffer1Active
      this.renderer.render(this.scene, camera)
      this.renderer.setRenderTarget(null)
    }
  }

  export{ProgressiveLightMap}

And then following the steps from this previous post

import React from "react";

import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

import { OrbitControls } from "./utils/others/orbitControl";
import { OrbitControlsGizmo } from "./utils/others/Gizmo";
import { HDRMapLoader } from "./utils/manager/textureLoader";
import fragment from "./utils/shader/fragment.glsl";
import vertex from "./utils/shader/vertex.glsl";

import { ProgressiveLightMap } from './utils/shaderPass/porgressiveLightMap'

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.InitialSetup();
  }

  InitialSetup = () => {
    this.shadowMapRes = 512;
    this.lightMapRes = 1024;
    this.lightCount = 8;
    this.lightOrigin = null;
    this.progressiveSurfacemap;
    this.dirLights = [];
    this.lightmapObjects = [];
    this.frames = Math.max(2, 40)
    this.blend = Math.max(2, this.frames === Infinity ? 20 : this.frames)
    this.alphaTest = 0.75
    this.colorBlend = 2
    this.scale = 10
    this.opacity = 1
    this.limit = Infinity
    this.toneMapped = true,
    this.count = 0


    this.container = document.getElementById("container");
    const item = document.getElementById("container").getBoundingClientRect();
    this.sizes = {
      width: item.width,
      height: item.height,
    };

    this.canvas = document.querySelector("canvas.webgl");
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color("#DAA520");
    this.camera = new THREE.PerspectiveCamera(
      75,
      this.sizes.width / this.sizes.height,
      0.001,
      1000
    );
    this.camera.position.set(20, 20, 20);
    this.scene.add(this.camera);

    this.manager = new THREE.LoadingManager();
    this.manager.onProgress = function (url, itemsLoaded, itemsTotal) {
      const ProgressVal = (itemsLoaded / itemsTotal) * 100;
      if (ProgressVal === 100) {
        console.log("scene loaded");
      }
    };

    this.controls = new OrbitControls(this.camera, this.canvas);
    this.controls.addEventListener("change", () => { });
    this.controls.enableDamping = true;
    this.controls.touches = {
      ONE: THREE.TOUCH.ROTATE,
      TWO: THREE.TOUCH.DOLLY_PAN,
    };
    this.controlsGizmo = new OrbitControlsGizmo(this.controls, {
      size: 100,
      padding: 8,
    });
    this.container.appendChild(this.controlsGizmo.domElement);

    this.renderer = new THREE.WebGLRenderer({
      canvas: this.canvas,
      antialias: true,
    });
    this.renderer.setSize(this.sizes.width, this.sizes.height);
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.renderer.toneMappingExposure = 1;
    this.renderer.outputEncoding = THREE.sRGBEncoding;
    this.renderer.shadowMap.enabled = true;
    HDRMapLoader("/hdr/default.hdr", this.renderer, this.scene, this.manager);

    this.progressiveSurfacemap = new ProgressiveLightMap(
      this.renderer,
      this.scene,
      this.lightMapRes
    );
    this.addObject();
 
    window.addEventListener("resize", this.resize);
    const render = () => {
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
      
      if (( this.frames === Infinity) && this.count < this.frames && this.count < Infinity) {
        this.updatePlane()
        this.count++
      }

      window.requestAnimationFrame(render);
    };
    render();
  };

  resize = () => {
    this.sizes.width = this.container.offsetWidth;
    this.sizes.height = this.container.offsetHeight;
    this.renderer.setSize(this.sizes.width, this.sizes.height);
    this.camera.aspect = this.sizes.width / this.sizes.height;
    this.camera.updateProjectionMatrix();
  };

  getGroundPlane() {
  }
  
 
  updateLight=()=>{
    const position=[0,0,0]
    const length = new THREE.Vector3(...position).length()
    for (let l = 0; l < this.dirLights.length; l++) {
      if (Math.random() > 0.5) {
        this.dirLights[l].position.set(
          position[0] + THREE.MathUtils.randFloatSpread(1),
          position[1] + THREE.MathUtils.randFloatSpread(1),
          position[2] + THREE.MathUtils.randFloatSpread(1)
        )
      } else {
        let lambda = Math.acos(2 * Math.random() - 1) - Math.PI / 2.0
        let phi = 2 * Math.PI * Math.random()
        this.dirLights[l].position.set(
          Math.cos(lambda) * Math.cos(phi) * length,
          Math.abs(Math.cos(lambda) * Math.sin(phi) * length),
          Math.sin(lambda) * length
        )
      }
    }
  }

  addPlane() {

    this.cube=new THREE.Mesh(new THREE.SphereGeometry(10,60),new THREE.MeshStandardMaterial({color:"#ff0000"}))
    this.cube.castShadow=true
    this.cube.receiveShadow=true
    this.cube.position.y = 9;
    this.scene.add(this.cube)

    this.material = new THREE.ShaderMaterial({
      uniforms: {
        color: { value: new THREE.Color(0xff0000) },
        opacity: { value: 0.0 },
        alphaTest: { value: 0.75 },
        blend: { value: 2.0 },
        map: { value: null },
      },
      vertexShader:  `varying vec2 vUv;
      void main() {
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.);
        vUv = uv;
      }`,

      fragmentShader: `varying vec2 vUv;
      uniform sampler2D map;
      uniform vec3 color;
      uniform float opacity;
      uniform float alphaTest;
      uniform float blend;
      void main() {
        vec4 sampledDiffuseColor = texture2D(map, vUv);
        gl_FragColor = vec4(color * sampledDiffuseColor.r * blend, max(0.0, (1.0 - (sampledDiffuseColor.r + sampledDiffuseColor.g + sampledDiffuseColor.b) / alphaTest)) * opacity);
        #include <tonemapping_fragment>
        #include <encodings_fragment>
      }`,
    });
    this.material3 = new THREE.ShadowMaterial();
    this.material3.opacity = 0.5;
    
    this.material2 = new THREE.MeshPhongMaterial({ color: "#ffffff" })
    this.groundMesh = new THREE.Mesh(
      new THREE.PlaneGeometry(100, 100),
      this.material
    );
    // this.material.transparent = true;
    this.material.depthWrite = false;
    this.material.toneMapped=true;
    this.material.uniforms.map = this.progressiveSurfacemap.progressiveLightMap2.texture;
    this.material.uniforms.opacity = 1;
    this.material.uniforms.alphaTest = 0.75;
    this.groundMesh.position.y = -0.5;
    this.groundMesh.rotation.x = -Math.PI / 2;
    this.groundMesh.receiveShadow = true;
    this.groundMesh.name = "Ground Mesh";
    this.scene.add(this.groundMesh);
    this.progressiveSurfacemap.configure(this.groundMesh);


  }


  addLights() {
    this.dirLights = []
    this.lightOrigin = new THREE.Group();
    this.lightOrigin.position.set(60, 60,60);
    this.scene.add(this.lightOrigin);
    for (let l = 0; l < this.lightCount; l++) {
      const dirLight = new THREE.DirectionalLight(0xffffff, 1.0 / this.lightCount);
      dirLight.name = 'Dir. Light ' + l;
    
      dirLight.castShadow = true;
      dirLight.shadow.camera.near = 100;
      dirLight.shadow.camera.far = 5000;
      dirLight.shadow.camera.right = 150;
      dirLight.shadow.camera.left = - 150;
      dirLight.shadow.camera.top = 150;
      dirLight.shadow.camera.bottom = - 150;
      dirLight.shadow.mapSize.width = this.shadowMapRes;
      dirLight.shadow.mapSize.height = this.shadowMapRes;
      // const helper = new THREE.DirectionalLightHelper( dirLight);
      // this.scene.add( helper );

      this.lightOrigin.add(dirLight)
      this.dirLights.push(dirLight);
    }
  }

  addObject = () => {
    // const GLtfLoader = new GLTFLoader(this.manager);
    // GLtfLoader.load("/scene.glb", (gltf) => {
    //   gltf.scene.traverse((child) => {
    //     if (child instanceof THREE.Mesh) {
    //       child.castShadow = true;
    //       child.receiveShadow = true;
    //     } 
    //   });
    //   this.scene.add(gltf.scene);
    // });

    this.addPlane()
    this.addLights()

    this.resetPlane()
    if (this.frames !== Infinity) this.updatePlane(this.blend)

  };

  resetPlane() {
    this.progressiveSurfacemap.clear()
    const material = this.groundMesh.material
    material.opacity = 0
    material.alphaTest = 0
    this.count = 0
  }

  updatePlane(frames = 1) {
    // Adapt the opacity-blend ratio to the number of frames
    const material = this.groundMesh.material
     if (false) {
      material.opacity = this.opacity
      material.alphaTest = this.alphaTest
    } else {
      material.opacity = Math.min(this.opacity, material.opacity + this.opacity / this.blend)
      material.alphaTest = Math.min(this.alphaTest, material.alphaTest + this.alphaTest / this.blend)
    }

    this.lightOrigin.visible = true
    this.progressiveSurfacemap.prepare()
  
    for (let i = 0; i < frames; i++) {
      this.updateLight()
      this.progressiveSurfacemap.update(this.camera, 100)
    }
    // this.lightOrigin.visible = false
    this.progressiveSurfacemap.finish()
  }


  render() {
    return (
      <div id="container">
        <canvas className="webgl"></canvas>
      </div>
    );
  }
}

export default App;

I am not sure why its not working … have I missed any steps ?
Any help is appreciated .

Thankyou
SeeOn

could you throw this into a codesandbox? then we can compare notes.

1 Like

Here is the Above code in Sandbox @drcmda can you please help me.

1 Like

hard to know what’s going on tbh. it looks good but can’t see what’s make it fail. i think you need to go through line by line. i had to do the same with the original three example, but i think that one was even harder to grasp due to all the potpack stuff. or did you figure it out already?

Nope. Not yet. Was busy with something else.
this is the Closest I got so far.

and that to in weird way , by setting light intensity to 1 instead if 0 which is default in prepare Function.

I’ll put that in code sandbox soon …previous one is just crashing.

Here is the current state