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