From b0575344fcf627b0eedfbe680481a35ca1c00f22 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 16:09:15 +0200
Subject: [PATCH 01/25] Clean-up
---
 example/index.js                              |  41 +++-
 example/materialBall.js                       |  44 +++-
 src/temporal-resolve/TemporalResolve.js       | 130 ++++++++++++
 .../ComposeTemporalResolveMaterial.js         |  36 ++++
 .../materials/TemporalResolveMaterial.js      |  36 ++++
 .../materials/VelocityShader.js               | 111 ++++++++++
 .../shaders/temporalResolveFragment.js        | 148 ++++++++++++++
 .../shaders/temporalResolveVertex.js          |   8 +
 .../passes/TemporalResolvePass.js             | 193 ++++++++++++++++++
 src/temporal-resolve/passes/VelocityPass.js   | 115 +++++++++++
 10 files changed, 857 insertions(+), 5 deletions(-)
 create mode 100644 src/temporal-resolve/TemporalResolve.js
 create mode 100644 src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
 create mode 100644 src/temporal-resolve/materials/TemporalResolveMaterial.js
 create mode 100644 src/temporal-resolve/materials/VelocityShader.js
 create mode 100644 src/temporal-resolve/materials/shaders/temporalResolveFragment.js
 create mode 100644 src/temporal-resolve/materials/shaders/temporalResolveVertex.js
 create mode 100644 src/temporal-resolve/passes/TemporalResolvePass.js
 create mode 100644 src/temporal-resolve/passes/VelocityPass.js
diff --git a/example/index.js b/example/index.js
index fc68bf1de..a6ad7a490 100644
--- a/example/index.js
+++ b/example/index.js
@@ -32,6 +32,7 @@ import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js
 import { PhysicalPathTracingMaterial, PathTracingRenderer, MaterialReducer, BlurredEnvMapGenerator } from '../src/index.js';
 import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
 
 const envMaps = {
 	'Royal Esplanade': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr',
@@ -71,6 +72,12 @@ const params = {
 	tilesY: 2,
 	samplesPerFrame: 1,
 
+	temporalResolve: true,
+	temporalResolveMix: 0.75,
+	clampRing: 1,
+	newSamplesSmoothing: 0.5,
+	newSamplesCorrection: 0.75,
+
 	model: initialModel,
 
 	envMap: envMaps[ 'Royal Esplanade' ],
@@ -104,7 +111,7 @@ const params = {
 let creditEl, loadingEl, samplesEl;
 let floorPlane, gui, stats, sceneInfo;
 let renderer, orthoCamera, perspectiveCamera, activeCamera;
-let ptRenderer, fsQuad, controls, scene;
+let ptRenderer, fsQuad, controls, scene, temporalResolve;
 let envMap, envMapGenerator;
 let loadingModel = false;
 let delaySamples = 0;
@@ -143,6 +150,12 @@ async function init() {
 	ptRenderer.material.bgGradientTop.set( params.bgGradientTop );
 	ptRenderer.material.bgGradientBottom.set( params.bgGradientBottom );
 
+	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
+	temporalResolve.temporalResolveMix = 0.75;
+	temporalResolve.clampRing = 1;
+	temporalResolve.newSamplesSmoothing = 0.5;
+	temporalResolve.newSamplesCorrection = 0.75;
+
 	fsQuad = new FullScreenQuad( new MeshBasicMaterial( {
 		map: ptRenderer.target.texture,
 		blending: CustomBlending
@@ -230,6 +243,17 @@ function animate() {
 		}
 
 		renderer.autoClear = false;
+		if ( params.temporalResolve ) {
+
+			temporalResolve.update();
+			fsQuad.material.map = temporalResolve.target.texture;
+
+		} else {
+
+			fsQuad.material.map = ptRenderer.target.texture;
+
+		}
+
 		fsQuad.render( renderer );
 		renderer.autoClear = true;
 
@@ -311,6 +335,21 @@ function buildGui() {
 
 	} );
 
+	const trFolder = gui.addFolder( 'Temporal Resolve' );
+	trFolder.add( params, 'temporalResolve' );
+	trFolder
+		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
+	trFolder
+		.add( params, 'clampRing', 1, 8, 1 )
+		.onChange( ( value ) => ( temporalResolve.clampRing = value ) );
+	trFolder
+		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
+	trFolder
+		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+
 	const resolutionFolder = gui.addFolder( 'resolution' );
 	resolutionFolder.add( params, 'resolutionScale', 0.1, 1.0, 0.01 ).onChange( () => {
 
diff --git a/example/materialBall.js b/example/materialBall.js
index 554339436..d1a850161 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -7,8 +7,9 @@ import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js
 import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
 import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
 import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
+import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
 
-let renderer, controls, sceneInfo, ptRenderer, activeCamera, fsQuad, materials;
+let renderer, controls, sceneInfo, ptRenderer, activeCamera, fsQuad, materials, temporalResolve;
 let perspectiveCamera, orthoCamera, equirectCamera;
 let envMap, envMapGenerator, scene;
 let samplesEl;
@@ -75,7 +76,6 @@ const params = {
 		matte: false,
 		castShadow: true,
 	},
-
 	multipleImportanceSampling: true,
 	stableNoise: false,
 	environmentIntensity: 1,
@@ -86,6 +86,11 @@ const params = {
 	samplesPerFrame: 1,
 	acesToneMapping: true,
 	resolutionScale: 1 / window.devicePixelRatio,
+	temporalResolve: true,
+	temporalResolveMix: 0.875,
+	clampRing: 2,
+	newSamplesSmoothing: 0.5,
+	newSamplesCorrection: 0.75,
 	transparentTraversals: 20,
 	filterGlossyFactor: 0.5,
 	tiles: 1,
@@ -158,6 +163,12 @@ async function init() {
 
 	scene = new THREE.Scene();
 
+	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
+	temporalResolve.temporalResolveMix = 0.875;
+	temporalResolve.clampRing = 2;
+	temporalResolve.newSamplesSmoothing = 0.5;
+	temporalResolve.newSamplesCorrection = 0.75;
+
 	samplesEl = document.getElementById( 'samples' );
 
 	envMapGenerator = new BlurredEnvMapGenerator( renderer );
@@ -315,6 +326,21 @@ async function init() {
 
 	} );
 
+	const trFolder = gui.addFolder( 'Temporal Resolve' );
+	trFolder.add( params, 'temporalResolve' );
+	trFolder
+		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
+	trFolder
+		.add( params, 'clampRing', 1, 8, 1 )
+		.onChange( ( value ) => ( temporalResolve.clampRing = value ) );
+	trFolder
+		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
+	trFolder
+		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+
 	const envFolder = gui.addFolder( 'Environment' );
 	envFolder.add( params, 'environmentIntensity', 0, 10 ).onChange( () => {
 
@@ -618,14 +644,24 @@ function animate() {
 
 	}
 
-	if ( ptRenderer.samples < 1 ) {
+	if ( ! params.temporalResolve && ptRenderer.samples < 1 ) {
 
 		renderer.render( scene, activeCamera );
 
 	}
 
 	renderer.autoClear = false;
-	fsQuad.material.map = ptRenderer.target.texture;
+	if ( params.temporalResolve ) {
+
+		temporalResolve.update();
+		fsQuad.material.map = temporalResolve.target.texture;
+
+	} else {
+
+		fsQuad.material.map = ptRenderer.target.texture;
+
+	}
+
 	fsQuad.render( renderer );
 	renderer.autoClear = true;
 
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
new file mode 100644
index 000000000..2dfa2684b
--- /dev/null
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -0,0 +1,130 @@
+import { HalfFloatType, LinearFilter, WebGLRenderTarget } from 'three';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
+import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial';
+import { TemporalResolvePass } from './passes/TemporalResolvePass';
+
+export class TemporalResolve {
+
+	constructor( ptRenderer, scene, camera ) {
+
+		this.ptRenderer = ptRenderer;
+		this.scene = scene;
+
+		this.temporalResolveMix = 0.75;
+		this.clampRing = 1;
+		this.newSamplesSmoothing = 0.5;
+		this.newSamplesCorrection = 0.75;
+
+		this.fullscreenMaterial = new ComposeTemporalResolveMaterial();
+
+		this.fsQuad = new FullScreenQuad( this.fullscreenMaterial );
+
+		this.renderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
+				type: HalfFloatType,
+				depthBuffer: false,
+			}
+		);
+
+		this.lastSize = { width: 0, height: 0 };
+
+		this.initNewCamera( camera );
+		this.initNewSize( window.innerWidth, window.innerHeight );
+
+	}
+
+	initNewCamera( camera ) {
+
+		this.activeCamera = camera;
+
+		this.temporalResolvePass = new TemporalResolvePass(
+			this.ptRenderer,
+			this.scene,
+			camera
+		);
+		this.temporalResolvePass.fullscreenMaterial.uniforms.samplesTexture.value =
+			this.ptRenderer.target.texture;
+
+		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
+			this.temporalResolvePass.renderTarget.texture;
+
+	}
+
+	initNewSize( width, height ) {
+
+		this.lastSize.width = width;
+		this.lastSize.height = height;
+
+		this.temporalResolvePass.setSize( width, height );
+
+	}
+
+	get target() {
+
+		return this.renderTarget;
+
+	}
+
+	update() {
+
+		const renderer = this.ptRenderer._renderer;
+
+		const origRenderTarget = renderer.getRenderTarget();
+
+		const { camera } = this.ptRenderer;
+		if ( camera !== this.activeCamera ) {
+
+			this.initNewCamera( camera );
+
+		}
+
+		const { width, height } = this.ptRenderer.target;
+		if ( width !== this.lastSize.width || height !== this.lastSize.height ) {
+
+			this.initNewSize( width, height );
+
+		}
+
+		// ensure that the scene's objects' matrices are updated for the VelocityPass
+		this.scene.updateMatrixWorld();
+
+		this.scene.traverse( ( c ) => {
+
+			// update the modelViewMatrix which is used by the VelocityPass
+			c.modelViewMatrix.multiplyMatrices(
+				this.activeCamera.matrixWorldInverse,
+				c.matrixWorld
+			);
+
+		} );
+
+		// keep uniforms updated
+		this.temporalResolvePass.fullscreenMaterial.uniforms.samples.value =
+			this.ptRenderer.samples;
+
+		this.temporalResolvePass.fullscreenMaterial.uniforms.temporalResolveMix.value =
+			this.temporalResolveMix;
+
+		this.temporalResolvePass.fullscreenMaterial.uniforms.clampRing.value =
+			parseInt( this.clampRing );
+
+		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesSmoothing.value =
+			this.newSamplesSmoothing;
+
+		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesCorrection.value =
+			this.newSamplesCorrection;
+
+		this.temporalResolvePass.render( renderer );
+
+		renderer.setRenderTarget( this.renderTarget );
+		this.fsQuad.render( renderer );
+
+		renderer.setRenderTarget( origRenderTarget );
+
+	}
+
+}
diff --git a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
new file mode 100644
index 000000000..d366eeb89
--- /dev/null
+++ b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
@@ -0,0 +1,36 @@
+import { ShaderMaterial } from 'three';
+
+const vertexShader = /* glsl */ `
+    varying vec2 vUv;
+
+    void main() {
+        vUv = position.xy * 0.5 + 0.5;
+        gl_Position = vec4(position.xy, 1.0, 1.0);
+    }
+`;
+
+const fragmentShader = /* glsl */ `
+    varying vec2 vUv;
+
+    uniform sampler2D temporalResolveTexture;
+
+    void main() {
+        gl_FragColor = vec4(texture2D(temporalResolveTexture, vUv).rgb, 1.);
+    }
+`;
+
+export class ComposeTemporalResolveMaterial extends ShaderMaterial {
+
+	constructor() {
+
+		super( {
+			vertexShader,
+			fragmentShader,
+			uniforms: {
+				temporalResolveTexture: { value: null },
+			},
+		} );
+
+	}
+
+}
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
new file mode 100644
index 000000000..ee18b6698
--- /dev/null
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -0,0 +1,36 @@
+import { Matrix4, ShaderMaterial } from 'three';
+import { fragmentShader } from './shaders/temporalResolveFragment';
+import { vertexShader } from './shaders/temporalResolveVertex';
+
+export class TemporalResolveMaterial extends ShaderMaterial {
+
+	constructor() {
+
+		super( {
+			type: 'TemporalResolveMaterial',
+			uniforms: {
+				inputTexture: { value: null },
+				samplesTexture: { value: null },
+				accumulatedSamplesTexture: { value: null },
+				velocityTexture: { value: null },
+				depthTexture: { value: null },
+				lastDepthTexture: { value: null },
+				samples: { value: 0 },
+				temporalResolveMix: { value: 0 },
+				clampRing: { value: 0 },
+				newSamplesSmoothing: { value: 0 },
+				newSamplesCorrection: { value: 0 },
+				curInverseProjectionMatrix: { value: new Matrix4() },
+				curCameraMatrixWorld: { value: new Matrix4() },
+				prevInverseProjectionMatrix: { value: new Matrix4() },
+				prevCameraMatrixWorld: { value: new Matrix4() },
+				cameraNear: { value: 0 },
+				cameraFar: { value: 0 },
+			},
+			vertexShader,
+			fragmentShader,
+		} );
+
+	}
+
+}
diff --git a/src/temporal-resolve/materials/VelocityShader.js b/src/temporal-resolve/materials/VelocityShader.js
new file mode 100644
index 000000000..f491df9dc
--- /dev/null
+++ b/src/temporal-resolve/materials/VelocityShader.js
@@ -0,0 +1,111 @@
+import { Matrix4, ShaderChunk } from 'three';
+
+// Modified ShaderChunk.skinning_pars_vertex to handle
+// a second set of bone information from the previou frame
+export const prev_skinning_pars_vertex = /* glsl */ `
+		#ifdef USE_SKINNING
+		#ifdef BONE_TEXTURE
+			uniform sampler2D prevBoneTexture;
+			mat4 getPrevBoneMatrix( const in float i ) {
+				float j = i * 4.0;
+				float x = mod( j, float( boneTextureSize ) );
+				float y = floor( j / float( boneTextureSize ) );
+				float dx = 1.0 / float( boneTextureSize );
+				float dy = 1.0 / float( boneTextureSize );
+				y = dy * ( y + 0.5 );
+				vec4 v1 = texture2D( prevBoneTexture, vec2( dx * ( x + 0.5 ), y ) );
+				vec4 v2 = texture2D( prevBoneTexture, vec2( dx * ( x + 1.5 ), y ) );
+				vec4 v3 = texture2D( prevBoneTexture, vec2( dx * ( x + 2.5 ), y ) );
+				vec4 v4 = texture2D( prevBoneTexture, vec2( dx * ( x + 3.5 ), y ) );
+				mat4 bone = mat4( v1, v2, v3, v4 );
+				return bone;
+			}
+		#else
+			uniform mat4 prevBoneMatrices[ MAX_BONES ];
+			mat4 getPrevBoneMatrix( const in float i ) {
+				mat4 bone = prevBoneMatrices[ int(i) ];
+				return bone;
+			}
+		#endif
+		#endif
+	`;
+
+// Returns the body of the vertex shader for the velocity buffer and
+// outputs the position of the current and last frame positions
+export const velocity_vertex = /* glsl */ `
+		vec3 transformed;
+
+		// Get the normal
+		${ShaderChunk.skinbase_vertex}
+		${ShaderChunk.beginnormal_vertex}
+		${ShaderChunk.skinnormal_vertex}
+		${ShaderChunk.defaultnormal_vertex}
+
+		// Get the current vertex position
+		transformed = vec3( position );
+		${ShaderChunk.skinning_vertex}
+		newPosition = velocityMatrix * vec4( transformed, 1.0 );
+
+		// Get the previous vertex position
+		transformed = vec3( position );
+		${ShaderChunk.skinbase_vertex
+		.replace( /mat4 /g, '' )
+		.replace( /getBoneMatrix/g, 'getPrevBoneMatrix' )}
+		${ShaderChunk.skinning_vertex.replace( /vec4 /g, '' )}
+		prevPosition = prevVelocityMatrix * vec4( transformed, 1.0 );
+
+		gl_Position = newPosition;
+
+	`;
+
+export const VelocityShader = {
+	uniforms: {
+		prevVelocityMatrix: { value: new Matrix4() },
+		velocityMatrix: { value: new Matrix4() },
+		prevBoneTexture: { value: null },
+		interpolateGeometry: { value: 0 },
+		intensity: { value: 1 },
+		boneTexture: { value: null },
+
+		alphaTest: { value: 0.0 },
+		map: { value: null },
+		alphaMap: { value: null },
+		opacity: { value: 1.0 },
+	},
+
+	vertexShader: /* glsl */ `
+			${ShaderChunk.skinning_pars_vertex}
+			${prev_skinning_pars_vertex}
+
+			uniform mat4 velocityMatrix;
+			uniform mat4 prevVelocityMatrix;
+			uniform float interpolateGeometry;
+			varying vec4 prevPosition;
+			varying vec4 newPosition;
+
+			void main() {
+
+				${velocity_vertex}
+
+			}
+		`,
+
+	fragmentShader: /* glsl */ `
+			uniform float intensity;
+			varying vec4 prevPosition;
+			varying vec4 newPosition;
+
+			void main() {
+				vec2 pos0 = (prevPosition.xy / prevPosition.w) * 0.5 + 0.5;
+				vec2 pos1 = (newPosition.xy / newPosition.w) * 0.5 + 0.5;
+
+				vec2 vel = pos1 - pos0;
+				
+				gl_FragColor = vec4( vel, 0., 1. );
+
+			}
+		`,
+	defines: {
+		MAX_BONES: 256,
+	},
+};
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
new file mode 100644
index 000000000..ae756849f
--- /dev/null
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -0,0 +1,148 @@
+export const fragmentShader = /* glsl */ `
+uniform sampler2D inputTexture;
+uniform sampler2D samplesTexture;
+uniform sampler2D accumulatedSamplesTexture;
+uniform sampler2D velocityTexture;
+uniform sampler2D depthTexture;
+uniform sampler2D lastDepthTexture;
+
+uniform float samples;
+
+uniform float temporalResolveMix;
+uniform int clampRing;
+uniform float newSamplesSmoothing;
+uniform float newSamplesCorrection;
+
+uniform mat4 curInverseProjectionMatrix;
+uniform mat4 curCameraMatrixWorld;
+
+uniform mat4 prevInverseProjectionMatrix;
+uniform mat4 prevCameraMatrixWorld;
+
+uniform float cameraNear;
+uniform float cameraFar;
+
+varying vec2 vUv;
+
+#include 
+
+// credits for transforming screen position to world position: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532/2
+vec3 screenSpaceToWorldSpace(const vec2 uv, const float depth, mat4 inverseProjectionMatrix, mat4 cameraMatrixWorld) {
+    vec4 ndc = vec4(
+        (uv.x - 0.5) * 2.0,
+        (uv.y - 0.5) * 2.0,
+        (depth - 0.5) * 2.0,
+        1.0);
+
+    vec4 clip = inverseProjectionMatrix * ndc;
+    vec4 view = cameraMatrixWorld * (clip / clip.w);
+
+    return view.xyz;
+}
+
+void main() {
+	vec4 samplesTexel = texture2D(samplesTexture, vUv);
+
+	// in case this pixel is from a tile that wasn't rendered yet
+	if(samplesTexel.a == 0.){
+		gl_FragColor = vec4(texture2D(inputTexture, vUv).rgb, 0.);
+		return;
+	}
+
+	vec4 depthTexel = texture2D(depthTexture, vUv);
+		
+	// background doesn't need reprojection
+	if(length(depthTexel.xyz) == 0.){
+		gl_FragColor = samplesTexel;
+		return;
+	}
+
+	float unpackedDepth = unpackRGBAToDepth(depthTexel);
+	vec3 curWorldPos = screenSpaceToWorldSpace(vUv, unpackedDepth, curInverseProjectionMatrix, curCameraMatrixWorld);
+
+
+	ivec2 size = textureSize(samplesTexture, 0);
+	vec2 pxSize = vec2(float(size.x), float(size.y));
+
+    vec4 velocityTexel = texture2D(velocityTexture, vUv);
+
+    vec2 velUv = velocityTexel.xy;
+    float movement = length(velUv) * 100.;
+
+    vec2 reprojectedUv = vUv - velUv;
+
+	float lastUnpackedDepth = unpackRGBAToDepth(texture2D(lastDepthTexture, reprojectedUv));
+
+	vec3 lastWorldPos = screenSpaceToWorldSpace(vUv, lastUnpackedDepth, prevInverseProjectionMatrix, prevCameraMatrixWorld);
+
+	float distToLastFrame = pow(distance(curWorldPos, lastWorldPos), 2.) * 0.25;
+
+	vec4 accumulatedSamplesTexel;
+    vec3 newColor;
+	float alpha;
+
+	// check that the reprojected UV is valid
+	if (reprojectedUv.x >= 0. && reprojectedUv.x <= 1. && reprojectedUv.y >= 0. && reprojectedUv.y <= 1.) {
+		accumulatedSamplesTexel = texture2D(accumulatedSamplesTexture, reprojectedUv);
+		alpha = accumulatedSamplesTexel.a;
+        alpha = distToLastFrame < 0.05 ? (0.1 + alpha) : 0.;
+		
+		vec2 px = 1. / pxSize;
+
+		vec3 minNeighborColor = vec3(1., 1., 1.);
+		vec3 maxNeighborColor = vec3(0., 0., 0.);
+
+		vec3 totalColor;
+
+		// use a small ring if there is a lot of movement otherwise there will be more smearing
+		int ring = movement > 1. ? 1 : clampRing;
+		
+		for(int x = -ring; x <= ring; x++){
+			for(int y = -ring; y <= ring; y++){
+				vec3 col;
+
+				if(x == 0 && y == 0){
+					col = samplesTexel.rgb;
+				}else{
+					vec2 curOffset = vec2(float(x), float(y));
+
+					col = textureLod(samplesTexture, vUv + px * curOffset, 0.).rgb;
+				}
+
+				minNeighborColor = min(col, minNeighborColor);
+				maxNeighborColor = max(col, maxNeighborColor);
+
+				if(x <= 1 && x >= -1 && y <= 1 && y >= -1) totalColor += col;
+			}
+		}
+
+		// clamp the reprojected frame (neighborhood clamping)
+		accumulatedSamplesTexel.rgb = clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor);
+
+		if(newSamplesSmoothing != 0. && alpha < 1.){
+			totalColor /= 9.;
+			samplesTexel.rgb = mix(samplesTexel.rgb, totalColor, newSamplesSmoothing);
+		}
+	} else {
+		// reprojected UV coordinates are outside of screen, so just use the current frame for it
+		alpha = 0.;
+		accumulatedSamplesTexel.rgb = samplesTexel.rgb;
+	}
+
+	float m = (1. - min(movement * 2., 1.) * (1. - temporalResolveMix)) - (samples - 1.) * 0.01 - 0.025;
+	
+	m = clamp(m, 0., 1.);
+	
+	newColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
+
+	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
+	// so make the final color blend more towards the new pixel
+	if(alpha < 1.){
+		float correctionMix = min(movement, 0.5) * newSamplesCorrection;
+
+		newColor = mix(newColor, samplesTexel.rgb, correctionMix);
+	}
+
+    gl_FragColor = vec4(newColor, alpha);
+}
+`;
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveVertex.js b/src/temporal-resolve/materials/shaders/temporalResolveVertex.js
new file mode 100644
index 000000000..ae9654b87
--- /dev/null
+++ b/src/temporal-resolve/materials/shaders/temporalResolveVertex.js
@@ -0,0 +1,8 @@
+export const vertexShader = /* glsl */ `
+varying vec2 vUv;
+
+void main() {
+    vUv = position.xy * 0.5 + 0.5;
+    gl_Position = vec4(position.xy, 1.0, 1.0);
+}
+`;
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
new file mode 100644
index 000000000..06533a637
--- /dev/null
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -0,0 +1,193 @@
+import {
+	Color,
+	FramebufferTexture,
+	HalfFloatType,
+	LinearFilter,
+	MeshDepthMaterial,
+	NearestFilter,
+	RGBADepthPacking,
+	RGBAFormat,
+	Vector2,
+	WebGLRenderTarget
+} from 'three';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
+import { TemporalResolveMaterial } from '../materials/TemporalResolveMaterial';
+import { VelocityPass } from './VelocityPass';
+
+const zeroVec2 = new Vector2();
+const meshDepthMaterial = new MeshDepthMaterial( {
+	depthPacking: RGBADepthPacking,
+} );
+const blackColor = new Color( 0 );
+
+export class TemporalResolvePass {
+
+	constructor( ptRenderer, scene, camera ) {
+
+		this.ptRenderer = ptRenderer;
+		this.scene = scene;
+		this.camera = camera;
+
+		this.renderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
+				type: HalfFloatType,
+				depthBuffer: false,
+			}
+		);
+
+		this.sceneRenderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
+			}
+		);
+
+		this.depthRenderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: NearestFilter,
+				magFilter: NearestFilter,
+			}
+		);
+
+		this.velocityPass = new VelocityPass( scene, camera );
+
+		this.fullscreenMaterial = new TemporalResolveMaterial();
+
+		this.fullscreenMaterial.uniforms.velocityTexture.value =
+			this.velocityPass.renderTarget.texture;
+		this.fullscreenMaterial.uniforms.depthTexture.value =
+			this.depthRenderTarget.texture;
+		this.fullscreenMaterial.uniforms.lastDepthTexture.value =
+			this.lastDepthTexture;
+
+		this.fsQuad = new FullScreenQuad( null );
+		this.fsQuad.material = this.fullscreenMaterial;
+
+		this.setSize( window.innerWidth, window.innerHeight );
+
+	}
+
+	dispose() {
+
+		this.renderTarget.dispose();
+		this.accumulatedSamplesTexture.dispose();
+		this.lastDepthTexture.dispose();
+		this.fullscreenMaterial.dispose();
+
+	}
+
+	setSize( width, height ) {
+
+		this.renderTarget.setSize( width, height );
+		this.sceneRenderTarget.setSize( width, height );
+		this.depthRenderTarget.setSize( width, height );
+		this.velocityPass.setSize( width, height );
+
+		this.createFramebuffers( width, height );
+
+	}
+
+	createFramebuffers( width, height ) {
+
+		if ( this.accumulatedSamplesTexture )
+			this.accumulatedSamplesTexture.dispose();
+		if ( this.lastDepthTexture ) this.lastDepthTexture.dispose();
+
+		this.accumulatedSamplesTexture = new FramebufferTexture(
+			width,
+			height,
+			RGBAFormat
+		);
+
+		this.accumulatedSamplesTexture.minFilter = LinearFilter;
+		this.accumulatedSamplesTexture.magFilter = LinearFilter;
+		this.accumulatedSamplesTexture.type = HalfFloatType;
+
+		this.lastDepthTexture = new FramebufferTexture( width, height, RGBAFormat );
+		this.lastDepthTexture.minFilter = NearestFilter;
+		this.lastDepthTexture.magFilter = NearestFilter;
+
+		this.fullscreenMaterial.uniforms.accumulatedSamplesTexture.value =
+			this.accumulatedSamplesTexture;
+		this.fullscreenMaterial.uniforms.lastDepthTexture.value =
+			this.lastDepthTexture;
+
+		this.fullscreenMaterial.needsUpdate = true;
+
+	}
+
+	render( renderer ) {
+
+		// render depth
+		this.scene.overrideMaterial = meshDepthMaterial;
+		renderer.setRenderTarget( this.depthRenderTarget );
+		renderer.clear();
+		const { background } = this.scene;
+
+		this.scene.background = blackColor;
+		renderer.render( this.scene, this.camera );
+		this.scene.background = background;
+		this.scene.overrideMaterial = null;
+
+		// render velocity
+		this.velocityPass.render( renderer );
+
+		if ( this.ptRenderer.tiles.x !== 1 || this.ptRenderer.tiles.y !== 1 ) {
+
+			if ( this.ptRenderer.samples < 1 ) {
+
+				renderer.setRenderTarget( this.sceneRenderTarget );
+				renderer.clear();
+				renderer.render( this.scene, this.camera );
+
+				this.fullscreenMaterial.uniforms.inputTexture.value =
+					this.sceneRenderTarget.texture;
+
+			}
+
+		} else {
+
+			delete this.fullscreenMaterial.uniforms.inputTexture.value;
+
+		}
+
+		// update uniforms of this pass
+		this.fullscreenMaterial.uniforms.curInverseProjectionMatrix.value.copy(
+			this.camera.projectionMatrixInverse
+		);
+		this.fullscreenMaterial.uniforms.curCameraMatrixWorld.value.copy(
+			this.camera.matrixWorld
+		);
+		this.fullscreenMaterial.uniforms.cameraNear.value = this.camera.near;
+		this.fullscreenMaterial.uniforms.cameraFar.value = this.camera.far;
+
+		// now render this fullscreen pass
+		renderer.setRenderTarget( this.renderTarget );
+		this.fsQuad.render( renderer );
+
+		// save all buffers for use in the next frame
+		renderer.copyFramebufferToTexture( zeroVec2, this.accumulatedSamplesTexture );
+
+		renderer.setRenderTarget( this.depthRenderTarget );
+		renderer.copyFramebufferToTexture( zeroVec2, this.lastDepthTexture );
+
+		this.fullscreenMaterial.uniforms.prevInverseProjectionMatrix.value.copy(
+			this.camera.projectionMatrixInverse
+		);
+		this.fullscreenMaterial.uniforms.prevCameraMatrixWorld.value.copy(
+			this.camera.matrixWorld
+		);
+
+		renderer.setRenderTarget( null );
+
+	}
+
+}
diff --git a/src/temporal-resolve/passes/VelocityPass.js b/src/temporal-resolve/passes/VelocityPass.js
new file mode 100644
index 000000000..dbd3b0e25
--- /dev/null
+++ b/src/temporal-resolve/passes/VelocityPass.js
@@ -0,0 +1,115 @@
+import {
+	HalfFloatType,
+	NearestFilter,
+	ShaderMaterial,
+	UniformsUtils,
+	WebGLRenderTarget
+} from 'three';
+import { VelocityShader } from '../materials/VelocityShader';
+
+export class VelocityPass {
+
+	constructor( scene, camera ) {
+
+		this.scene = scene;
+		this.camera = camera;
+
+		this.cachedMaterials = new WeakMap();
+
+		this.renderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: NearestFilter,
+				magFilter: NearestFilter,
+				type: HalfFloatType,
+			}
+		);
+
+	}
+
+	setVelocityMaterialInScene() {
+
+		this.scene.traverse( ( c ) => {
+
+			if ( c.material ) {
+
+				const originalMaterial = c.material;
+
+				// eslint-disable-next-line prefer-const
+				let [ cachedOriginalMaterial, velocityMaterial ] =
+					this.cachedMaterials.get( c ) || [];
+
+				if (
+					! this.cachedMaterials.has( c ) ||
+					originalMaterial !== cachedOriginalMaterial
+				) {
+
+					velocityMaterial = new ShaderMaterial( {
+						uniforms: UniformsUtils.clone( VelocityShader.uniforms ),
+						vertexShader: VelocityShader.vertexShader,
+						fragmentShader: VelocityShader.fragmentShader,
+					} );
+
+					this.cachedMaterials.set( c, [ originalMaterial, velocityMaterial ] );
+
+				}
+
+				velocityMaterial.uniforms.velocityMatrix.value.multiplyMatrices(
+					this.camera.projectionMatrix,
+					c.modelViewMatrix
+				);
+
+				c.material = velocityMaterial;
+
+			}
+
+		} );
+
+	}
+
+	unsetVelocityMaterialInScene() {
+
+		this.scene.traverse( ( c ) => {
+
+			if ( c.material ) {
+
+				c.material.uniforms.prevVelocityMatrix.value.copy(
+					c.material.uniforms.velocityMatrix.value
+				);
+
+				const [ originalMaterial ] = this.cachedMaterials.get( c );
+
+				c.material = originalMaterial;
+
+			}
+
+		} );
+
+	}
+
+	dispose() {
+
+		this.renderTarget.dispose();
+
+	}
+
+	setSize( width, height ) {
+
+		this.renderTarget.setSize( width, height );
+
+	}
+
+	render( renderer ) {
+
+		this.setVelocityMaterialInScene();
+
+		renderer.setRenderTarget( this.renderTarget );
+		renderer.clear();
+		renderer.render( this.scene, this.camera );
+
+		this.unsetVelocityMaterialInScene();
+
+	}
+
+}
From 306771b9c79acc64c6fd476be36d58da25315bb9 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 16:12:02 +0200
Subject: [PATCH 02/25] Update readme
---
 README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
diff --git a/README.md b/README.md
index 50b8cdff4..698780c87 100644
--- a/README.md
+++ b/README.md
@@ -382,6 +382,56 @@ _extends THREE.Camera_
 
 A class indicating that the path tracer should render an equirectangular view. Does not work with three.js raster rendering.
 
+## TemporalResolve
+
+A class that implements temporal filtering to preserve samples when the camera is moving. This helps reduce noise on camera movement by reprojecting the last frame's samples into the current one.
+
+### .temporalResolveMix
+
+```javascript
+temporalResolveMix = 0.75 : Number
+```
+
+How much the last frame should be blended into the current one. Higher values will result in a less noisy look at the cost of more smearing.
+
+### .clampRing
+
+```javascript
+clampRing = 1 : Number
+```
+
+An integer to set the radius of pixels to be used for neighborhood clamping. Higher values will result in a less noisy look at the cost of more blurring.
+
+### .newSamplesSmoothing
+
+```javascript
+newSamplesSmoothing = 0.5 : Number
+```
+
+To reduce noise for pixels that appeared recently, the average of multiple adjacent pixels can be used instead of a pixel itself. This factor determines the influence of the averaged pixel. Higher values will result in less noise but also less sharpness.
+
+### .newSamplesCorrection
+
+```javascript
+newSamplesCorrection = 0.75 : Number
+```
+
+Higher values will make pixels that appeared recently have a greater influence on the output. This will result in more noise but less smearing.
+
+### .constructor
+
+```javascript
+constructor( ptRenderer : PathTracingRenderer, scene : Object3D, camera : Camera )
+```
+
+### .update
+
+```javascript
+update() : void
+```
+
+Updates the temporal resolve pass for the current frame.
+
 ## PhysicalSpotLight
 
 _extends THREE.SpotLight_
From 2ce768894d2eada6582f2e00c4d7e7a20c75eeea Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 16:43:34 +0200
Subject: [PATCH 03/25] Rename clampRing to clampRadius
---
 README.md                                     |  84 +-
 example/index.js                              | 853 ++++++++----------
 example/materialBall.js                       | 713 +++++++--------
 src/temporal-resolve/TemporalResolve.js       |  70 +-
 .../materials/TemporalResolveMaterial.js      |  18 +-
 .../shaders/temporalResolveFragment.js        |   4 +-
 6 files changed, 792 insertions(+), 950 deletions(-)
diff --git a/README.md b/README.md
index 698780c87..f4efe4241 100644
--- a/README.md
+++ b/README.md
@@ -45,14 +45,13 @@ _More features and capabilities in progress!_
 
 [Ambient Occlusion Material](https://gkjohnson.github.io/three-gpu-pathtracer/example/bundle/aoRender.html)
 
-
 ## Running examples locally
 
-To run and modify the examples locally, make sure you have Node and NPM installed.  Check the supported versions in [the test configuration](./.github/workflows/node.js.yml).
+To run and modify the examples locally, make sure you have Node and NPM installed. Check the supported versions in [the test configuration](./.github/workflows/node.js.yml).
 
 In order to install dependencies, you will need `make` and a C++ compiler available.
 
-On Debian or Ubuntu, run `sudo apt install build-essential`.  It should just work on MacOS.
+On Debian or Ubuntu, run `sudo apt install build-essential`. It should just work on MacOS.
 
 - To install dependencies, run `npm install`
 - To start the demos run `npm start`
@@ -63,13 +62,13 @@ On Debian or Ubuntu, run `sudo apt install build-essential`.  It should just wor
 **Basic Renderer**
 
 ```js
-import * as THREE from 'three';
-import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
+import * as THREE from "three";
+import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
 import {
 	PathTracingSceneGenerator,
 	PathTracingRenderer,
 	PhysicalPathTracingMaterial,
-} from 'three-gpu-pathtracer';
+} from "three-gpu-pathtracer";
 
 // init scene, renderer, camera, controls, etc
 
@@ -78,8 +77,8 @@ renderer.toneMapping = THREE.ACESFilmicToneMapping;
 
 // initialize the path tracing material and renderer
 const ptMaterial = new PhysicalPathTracingMaterial();
-const ptRenderer = new PathTracingRenderer( renderer );
-ptRenderer.setSize( window.innerWidth, window.innerHeight );
+const ptRenderer = new PathTracingRenderer(renderer);
+ptRenderer.setSize(window.innerWidth, window.innerHeight);
 ptRenderer.camera = camera;
 ptRenderer.material = ptMaterial;
 
@@ -87,45 +86,46 @@ ptRenderer.material = ptMaterial;
 ptRenderer.alpha = true;
 
 // init quad for rendering to the canvas
-const fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
-	map: ptRenderer.target.texture,
+const fsQuad = new FullScreenQuad(
+	new THREE.MeshBasicMaterial({
+		map: ptRenderer.target.texture,
 
-	// if rendering transparent background
-	blending: THREE.CustomBlending,
-} ) );
+		// if rendering transparent background
+		blending: THREE.CustomBlending,
+	})
+);
 
 // ensure scene matrices are up to date
 scene.updateMatrixWorld();
 
 // initialize the scene and update the material properties with the bvh, materials, etc
 const generator = new PathTracingSceneGenerator();
-const { bvh, textures, materials, lights } = generator.generate( scene );
+const { bvh, textures, materials, lights } = generator.generate(scene);
 
 // update bvh and geometry attribute textures
-ptMaterial.bvh.updateFrom( bvh );
-ptMaterial.normalAttribute.updateFrom( geometry.attributes.normal );
-ptMaterial.tangentAttribute.updateFrom( geometry.attributes.tangent );
-ptMaterial.uvAttribute.updateFrom( geometry.attributes.uv );
+ptMaterial.bvh.updateFrom(bvh);
+ptMaterial.normalAttribute.updateFrom(geometry.attributes.normal);
+ptMaterial.tangentAttribute.updateFrom(geometry.attributes.tangent);
+ptMaterial.uvAttribute.updateFrom(geometry.attributes.uv);
 
 // update materials and texture arrays
-ptMaterial.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
-ptMaterial.textures.setTextures( renderer, 2048, 2048, textures );
-ptMaterial.materials.updateFrom( materials, textures );
+ptMaterial.materialIndexAttribute.updateFrom(geometry.attributes.materialIndex);
+ptMaterial.textures.setTextures(renderer, 2048, 2048, textures);
+ptMaterial.materials.updateFrom(materials, textures);
 
 // update the lights
-ptMaterial.lights.updateFrom( lights );
+ptMaterial.lights.updateFrom(lights);
 ptMaterial.lightCount = lights.length;
 
 // set the environment map
-const texture = await new RGBELoader().loadAsync( envMapUrl );
-ptRenderer.material.envMapInfo.updateFrom( texture );
+const texture = await new RGBELoader().loadAsync(envMapUrl);
+ptRenderer.material.envMapInfo.updateFrom(texture);
 
 animate();
 
 // ...
 
 function animate() {
-
 	// if the camera position changes call "ptRenderer.reset()"
 
 	// update the camera and render one sample
@@ -137,8 +137,7 @@ function animate() {
 	fsQuad.material.map = ptRenderer.target.texture;
 
 	// copy the current state of the path tracer to canvas to display
-	fsQuad.render( renderer );
-
+	fsQuad.render(renderer);
 }
 ```
 
@@ -147,16 +146,15 @@ function animate() {
 Using a pre blurred envioronment map can help improve frame convergence time at the cost of sharp environment reflections. If performance is concern then multiple importance sampling can be disabled and blurred environment map used.
 
 ```js
-import { BlurredEnvMapGenerator } from 'three-gpu-pathtracer';
+import { BlurredEnvMapGenerator } from "three-gpu-pathtracer";
 
 // ...
 
-const envMap = await new RGBELoader().loadAsync( envMapUrl );
-const generator = new BlurredEnvMapGenerator( renderer );
-const blurredEnvMap = generator.generate( envMap, 0.35 );
+const envMap = await new RGBELoader().loadAsync(envMapUrl);
+const generator = new BlurredEnvMapGenerator(renderer);
+const blurredEnvMap = generator.generate(envMap, 0.35);
 
 // render!
-
 ```
 
 ## Dynamic Scenes
@@ -164,12 +162,12 @@ const blurredEnvMap = generator.generate( envMap, 0.35 );
 Using the dynamic scene generator the same, frequently updated scene can be converted into a single reusable geometry multiple times and BVH refit which greatly improves subsequent scene updates. See `DynamicPathTracingSceneGenerator` docs for more info.
 
 ```js
-import { DynamicPathTracingSceneGenerator } from 'three-gpu-pathtracer';
+import { DynamicPathTracingSceneGenerator } from "three-gpu-pathtracer";
 
 // ... initialize scene etc
 
-const generator = new DynamicPathTracingSceneGenerator( scene );
-const { bvh, textures, materials } = generator.generate( scene );
+const generator = new DynamicPathTracingSceneGenerator(scene);
+const { bvh, textures, materials } = generator.generate(scene);
 
 // ... update path tracer and render
 ```
@@ -179,13 +177,13 @@ const { bvh, textures, materials } = generator.generate( scene );
 _NOTE WebWorker syntax is inconsistently supported across bundlers and sometimes not supported at all so the PathTracingSceneWorker class is not exported from the package root. If needed the code from src/worker can be copied and modified to accomodate a particular build process._
 
 ```js
-import { PathTracingSceneWorker } from 'three-gpu-pathtracer/src/workers/PathTracingSceneWorker.js';
+import { PathTracingSceneWorker } from "three-gpu-pathtracer/src/workers/PathTracingSceneWorker.js";
 
 // ...
 
 // initialize the scene and update the material properties with the bvh, materials, etc
 const generator = new PathTracingSceneWorker();
-const { bvh, textures, materials, lights } = await generator.generate( scene );
+const { bvh, textures, materials, lights } = await generator.generate(scene);
 
 // ...
 ```
@@ -269,7 +267,7 @@ Sets the size of the target to render to.
 ### .update
 
 ```js
-update()
+update();
 ```
 
 Renders a single sample to the target.
@@ -347,7 +345,7 @@ The fstop value of the camera. If this is changed then the `bokehSize` field is
 ### .bokehSize
 
 ```js
-bokehSize : Number
+bokehSize: Number;
 ```
 
 The bokeh size as derived from the fStop and focal length in millimeters. If this is set then the fStop is implicitly updated.
@@ -394,10 +392,10 @@ temporalResolveMix = 0.75 : Number
 
 How much the last frame should be blended into the current one. Higher values will result in a less noisy look at the cost of more smearing.
 
-### .clampRing
+### .clampRadius
 
 ```javascript
-clampRing = 1 : Number
+clampRadius = 1 : Number
 ```
 
 An integer to set the radius of pixels to be used for neighborhood clamping. Higher values will result in a less noisy look at the cost of more blurring.
@@ -830,11 +828,13 @@ Set of randomness and other light transport utilities for use in a shader. See t
 
 
 
+
 
 Botanists Study model by riikkakilpelainen
 
 
 
+
 
 Japanese Bridge Garden model by kristenlee
 
@@ -846,5 +846,3 @@ Set of randomness and other light transport utilities for use in a shader. See t
 [PBR Book](https://pbr-book.org/)
 
 [knightcrawler25/GLSL-PathTracer](https://github.com/knightcrawler25/GLSL-PathTracer/)
-
-
diff --git a/example/index.js b/example/index.js
index a6ad7a490..90d4a132c 100644
--- a/example/index.js
+++ b/example/index.js
@@ -18,53 +18,65 @@ import {
 	MeshBasicMaterial,
 	sRGBEncoding,
 	CustomBlending,
-	Matrix4
-} from 'three';
-import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
-import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
-import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
-import { LDrawLoader } from 'three/examples/jsm/loaders/LDrawLoader.js';
-import { LDrawUtils } from 'three/examples/jsm/utils/LDrawUtils.js';
-import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
-import Stats from 'three/examples/jsm/libs/stats.module.js';
-import { generateRadialFloorTexture } from './utils/generateRadialFloorTexture.js';
-import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js';
-import { PhysicalPathTracingMaterial, PathTracingRenderer, MaterialReducer, BlurredEnvMapGenerator } from '../src/index.js';
-import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
-import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
+	Matrix4,
+} from "three";
+import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
+import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import { LDrawLoader } from "three/examples/jsm/loaders/LDrawLoader.js";
+import { LDrawUtils } from "three/examples/jsm/utils/LDrawUtils.js";
+import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
+import Stats from "three/examples/jsm/libs/stats.module.js";
+import { generateRadialFloorTexture } from "./utils/generateRadialFloorTexture.js";
+import { PathTracingSceneWorker } from "../src/workers/PathTracingSceneWorker.js";
+import {
+	PhysicalPathTracingMaterial,
+	PathTracingRenderer,
+	MaterialReducer,
+	BlurredEnvMapGenerator,
+} from "../src/index.js";
+import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import { TemporalResolve } from "../src/temporal-resolve/TemporalResolve.js";
 
 const envMaps = {
-	'Royal Esplanade': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr',
-	'Moonless Golf': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/moonless_golf_1k.hdr',
-	'Overpass': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/pedestrian_overpass_1k.hdr',
-	'Venice Sunset': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/venice_sunset_1k.hdr',
-	'Small Studio': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/studio_small_05_1k.hdr',
-	'Pfalzer Forest': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/phalzer_forest_01_1k.hdr',
-	'Leadenhall Market': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/leadenhall_market_1k.hdr',
-	'Kloppenheim': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/kloppenheim_05_1k.hdr',
-	'Hilly Terrain': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/hilly_terrain_01_1k.hdr',
-	'Circus Arena': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/circus_arena_1k.hdr',
-	'Chinese Garden': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/chinese_garden_1k.hdr',
-	'Autoshop': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/autoshop_01_1k.hdr',
+	"Royal Esplanade":
+		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr",
+	"Moonless Golf":
+		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/moonless_golf_1k.hdr",
+	Overpass:
+		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/pedestrian_overpass_1k.hdr",
+	"Venice Sunset":
+		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/venice_sunset_1k.hdr",
+	"Small Studio":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/studio_small_05_1k.hdr",
+	"Pfalzer Forest":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/phalzer_forest_01_1k.hdr",
+	"Leadenhall Market":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/leadenhall_market_1k.hdr",
+	Kloppenheim:
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/kloppenheim_05_1k.hdr",
+	"Hilly Terrain":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/hilly_terrain_01_1k.hdr",
+	"Circus Arena":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/circus_arena_1k.hdr",
+	"Chinese Garden":
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/chinese_garden_1k.hdr",
+	Autoshop:
+		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/autoshop_01_1k.hdr",
 };
 
 const models = window.MODEL_LIST || {};
 
-let initialModel = Object.keys( models )[ 0 ];
-if ( window.location.hash ) {
-
-	const modelName = window.location.hash.substring( 1 ).replaceAll( '%20', ' ' );
-	if ( modelName in models ) {
-
+let initialModel = Object.keys(models)[0];
+if (window.location.hash) {
+	const modelName = window.location.hash.substring(1).replaceAll("%20", " ");
+	if (modelName in models) {
 		initialModel = modelName;
-
 	}
-
 }
 
 const params = {
-
 	multipleImportanceSampling: true,
 	acesToneMapping: true,
 	resolutionScale: 1 / window.devicePixelRatio,
@@ -74,26 +86,26 @@ const params = {
 
 	temporalResolve: true,
 	temporalResolveMix: 0.75,
-	clampRing: 1,
+	clampRadius: 1,
 	newSamplesSmoothing: 0.5,
 	newSamplesCorrection: 0.75,
 
 	model: initialModel,
 
-	envMap: envMaps[ 'Royal Esplanade' ],
+	envMap: envMaps["Royal Esplanade"],
 
-	gradientTop: '#bfd8ff',
-	gradientBottom: '#ffffff',
+	gradientTop: "#bfd8ff",
+	gradientBottom: "#ffffff",
 
 	environmentIntensity: 3.0,
 	environmentBlur: 0.0,
 	environmentRotation: 0,
 
-	cameraProjection: 'Perspective',
+	cameraProjection: "Perspective",
 
-	backgroundType: 'Gradient',
-	bgGradientTop: '#111111',
-	bgGradientBottom: '#000000',
+	backgroundType: "Gradient",
+	bgGradientTop: "#111111",
+	bgGradientBottom: "#000000",
 	backgroundAlpha: 1.0,
 	checkerboardTransparency: true,
 
@@ -101,11 +113,10 @@ const params = {
 	bounces: 3,
 	pause: false,
 
-	floorColor: '#080808',
+	floorColor: "#080808",
 	floorOpacity: 1.0,
 	floorRoughness: 0.1,
-	floorMetalness: 0.0
-
+	floorMetalness: 0.0,
 };
 
 let creditEl, loadingEl, samplesEl;
@@ -121,176 +132,164 @@ const orthoWidth = 2;
 init();
 
 async function init() {
+	creditEl = document.getElementById("credits");
+	loadingEl = document.getElementById("loading");
+	samplesEl = document.getElementById("samples");
 
-	creditEl = document.getElementById( 'credits' );
-	loadingEl = document.getElementById( 'loading' );
-	samplesEl = document.getElementById( 'samples' );
-
-	renderer = new WebGLRenderer( { antialias: true } );
+	renderer = new WebGLRenderer({ antialias: true });
 	renderer.outputEncoding = sRGBEncoding;
 	renderer.toneMapping = ACESFilmicToneMapping;
-	document.body.appendChild( renderer.domElement );
+	document.body.appendChild(renderer.domElement);
 
 	scene = new Scene();
 
 	const aspect = window.innerWidth / window.innerHeight;
-	perspectiveCamera = new PerspectiveCamera( 60, aspect, 0.025, 500 );
-	perspectiveCamera.position.set( - 1, 0.25, 1 );
+	perspectiveCamera = new PerspectiveCamera(60, aspect, 0.025, 500);
+	perspectiveCamera.position.set(-1, 0.25, 1);
 
 	const orthoHeight = orthoWidth / aspect;
-	orthoCamera = new OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
-	orthoCamera.position.set( - 1, 0.25, 1 );
+	orthoCamera = new OrthographicCamera(
+		orthoWidth / -2,
+		orthoWidth / 2,
+		orthoHeight / 2,
+		orthoHeight / -2,
+		0,
+		100
+	);
+	orthoCamera.position.set(-1, 0.25, 1);
 
-	ptRenderer = new PathTracingRenderer( renderer );
+	ptRenderer = new PathTracingRenderer(renderer);
 	ptRenderer.alpha = true;
 	ptRenderer.material = new PhysicalPathTracingMaterial();
-	ptRenderer.tiles.set( params.tiles, params.tiles );
-	ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', 1 );
-	ptRenderer.material.setDefine( 'FEATURE_MIS', Number( params.multipleImportanceSampling ) );
-	ptRenderer.material.bgGradientTop.set( params.bgGradientTop );
-	ptRenderer.material.bgGradientBottom.set( params.bgGradientBottom );
+	ptRenderer.tiles.set(params.tiles, params.tiles);
+	ptRenderer.material.setDefine("FEATURE_GRADIENT_BG", 1);
+	ptRenderer.material.setDefine(
+		"FEATURE_MIS",
+		Number(params.multipleImportanceSampling)
+	);
+	ptRenderer.material.bgGradientTop.set(params.bgGradientTop);
+	ptRenderer.material.bgGradientBottom.set(params.bgGradientBottom);
 
-	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
+	temporalResolve = new TemporalResolve(ptRenderer, scene, activeCamera);
 	temporalResolve.temporalResolveMix = 0.75;
-	temporalResolve.clampRing = 1;
+	temporalResolve.clampRadius = 1;
 	temporalResolve.newSamplesSmoothing = 0.5;
 	temporalResolve.newSamplesCorrection = 0.75;
 
-	fsQuad = new FullScreenQuad( new MeshBasicMaterial( {
-		map: ptRenderer.target.texture,
-		blending: CustomBlending
-	} ) );
+	fsQuad = new FullScreenQuad(
+		new MeshBasicMaterial({
+			map: ptRenderer.target.texture,
+			blending: CustomBlending,
+		})
+	);
 
-	controls = new OrbitControls( perspectiveCamera, renderer.domElement );
-	controls.addEventListener( 'change', resetRenderer );
+	controls = new OrbitControls(perspectiveCamera, renderer.domElement);
+	controls.addEventListener("change", resetRenderer);
 
-	envMapGenerator = new BlurredEnvMapGenerator( renderer );
+	envMapGenerator = new BlurredEnvMapGenerator(renderer);
 
-	const floorTex = generateRadialFloorTexture( 2048 );
+	const floorTex = generateRadialFloorTexture(2048);
 	floorPlane = new Mesh(
 		new PlaneBufferGeometry(),
-		new MeshStandardMaterial( {
+		new MeshStandardMaterial({
 			map: floorTex,
 			transparent: true,
 			color: 0x080808,
 			roughness: 0.1,
-			metalness: 0.0
-		} )
+			metalness: 0.0,
+		})
 	);
-	floorPlane.scale.setScalar( 3 );
-	floorPlane.rotation.x = - Math.PI / 2;
+	floorPlane.scale.setScalar(3);
+	floorPlane.rotation.x = -Math.PI / 2;
 
 	stats = new Stats();
-	document.body.appendChild( stats.dom );
+	document.body.appendChild(stats.dom);
 	renderer.physicallyCorrectLights = true;
 	renderer.toneMapping = ACESFilmicToneMapping;
-	ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', 1 );
-	scene.background = new Color( 0x060606 );
-	ptRenderer.tiles.set( params.tilesX, params.tilesY );
+	ptRenderer.material.setDefine("FEATURE_GRADIENT_BG", 1);
+	scene.background = new Color(0x060606);
+	ptRenderer.tiles.set(params.tilesX, params.tilesY);
 
-	updateCamera( params.cameraProjection );
+	updateCamera(params.cameraProjection);
 	updateModel();
 	updateEnvMap();
 	onResize();
 
 	animate();
 
-	window.addEventListener( 'resize', onResize );
-
+	window.addEventListener("resize", onResize);
 }
 
 function animate() {
-
-	requestAnimationFrame( animate );
+	requestAnimationFrame(animate);
 
 	stats.update();
 
-	if ( loadingModel ) {
-
+	if (loadingModel) {
 		return;
-
 	}
 
-	if ( ptRenderer.samples < 1.0 || ! params.enable ) {
-
-		renderer.render( scene, activeCamera );
-
+	if (ptRenderer.samples < 1.0 || !params.enable) {
+		renderer.render(scene, activeCamera);
 	}
 
-	if ( params.enable && delaySamples === 0 ) {
-
-		const samples = Math.floor( ptRenderer.samples );
-		samplesEl.innerText = `samples: ${ samples }`;
+	if (params.enable && delaySamples === 0) {
+		const samples = Math.floor(ptRenderer.samples);
+		samplesEl.innerText = `samples: ${samples}`;
 
-		ptRenderer.material.materials.updateFrom( sceneInfo.materials, sceneInfo.textures );
+		ptRenderer.material.materials.updateFrom(
+			sceneInfo.materials,
+			sceneInfo.textures
+		);
 		ptRenderer.material.filterGlossyFactor = 0.5;
 		ptRenderer.material.environmentIntensity = params.environmentIntensity;
 		ptRenderer.material.bounces = params.bounces;
-		ptRenderer.material.physicalCamera.updateFrom( activeCamera );
+		ptRenderer.material.physicalCamera.updateFrom(activeCamera);
 
 		activeCamera.updateMatrixWorld();
 
-
-
-		if ( ! params.pause || ptRenderer.samples < 1 ) {
-
-			for ( let i = 0, l = params.samplesPerFrame; i < l; i ++ ) {
-
+		if (!params.pause || ptRenderer.samples < 1) {
+			for (let i = 0, l = params.samplesPerFrame; i < l; i++) {
 				ptRenderer.update();
-
 			}
-
 		}
 
 		renderer.autoClear = false;
-		if ( params.temporalResolve ) {
-
+		if (params.temporalResolve) {
 			temporalResolve.update();
 			fsQuad.material.map = temporalResolve.target.texture;
-
 		} else {
-
 			fsQuad.material.map = ptRenderer.target.texture;
-
 		}
 
-		fsQuad.render( renderer );
+		fsQuad.render(renderer);
 		renderer.autoClear = true;
-
-	} else if ( delaySamples > 0 ) {
-
-		delaySamples --;
-
+	} else if (delaySamples > 0) {
+		delaySamples--;
 	}
 
-	samplesEl.innerText = `Samples: ${ Math.floor( ptRenderer.samples ) }`;
-
+	samplesEl.innerText = `Samples: ${Math.floor(ptRenderer.samples)}`;
 }
 
 function resetRenderer() {
-
-	if ( params.tilesX * params.tilesY !== 1.0 ) {
-
+	if (params.tilesX * params.tilesY !== 1.0) {
 		delaySamples = 1;
-
 	}
 
 	ptRenderer.reset();
-
 }
 
 function onResize() {
-
 	const w = window.innerWidth;
 	const h = window.innerHeight;
 	const scale = params.resolutionScale;
 	const dpr = window.devicePixelRatio;
 
-	ptRenderer.setSize( w * scale * dpr, h * scale * dpr );
+	ptRenderer.setSize(w * scale * dpr, h * scale * dpr);
 	ptRenderer.reset();
 
-	renderer.setSize( w, h );
-	renderer.setPixelRatio( window.devicePixelRatio * scale );
+	renderer.setSize(w, h);
+	renderer.setPixelRatio(window.devicePixelRatio * scale);
 
 	const aspect = w / h;
 	perspectiveCamera.aspect = aspect;
@@ -298,230 +297,188 @@ function onResize() {
 
 	const orthoHeight = orthoWidth / aspect;
 	orthoCamera.top = orthoHeight / 2;
-	orthoCamera.bottom = orthoHeight / - 2;
+	orthoCamera.bottom = orthoHeight / -2;
 	orthoCamera.updateProjectionMatrix();
-
 }
 
 function buildGui() {
-
-	if ( gui ) {
-
+	if (gui) {
 		gui.destroy();
-
 	}
 
 	gui = new GUI();
 
-	gui.add( params, 'model', Object.keys( models ) ).onChange( updateModel );
+	gui.add(params, "model", Object.keys(models)).onChange(updateModel);
 
-	const pathTracingFolder = gui.addFolder( 'path tracing' );
-	pathTracingFolder.add( params, 'enable' );
-	pathTracingFolder.add( params, 'pause' );
-	pathTracingFolder.add( params, 'multipleImportanceSampling' ).onChange( v => {
-
-		ptRenderer.material.setDefine( 'FEATURE_MIS', Number( v ) );
+	const pathTracingFolder = gui.addFolder("path tracing");
+	pathTracingFolder.add(params, "enable");
+	pathTracingFolder.add(params, "pause");
+	pathTracingFolder.add(params, "multipleImportanceSampling").onChange((v) => {
+		ptRenderer.material.setDefine("FEATURE_MIS", Number(v));
 		ptRenderer.reset();
-
-	} );
-	pathTracingFolder.add( params, 'acesToneMapping' ).onChange( v => {
-
+	});
+	pathTracingFolder.add(params, "acesToneMapping").onChange((v) => {
 		renderer.toneMapping = v ? ACESFilmicToneMapping : NoToneMapping;
-
-	} );
-	pathTracingFolder.add( params, 'bounces', 1, 20, 1 ).onChange( () => {
-
+	});
+	pathTracingFolder.add(params, "bounces", 1, 20, 1).onChange(() => {
 		ptRenderer.reset();
+	});
 
-	} );
-
-	const trFolder = gui.addFolder( 'Temporal Resolve' );
-	trFolder.add( params, 'temporalResolve' );
+	const trFolder = gui.addFolder("Temporal Resolve");
+	trFolder.add(params, "temporalResolve");
 	trFolder
-		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
+		.add(params, "temporalResolveMix", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.temporalResolveMix = value));
 	trFolder
-		.add( params, 'clampRing', 1, 8, 1 )
-		.onChange( ( value ) => ( temporalResolve.clampRing = value ) );
+		.add(params, "clampRadius", 1, 8, 1)
+		.onChange((value) => (temporalResolve.clampRadius = value));
 	trFolder
-		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
+		.add(params, "newSamplesSmoothing", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.newSamplesSmoothing = value));
 	trFolder
-		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
-
-	const resolutionFolder = gui.addFolder( 'resolution' );
-	resolutionFolder.add( params, 'resolutionScale', 0.1, 1.0, 0.01 ).onChange( () => {
-
-		onResize();
-
-	} );
-	resolutionFolder.add( params, 'samplesPerFrame', 1, 10, 1 );
-	resolutionFolder.add( params, 'tilesX', 1, 10, 1 ).onChange( v => {
-
+		.add(params, "newSamplesCorrection", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.newSamplesCorrection = value));
+
+	const resolutionFolder = gui.addFolder("resolution");
+	resolutionFolder
+		.add(params, "resolutionScale", 0.1, 1.0, 0.01)
+		.onChange(() => {
+			onResize();
+		});
+	resolutionFolder.add(params, "samplesPerFrame", 1, 10, 1);
+	resolutionFolder.add(params, "tilesX", 1, 10, 1).onChange((v) => {
 		ptRenderer.tiles.x = v;
-
-	} );
-	resolutionFolder.add( params, 'tilesY', 1, 10, 1 ).onChange( v => {
-
+	});
+	resolutionFolder.add(params, "tilesY", 1, 10, 1).onChange((v) => {
 		ptRenderer.tiles.y = v;
-
-	} );
-	resolutionFolder.add( params, 'cameraProjection', [ 'Perspective', 'Orthographic' ] ).onChange( v => {
-
-		updateCamera( v );
-
-	} );
+	});
+	resolutionFolder
+		.add(params, "cameraProjection", ["Perspective", "Orthographic"])
+		.onChange((v) => {
+			updateCamera(v);
+		});
 	resolutionFolder.open();
 
-	const environmentFolder = gui.addFolder( 'environment' );
-	environmentFolder.add( params, 'envMap', envMaps ).name( 'map' ).onChange( updateEnvMap );
-	environmentFolder.add( params, 'environmentBlur', 0.0, 1.0 ).onChange( () => {
-
-		updateEnvBlur();
-		ptRenderer.reset();
-
-	} ).name( 'env map blur' );
-	environmentFolder.add( params, 'environmentIntensity', 0.0, 10.0 ).onChange( () => {
-
-		ptRenderer.reset();
-
-	} ).name( 'intensity' );
-	environmentFolder.add( params, 'environmentRotation', 0, 2 * Math.PI ).onChange( v => {
-
-		ptRenderer.material.environmentRotation.setFromMatrix4( new Matrix4().makeRotationY( v ) );
-		ptRenderer.reset();
-
-	} );
+	const environmentFolder = gui.addFolder("environment");
+	environmentFolder
+		.add(params, "envMap", envMaps)
+		.name("map")
+		.onChange(updateEnvMap);
+	environmentFolder
+		.add(params, "environmentBlur", 0.0, 1.0)
+		.onChange(() => {
+			updateEnvBlur();
+			ptRenderer.reset();
+		})
+		.name("env map blur");
+	environmentFolder
+		.add(params, "environmentIntensity", 0.0, 10.0)
+		.onChange(() => {
+			ptRenderer.reset();
+		})
+		.name("intensity");
+	environmentFolder
+		.add(params, "environmentRotation", 0, 2 * Math.PI)
+		.onChange((v) => {
+			ptRenderer.material.environmentRotation.setFromMatrix4(
+				new Matrix4().makeRotationY(v)
+			);
+			ptRenderer.reset();
+		});
 	environmentFolder.open();
 
-	const backgroundFolder = gui.addFolder( 'background' );
-	backgroundFolder.add( params, 'backgroundType', [ 'Environment', 'Gradient' ] ).onChange( v => {
-
-		ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', Number( v === 'Gradient' ) );
-		if ( v === 'Gradient' ) {
-
-			scene.background = new Color( 0x060606 );
-
-		} else {
-
-			scene.background = scene.environment;
-
-		}
-
-		ptRenderer.reset();
-
-	} );
-	backgroundFolder.addColor( params, 'bgGradientTop' ).onChange( v => {
+	const backgroundFolder = gui.addFolder("background");
+	backgroundFolder
+		.add(params, "backgroundType", ["Environment", "Gradient"])
+		.onChange((v) => {
+			ptRenderer.material.setDefine(
+				"FEATURE_GRADIENT_BG",
+				Number(v === "Gradient")
+			);
+			if (v === "Gradient") {
+				scene.background = new Color(0x060606);
+			} else {
+				scene.background = scene.environment;
+			}
 
-		ptRenderer.material.bgGradientTop.set( v );
+			ptRenderer.reset();
+		});
+	backgroundFolder.addColor(params, "bgGradientTop").onChange((v) => {
+		ptRenderer.material.bgGradientTop.set(v);
 		ptRenderer.reset();
-
-	} );
-	backgroundFolder.addColor( params, 'bgGradientBottom' ).onChange( v => {
-
-		ptRenderer.material.bgGradientBottom.set( v );
+	});
+	backgroundFolder.addColor(params, "bgGradientBottom").onChange((v) => {
+		ptRenderer.material.bgGradientBottom.set(v);
 		ptRenderer.reset();
-
-	} );
-	backgroundFolder.add( params, 'backgroundAlpha', 0, 1 ).onChange( v => {
-
+	});
+	backgroundFolder.add(params, "backgroundAlpha", 0, 1).onChange((v) => {
 		ptRenderer.material.backgroundAlpha = v;
 		ptRenderer.reset();
-
-	} );
-	backgroundFolder.add( params, 'checkerboardTransparency' ).onChange( v => {
-
-		if ( v ) document.body.classList.add( 'checkerboard' );
-		else document.body.classList.remove( 'checkerboard' );
-
-	} );
-
-	const floorFolder = gui.addFolder( 'floor' );
-	floorFolder.addColor( params, 'floorColor' ).onChange( v => {
-
-		floorPlane.material.color.set( v );
+	});
+	backgroundFolder.add(params, "checkerboardTransparency").onChange((v) => {
+		if (v) document.body.classList.add("checkerboard");
+		else document.body.classList.remove("checkerboard");
+	});
+
+	const floorFolder = gui.addFolder("floor");
+	floorFolder.addColor(params, "floorColor").onChange((v) => {
+		floorPlane.material.color.set(v);
 		ptRenderer.reset();
-
-	} );
-	floorFolder.add( params, 'floorRoughness', 0, 1 ).onChange( v => {
-
+	});
+	floorFolder.add(params, "floorRoughness", 0, 1).onChange((v) => {
 		floorPlane.material.roughness = v;
 		ptRenderer.reset();
-
-	} );
-	floorFolder.add( params, 'floorMetalness', 0, 1 ).onChange( v => {
-
+	});
+	floorFolder.add(params, "floorMetalness", 0, 1).onChange((v) => {
 		floorPlane.material.metalness = v;
 		ptRenderer.reset();
-
-	} );
-	floorFolder.add( params, 'floorOpacity', 0, 1 ).onChange( v => {
-
+	});
+	floorFolder.add(params, "floorOpacity", 0, 1).onChange((v) => {
 		floorPlane.material.opacity = v;
 		ptRenderer.reset();
-
-	} );
+	});
 	floorFolder.close();
-
 }
 
 function updateEnvMap() {
+	new RGBELoader().load(params.envMap, (texture) => {
+		if (scene.environmentMap) {
+			scene.environment.dispose();
+			envMap.dispose();
+		}
 
-	new RGBELoader()
-		.load( params.envMap, texture => {
-
-			if ( scene.environmentMap ) {
-
-				scene.environment.dispose();
-				envMap.dispose();
-
-			}
-
-			envMap = texture;
-			updateEnvBlur();
-			ptRenderer.reset();
-
-		} );
-
+		envMap = texture;
+		updateEnvBlur();
+		ptRenderer.reset();
+	});
 }
 
 function updateEnvBlur() {
-
-	const blurredEnvMap = envMapGenerator.generate( envMap, params.environmentBlur );
-	ptRenderer.material.envMapInfo.updateFrom( blurredEnvMap );
+	const blurredEnvMap = envMapGenerator.generate(
+		envMap,
+		params.environmentBlur
+	);
+	ptRenderer.material.envMapInfo.updateFrom(blurredEnvMap);
 
 	scene.environment = blurredEnvMap;
-	if ( params.backgroundType !== 'Gradient' ) {
-
+	if (params.backgroundType !== "Gradient") {
 		scene.background = blurredEnvMap;
-
 	}
-
 }
 
-function updateCamera( cameraProjection ) {
-
-	if ( cameraProjection === 'Perspective' ) {
-
-		if ( activeCamera ) {
-
-			perspectiveCamera.position.copy( activeCamera.position );
-
+function updateCamera(cameraProjection) {
+	if (cameraProjection === "Perspective") {
+		if (activeCamera) {
+			perspectiveCamera.position.copy(activeCamera.position);
 		}
 
 		activeCamera = perspectiveCamera;
-
 	} else {
-
-		if ( activeCamera ) {
-
-			orthoCamera.position.copy( activeCamera.position );
-
+		if (activeCamera) {
+			orthoCamera.position.copy(activeCamera.position);
 		}
 
 		activeCamera = orthoCamera;
-
 	}
 
 	controls.object = activeCamera;
@@ -530,284 +487,212 @@ function updateCamera( cameraProjection ) {
 	controls.update();
 
 	resetRenderer();
-
 }
 
-function convertOpacityToTransmission( model ) {
-
-	model.traverse( c => {
-
-		if ( c.material ) {
-
+function convertOpacityToTransmission(model) {
+	model.traverse((c) => {
+		if (c.material) {
 			const material = c.material;
-			if ( material.opacity < 0.65 && material.opacity > 0.2 ) {
-
+			if (material.opacity < 0.65 && material.opacity > 0.2) {
 				const newMaterial = new MeshPhysicalMaterial();
-				for ( const key in material ) {
-
-					if ( key in material ) {
-
-						if ( material[ key ] === null ) {
-
+				for (const key in material) {
+					if (key in material) {
+						if (material[key] === null) {
 							continue;
-
 						}
 
-						if ( material[ key ].isTexture ) {
-
-							newMaterial[ key ] = material[ key ];
-
-						} else if ( material[ key ].copy && material[ key ].constructor === newMaterial[ key ].constructor ) {
-
-							newMaterial[ key ].copy( material[ key ] );
-
-						} else if ( ( typeof material[ key ] ) === 'number' ) {
-
-							newMaterial[ key ] = material[ key ];
-
+						if (material[key].isTexture) {
+							newMaterial[key] = material[key];
+						} else if (
+							material[key].copy &&
+							material[key].constructor === newMaterial[key].constructor
+						) {
+							newMaterial[key].copy(material[key]);
+						} else if (typeof material[key] === "number") {
+							newMaterial[key] = material[key];
 						}
-
 					}
-
 				}
 
 				newMaterial.opacity = 1.0;
 				newMaterial.transmission = 1.0;
 				c.material = newMaterial;
-
 			}
-
 		}
-
-	} );
-
+	});
 }
 
 async function updateModel() {
-
-	if ( gui ) {
-
-		document.body.classList.remove( 'checkerboard' );
+	if (gui) {
+		document.body.classList.remove("checkerboard");
 		gui.destroy();
 		gui = null;
-
 	}
 
 	let model;
 	const manager = new LoadingManager();
-	const modelInfo = models[ params.model ];
+	const modelInfo = models[params.model];
 
 	loadingModel = true;
-	renderer.domElement.style.visibility = 'hidden';
-	samplesEl.innerText = '--';
-	creditEl.innerText = '--';
-	loadingEl.innerText = 'Loading';
-	loadingEl.style.visibility = 'visible';
-
-	scene.traverse( c => {
-
-		if ( c.material ) {
-
+	renderer.domElement.style.visibility = "hidden";
+	samplesEl.innerText = "--";
+	creditEl.innerText = "--";
+	loadingEl.innerText = "Loading";
+	loadingEl.style.visibility = "visible";
+
+	scene.traverse((c) => {
+		if (c.material) {
 			const material = c.material;
-			for ( const key in material ) {
-
-				if ( material[ key ] && material[ key ].isTexture ) {
-
-					material[ key ].dispose();
-
+			for (const key in material) {
+				if (material[key] && material[key].isTexture) {
+					material[key].dispose();
 				}
-
 			}
-
 		}
+	});
 
-	} );
-
-	if ( sceneInfo ) {
-
-		scene.remove( sceneInfo.scene );
-
+	if (sceneInfo) {
+		scene.remove(sceneInfo.scene);
 	}
 
-
 	const onFinish = async () => {
-
-		if ( modelInfo.removeEmission ) {
-
-			model.traverse( c => {
-
-				if ( c.material ) {
-
+		if (modelInfo.removeEmission) {
+			model.traverse((c) => {
+				if (c.material) {
 					c.material.emissiveMap = null;
 					c.material.emissiveIntensity = 0;
-
 				}
-
-			} );
-
+			});
 		}
 
-		if ( modelInfo.opacityToTransmission ) {
-
-			convertOpacityToTransmission( model );
-
+		if (modelInfo.opacityToTransmission) {
+			convertOpacityToTransmission(model);
 		}
 
-		model.traverse( c => {
-
-			if ( c.material ) {
-
+		model.traverse((c) => {
+			if (c.material) {
 				c.material.side = DoubleSide;
-
 			}
+		});
 
-		} );
-
-		if ( modelInfo.postProcess ) {
-
-			modelInfo.postProcess( model );
-
+		if (modelInfo.postProcess) {
+			modelInfo.postProcess(model);
 		}
 
 		// rotate model after so it doesn't affect the bounding sphere scale
-		if ( modelInfo.rotation ) {
-
-			model.rotation.set( ...modelInfo.rotation );
-
+		if (modelInfo.rotation) {
+			model.rotation.set(...modelInfo.rotation);
 		}
 
 		// center the model
 		const box = new Box3();
-		box.setFromObject( model );
+		box.setFromObject(model);
 		model.position
-			.addScaledVector( box.min, - 0.5 )
-			.addScaledVector( box.max, - 0.5 );
+			.addScaledVector(box.min, -0.5)
+			.addScaledVector(box.max, -0.5);
 
 		const sphere = new Sphere();
-		box.getBoundingSphere( sphere );
+		box.getBoundingSphere(sphere);
 
-		model.scale.setScalar( 1 / sphere.radius );
-		model.position.multiplyScalar( 1 / sphere.radius );
+		model.scale.setScalar(1 / sphere.radius);
+		model.position.multiplyScalar(1 / sphere.radius);
 
-		box.setFromObject( model );
+		box.setFromObject(model);
 
 		model.updateMatrixWorld();
 
 		const group = new Group();
 		floorPlane.position.y = box.min.y;
-		group.add( model, floorPlane );
+		group.add(model, floorPlane);
 
 		const reducer = new MaterialReducer();
-		reducer.process( group );
+		reducer.process(group);
 
 		const generator = new PathTracingSceneWorker();
-		const result = await generator.generate( group, { onProgress: v => {
-
-			const percent = Math.floor( 100 * v );
-			loadingEl.innerText = `Building BVH : ${ percent }%`;
-
-		} } );
+		const result = await generator.generate(group, {
+			onProgress: (v) => {
+				const percent = Math.floor(100 * v);
+				loadingEl.innerText = `Building BVH : ${percent}%`;
+			},
+		});
 
 		sceneInfo = result;
-		scene.add( sceneInfo.scene );
+		scene.add(sceneInfo.scene);
 
 		const { bvh, textures, materials } = result;
 		const geometry = bvh.geometry;
 		const material = ptRenderer.material;
 
-		material.bvh.updateFrom( bvh );
-		material.normalAttribute.updateFrom( geometry.attributes.normal );
-		material.tangentAttribute.updateFrom( geometry.attributes.tangent );
-		material.uvAttribute.updateFrom( geometry.attributes.uv );
-		material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
-		material.textures.setTextures( renderer, 2048, 2048, textures );
-		material.materials.updateFrom( materials, textures );
+		material.bvh.updateFrom(bvh);
+		material.normalAttribute.updateFrom(geometry.attributes.normal);
+		material.tangentAttribute.updateFrom(geometry.attributes.tangent);
+		material.uvAttribute.updateFrom(geometry.attributes.uv);
+		material.materialIndexAttribute.updateFrom(
+			geometry.attributes.materialIndex
+		);
+		material.textures.setTextures(renderer, 2048, 2048, textures);
+		material.materials.updateFrom(materials, textures);
 
 		generator.dispose();
 
-		loadingEl.style.visibility = 'hidden';
+		loadingEl.style.visibility = "hidden";
 
-		creditEl.innerHTML = modelInfo.credit || '';
-		creditEl.style.visibility = modelInfo.credit ? 'visible' : 'hidden';
+		creditEl.innerHTML = modelInfo.credit || "";
+		creditEl.style.visibility = modelInfo.credit ? "visible" : "hidden";
 		params.bounces = modelInfo.bounces || 3;
 		buildGui();
 
 		loadingModel = false;
-		renderer.domElement.style.visibility = 'visible';
-		if ( params.checkerboardTransparency ) {
-
-			document.body.classList.add( 'checkerboard' );
-
+		renderer.domElement.style.visibility = "visible";
+		if (params.checkerboardTransparency) {
+			document.body.classList.add("checkerboard");
 		}
 
 		ptRenderer.reset();
-
 	};
 
 	const url = modelInfo.url;
-	if ( /(gltf|glb)$/i.test( url ) ) {
-
+	if (/(gltf|glb)$/i.test(url)) {
 		manager.onLoad = onFinish;
-		new GLTFLoader( manager )
-			.setMeshoptDecoder( MeshoptDecoder )
-			.load(
-				url,
-				gltf => {
-
-					model = gltf.scene;
-
-				},
-				progress => {
-
-					if ( progress.total !== 0 && progress.total >= progress.loaded ) {
-
-						const percent = Math.floor( 100 * progress.loaded / progress.total );
-						loadingEl.innerText = `Loading : ${ percent }%`;
-
-					}
-
-				},
-			);
-
-	} else if ( /mpd$/i.test( url ) ) {
-
-		manager.onProgress = ( url, loaded, total ) => {
-
-			const percent = Math.floor( 100 * loaded / total );
-			loadingEl.innerText = `Loading : ${ percent }%`;
-
+		new GLTFLoader(manager).setMeshoptDecoder(MeshoptDecoder).load(
+			url,
+			(gltf) => {
+				model = gltf.scene;
+			},
+			(progress) => {
+				if (progress.total !== 0 && progress.total >= progress.loaded) {
+					const percent = Math.floor((100 * progress.loaded) / progress.total);
+					loadingEl.innerText = `Loading : ${percent}%`;
+				}
+			}
+		);
+	} else if (/mpd$/i.test(url)) {
+		manager.onProgress = (url, loaded, total) => {
+			const percent = Math.floor((100 * loaded) / total);
+			loadingEl.innerText = `Loading : ${percent}%`;
 		};
 
-		const loader = new LDrawLoader( manager );
-		await loader.preloadMaterials( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/colors/ldcfgalt.ldr' );
+		const loader = new LDrawLoader(manager);
+		await loader.preloadMaterials(
+			"https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/colors/ldcfgalt.ldr"
+		);
 		loader
-			.setPartsLibraryPath( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/complete/ldraw/' )
-			.load(
-				url,
-				result => {
-
-					model = LDrawUtils.mergeObject( result );
-					model.rotation.set( Math.PI, 0, 0 );
-					model.traverse( c => {
-
-						if ( c.isLineSegments ) {
-
-							c.visible = false;
-
-						}
-
-						if ( c.isMesh ) {
-
-							c.material.roughness *= 0.01;
-
-						}
-
-					} );
-					onFinish();
-
-				},
-			);
+			.setPartsLibraryPath(
+				"https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/complete/ldraw/"
+			)
+			.load(url, (result) => {
+				model = LDrawUtils.mergeObject(result);
+				model.rotation.set(Math.PI, 0, 0);
+				model.traverse((c) => {
+					if (c.isLineSegments) {
+						c.visible = false;
+					}
 
+					if (c.isMesh) {
+						c.material.roughness *= 0.01;
+					}
+				});
+				onFinish();
+			});
 	}
-
 }
diff --git a/example/materialBall.js b/example/materialBall.js
index d1a850161..cba1106dd 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -1,15 +1,28 @@
-import * as THREE from 'three';
-import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
-import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
-import { PathTracingRenderer, PhysicalPathTracingMaterial, PhysicalCamera, BlurredEnvMapGenerator, EquirectCamera } from '../src/index.js';
-import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js';
-import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
-import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
-import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
-import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
-
-let renderer, controls, sceneInfo, ptRenderer, activeCamera, fsQuad, materials, temporalResolve;
+import * as THREE from "three";
+import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import {
+	PathTracingRenderer,
+	PhysicalPathTracingMaterial,
+	PhysicalCamera,
+	BlurredEnvMapGenerator,
+	EquirectCamera,
+} from "../src/index.js";
+import { PathTracingSceneWorker } from "../src/workers/PathTracingSceneWorker.js";
+import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
+import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
+import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
+import { TemporalResolve } from "../src/temporal-resolve/TemporalResolve.js";
+
+let renderer,
+	controls,
+	sceneInfo,
+	ptRenderer,
+	activeCamera,
+	fsQuad,
+	materials,
+	temporalResolve;
 let perspectiveCamera, orthoCamera, equirectCamera;
 let envMap, envMapGenerator, scene;
 let samplesEl;
@@ -17,10 +30,9 @@ let samplesEl;
 const orthoWidth = 5;
 
 const params = {
-
 	material1: {
-		color: '#ffc766',
-		emissive: '#000000',
+		color: "#ffc766",
+		emissive: "#000000",
 		emissiveIntensity: 1,
 		roughness: 0.1,
 		metalness: 0.8,
@@ -29,19 +41,19 @@ const params = {
 		opacity: 1.0,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: '#000000',
+		sheenColor: "#000000",
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: '#ffffff',
+		specularColor: "#ffffff",
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
 	},
 	material2: {
-		color: '#db7157',
-		emissive: '#000000',
+		color: "#db7157",
+		emissive: "#000000",
 		emissiveIntensity: 1,
 		roughness: 0.8,
 		metalness: 0.1,
@@ -50,28 +62,28 @@ const params = {
 		opacity: 1.0,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: '#000000',
+		sheenColor: "#000000",
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: '#ffffff',
+		specularColor: "#ffffff",
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
 	},
 	material3: {
-		color: '#000000',
+		color: "#000000",
 		roughness: 0.01,
 		metalness: 0.05,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: '#000000',
+		sheenColor: "#000000",
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: '#ffffff',
+		specularColor: "#ffffff",
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
@@ -88,7 +100,7 @@ const params = {
 	resolutionScale: 1 / window.devicePixelRatio,
 	temporalResolve: true,
 	temporalResolveMix: 0.875,
-	clampRing: 2,
+	clampRadius: 2,
 	newSamplesSmoothing: 0.5,
 	newSamplesCorrection: 0.75,
 	transparentTraversals: 20,
@@ -96,394 +108,395 @@ const params = {
 	tiles: 1,
 	backgroundAlpha: 1,
 	checkerboardTransparency: true,
-	cameraProjection: 'Perspective',
+	cameraProjection: "Perspective",
 };
 
-if ( window.location.hash.includes( 'transmission' ) ) {
-
+if (window.location.hash.includes("transmission")) {
 	params.material1.metalness = 0.0;
 	params.material1.roughness = 0.05;
 	params.material1.transmission = 1.0;
-	params.material1.color = '#ffffff';
+	params.material1.color = "#ffffff";
 	params.bounces = 10;
-
 }
 
 // adjust performance parameters for mobile
 const aspectRatio = window.innerWidth / window.innerHeight;
-if ( aspectRatio < 0.65 ) {
-
-	params.bounces = Math.max( params.bounces, 6 );
+if (aspectRatio < 0.65) {
+	params.bounces = Math.max(params.bounces, 6);
 	params.resolutionScale *= 0.5;
 	params.tiles = 2;
 	params.multipleImportanceSampling = false;
 	params.environmentBlur = 0.35;
-
 }
 
 init();
 
 async function init() {
-
-	renderer = new THREE.WebGLRenderer( { antialias: true } );
+	renderer = new THREE.WebGLRenderer({ antialias: true });
 	renderer.toneMapping = THREE.ACESFilmicToneMapping;
 	renderer.outputEncoding = THREE.sRGBEncoding;
-	renderer.setClearColor( 0, 0 );
-	document.body.appendChild( renderer.domElement );
+	renderer.setClearColor(0, 0);
+	document.body.appendChild(renderer.domElement);
 
 	const aspect = window.innerWidth / window.innerHeight;
-	perspectiveCamera = new PhysicalCamera( 75, aspect, 0.025, 500 );
-	perspectiveCamera.position.set( - 4, 2, 3 );
+	perspectiveCamera = new PhysicalCamera(75, aspect, 0.025, 500);
+	perspectiveCamera.position.set(-4, 2, 3);
 
 	const orthoHeight = orthoWidth / aspect;
-	orthoCamera = new THREE.OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
-	orthoCamera.position.set( - 4, 2, 3 );
+	orthoCamera = new THREE.OrthographicCamera(
+		orthoWidth / -2,
+		orthoWidth / 2,
+		orthoHeight / 2,
+		orthoHeight / -2,
+		0,
+		100
+	);
+	orthoCamera.position.set(-4, 2, 3);
 
 	equirectCamera = new EquirectCamera();
-	equirectCamera.position.set( - 4, 2, 3 );
+	equirectCamera.position.set(-4, 2, 3);
 
-	ptRenderer = new PathTracingRenderer( renderer );
+	ptRenderer = new PathTracingRenderer(renderer);
 	ptRenderer.alpha = true;
 	ptRenderer.material = new PhysicalPathTracingMaterial();
-	ptRenderer.material.setDefine( 'TRANSPARENT_TRAVERSALS', params.transparentTraversals );
-	ptRenderer.material.setDefine( 'FEATURE_MIS', Number( params.multipleImportanceSampling ) );
-	ptRenderer.tiles.set( params.tiles, params.tiles );
-
-	fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
-		map: ptRenderer.target.texture,
-		blending: THREE.CustomBlending,
-	} ) );
-
-	controls = new OrbitControls( perspectiveCamera, renderer.domElement );
-	controls.addEventListener( 'change', () => {
-
+	ptRenderer.material.setDefine(
+		"TRANSPARENT_TRAVERSALS",
+		params.transparentTraversals
+	);
+	ptRenderer.material.setDefine(
+		"FEATURE_MIS",
+		Number(params.multipleImportanceSampling)
+	);
+	ptRenderer.tiles.set(params.tiles, params.tiles);
+
+	fsQuad = new FullScreenQuad(
+		new THREE.MeshBasicMaterial({
+			map: ptRenderer.target.texture,
+			blending: THREE.CustomBlending,
+		})
+	);
+
+	controls = new OrbitControls(perspectiveCamera, renderer.domElement);
+	controls.addEventListener("change", () => {
 		ptRenderer.reset();
-
-	} );
+	});
 
 	scene = new THREE.Scene();
 
-	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
+	temporalResolve = new TemporalResolve(ptRenderer, scene, activeCamera);
 	temporalResolve.temporalResolveMix = 0.875;
-	temporalResolve.clampRing = 2;
+	temporalResolve.clampRadius = 2;
 	temporalResolve.newSamplesSmoothing = 0.5;
 	temporalResolve.newSamplesCorrection = 0.75;
 
-	samplesEl = document.getElementById( 'samples' );
+	samplesEl = document.getElementById("samples");
 
-	envMapGenerator = new BlurredEnvMapGenerator( renderer );
-
-	const envMapPromise = new Promise( resolve => {
-
-		new RGBELoader()
-			.load( 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr', texture => {
+	envMapGenerator = new BlurredEnvMapGenerator(renderer);
 
+	const envMapPromise = new Promise((resolve) => {
+		new RGBELoader().load(
+			"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr",
+			(texture) => {
 				envMap = texture;
 
 				updateEnvBlur();
 				resolve();
-
-			} );
-
-	} );
+			}
+		);
+	});
 
 	const generator = new PathTracingSceneWorker();
 	const gltfPromise = new GLTFLoader()
-		.setMeshoptDecoder( MeshoptDecoder )
-		.loadAsync( 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/main/models/material-balls/material_ball_v2.glb' )
-		.then( gltf => {
-
+		.setMeshoptDecoder(MeshoptDecoder)
+		.loadAsync(
+			"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/main/models/material-balls/material_ball_v2.glb"
+		)
+		.then((gltf) => {
 			const group = new THREE.Group();
 
-			gltf.scene.scale.setScalar( 0.01 );
+			gltf.scene.scale.setScalar(0.01);
 			gltf.scene.updateMatrixWorld();
-			group.add( gltf.scene );
+			group.add(gltf.scene);
 
 			const box = new THREE.Box3();
-			box.setFromObject( gltf.scene );
+			box.setFromObject(gltf.scene);
 
 			const floor = new THREE.Mesh(
-				new THREE.CylinderBufferGeometry( 3, 3, 0.05, 200 ),
-				new THREE.MeshPhysicalMaterial( { color: 0xffffff, roughness: 0, metalness: 0.25 } ),
+				new THREE.CylinderBufferGeometry(3, 3, 0.05, 200),
+				new THREE.MeshPhysicalMaterial({
+					color: 0xffffff,
+					roughness: 0,
+					metalness: 0.25,
+				})
 			);
 			floor.geometry = floor.geometry.toNonIndexed();
 			floor.geometry.clearGroups();
 			floor.position.y = box.min.y - 0.03;
-			group.add( floor );
+			group.add(floor);
 
 			const material1 = new THREE.MeshPhysicalMaterial();
 			const material2 = new THREE.MeshPhysicalMaterial();
 
-			gltf.scene.traverse( c => {
-
+			gltf.scene.traverse((c) => {
 				// the vertex normals on the material ball are off...
 				// TODO: precompute the vertex normals so they are correct on load
-				if ( c.geometry ) {
-
+				if (c.geometry) {
 					c.geometry.computeVertexNormals();
-
 				}
 
-				if ( c.name === 'Sphere_1' ) {
-
+				if (c.name === "Sphere_1") {
 					c.material = material2;
-
 				} else {
-
 					c.material = material1;
-
 				}
 
-				if ( c.name === 'subsphere_1' ) {
-
+				if (c.name === "subsphere_1") {
 					c.material = material2;
-
 				}
+			});
 
-			} );
-
-			materials = [ material1, material2, floor.material ];
-
-			return generator.generate( group );
-
-		} )
-		.then( result => {
+			materials = [material1, material2, floor.material];
 
+			return generator.generate(group);
+		})
+		.then((result) => {
 			sceneInfo = result;
 
-			scene.add( result.scene );
+			scene.add(result.scene);
 
 			const { bvh, textures, materials } = result;
 			const geometry = bvh.geometry;
 			const material = ptRenderer.material;
 
-			material.bvh.updateFrom( bvh );
-			material.normalAttribute.updateFrom( geometry.attributes.normal );
-			material.tangentAttribute.updateFrom( geometry.attributes.tangent );
-			material.uvAttribute.updateFrom( geometry.attributes.uv );
-			material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
-			material.textures.setTextures( renderer, 2048, 2048, textures );
-			material.materials.updateFrom( materials, textures );
+			material.bvh.updateFrom(bvh);
+			material.normalAttribute.updateFrom(geometry.attributes.normal);
+			material.tangentAttribute.updateFrom(geometry.attributes.tangent);
+			material.uvAttribute.updateFrom(geometry.attributes.uv);
+			material.materialIndexAttribute.updateFrom(
+				geometry.attributes.materialIndex
+			);
+			material.textures.setTextures(renderer, 2048, 2048, textures);
+			material.materials.updateFrom(materials, textures);
 
 			generator.dispose();
+		});
 
-		} );
-
-	await Promise.all( [ gltfPromise, envMapPromise ] );
+	await Promise.all([gltfPromise, envMapPromise]);
 
-	document.getElementById( 'loading' ).remove();
-	document.body.classList.add( 'checkerboard' );
+	document.getElementById("loading").remove();
+	document.body.classList.add("checkerboard");
 
 	onResize();
-	window.addEventListener( 'resize', onResize );
+	window.addEventListener("resize", onResize);
 	const gui = new GUI();
 
-	updateCamera( params.cameraProjection );
+	updateCamera(params.cameraProjection);
 
-	const ptFolder = gui.addFolder( 'Path Tracing' );
-	ptFolder.add( params, 'acesToneMapping' ).onChange( value => {
-
-		renderer.toneMapping = value ? THREE.ACESFilmicToneMapping : THREE.NoToneMapping;
+	const ptFolder = gui.addFolder("Path Tracing");
+	ptFolder.add(params, "acesToneMapping").onChange((value) => {
+		renderer.toneMapping = value
+			? THREE.ACESFilmicToneMapping
+			: THREE.NoToneMapping;
 		fsQuad.material.needsUpdate = true;
-
-	} );
-	ptFolder.add( params, 'stableNoise' ).onChange( value => {
-
+	});
+	ptFolder.add(params, "stableNoise").onChange((value) => {
 		ptRenderer.stableNoise = value;
-
-	} );
-	ptFolder.add( params, 'multipleImportanceSampling' ).onChange( value => {
-
-		ptRenderer.material.setDefine( 'FEATURE_MIS', Number( value ) );
+	});
+	ptFolder.add(params, "multipleImportanceSampling").onChange((value) => {
+		ptRenderer.material.setDefine("FEATURE_MIS", Number(value));
 		ptRenderer.reset();
-
-	} );
-	ptFolder.add( params, 'tiles', 1, 4, 1 ).onChange( value => {
-
-		ptRenderer.tiles.set( value, value );
-
-	} );
-	ptFolder.add( params, 'samplesPerFrame', 1, 10, 1 );
-	ptFolder.add( params, 'filterGlossyFactor', 0, 1 ).onChange( () => {
-
+	});
+	ptFolder.add(params, "tiles", 1, 4, 1).onChange((value) => {
+		ptRenderer.tiles.set(value, value);
+	});
+	ptFolder.add(params, "samplesPerFrame", 1, 10, 1);
+	ptFolder.add(params, "filterGlossyFactor", 0, 1).onChange(() => {
 		ptRenderer.reset();
-
-	} );
-	ptFolder.add( params, 'bounces', 1, 30, 1 ).onChange( () => {
-
+	});
+	ptFolder.add(params, "bounces", 1, 30, 1).onChange(() => {
 		ptRenderer.reset();
-
-	} );
-	ptFolder.add( params, 'transparentTraversals', 0, 40, 1 ).onChange( value => {
-
-		ptRenderer.material.setDefine( 'TRANSPARENT_TRAVERSALS', value );
+	});
+	ptFolder.add(params, "transparentTraversals", 0, 40, 1).onChange((value) => {
+		ptRenderer.material.setDefine("TRANSPARENT_TRAVERSALS", value);
 		ptRenderer.reset();
-
-	} );
-	ptFolder.add( params, 'resolutionScale', 0.1, 1 ).onChange( () => {
-
+	});
+	ptFolder.add(params, "resolutionScale", 0.1, 1).onChange(() => {
 		onResize();
+	});
 
-	} );
-
-	const trFolder = gui.addFolder( 'Temporal Resolve' );
-	trFolder.add( params, 'temporalResolve' );
+	const trFolder = gui.addFolder("Temporal Resolve");
+	trFolder.add(params, "temporalResolve");
 	trFolder
-		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
+		.add(params, "temporalResolveMix", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.temporalResolveMix = value));
 	trFolder
-		.add( params, 'clampRing', 1, 8, 1 )
-		.onChange( ( value ) => ( temporalResolve.clampRing = value ) );
+		.add(params, "clampRadius", 1, 8, 1)
+		.onChange((value) => (temporalResolve.clampRadius = value));
 	trFolder
-		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
+		.add(params, "newSamplesSmoothing", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.newSamplesSmoothing = value));
 	trFolder
-		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
-		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
-
-	const envFolder = gui.addFolder( 'Environment' );
-	envFolder.add( params, 'environmentIntensity', 0, 10 ).onChange( () => {
+		.add(params, "newSamplesCorrection", 0, 1, 0.025)
+		.onChange((value) => (temporalResolve.newSamplesCorrection = value));
 
+	const envFolder = gui.addFolder("Environment");
+	envFolder.add(params, "environmentIntensity", 0, 10).onChange(() => {
 		ptRenderer.reset();
-
-	} );
-	envFolder.add( params, 'environmentRotation', 0, 2 * Math.PI ).onChange( v => {
-
-		ptRenderer.material.environmentRotation.setFromMatrix4( new THREE.Matrix4().makeRotationY( v ) );
+	});
+	envFolder.add(params, "environmentRotation", 0, 2 * Math.PI).onChange((v) => {
+		ptRenderer.material.environmentRotation.setFromMatrix4(
+			new THREE.Matrix4().makeRotationY(v)
+		);
 		ptRenderer.reset();
-
-	} );
-	envFolder.add( params, 'environmentBlur', 0, 1 ).onChange( () => {
-
+	});
+	envFolder.add(params, "environmentBlur", 0, 1).onChange(() => {
 		updateEnvBlur();
-
-	} );
-	envFolder.add( params, 'backgroundBlur', 0, 1 ).onChange( () => {
-
+	});
+	envFolder.add(params, "backgroundBlur", 0, 1).onChange(() => {
 		ptRenderer.reset();
-
-	} );
-	envFolder.add( params, 'backgroundAlpha', 0, 1 ).onChange( () => {
-
+	});
+	envFolder.add(params, "backgroundAlpha", 0, 1).onChange(() => {
 		ptRenderer.reset();
-
-	} );
-	envFolder.add( params, 'checkerboardTransparency' ).onChange( v => {
-
-		if ( v ) {
-
-			document.body.classList.add( 'checkerboard' );
-
+	});
+	envFolder.add(params, "checkerboardTransparency").onChange((v) => {
+		if (v) {
+			document.body.classList.add("checkerboard");
 		} else {
-
-			document.body.classList.remove( 'checkerboard' );
-
+			document.body.classList.remove("checkerboard");
 		}
-
-	} );
-
-	const cameraFolder = gui.addFolder( 'Camera' );
-	cameraFolder.add( params, 'cameraProjection', [ 'Perspective', 'Orthographic', 'Equirectangular' ] ).onChange( v => {
-
-		updateCamera( v );
-
-	} );
-	cameraFolder.add( perspectiveCamera, 'focusDistance', 1, 100 ).onChange( reset );
-	cameraFolder.add( perspectiveCamera, 'apertureBlades', 0, 10, 1 ).onChange( function ( v ) {
-
-		perspectiveCamera.apertureBlades = v === 0 ? 0 : Math.max( v, 3 );
-		this.updateDisplay();
-		reset();
-
-	} );
-	cameraFolder.add( perspectiveCamera, 'apertureRotation', 0, 12.5 ).onChange( reset );
-	cameraFolder.add( perspectiveCamera, 'anamorphicRatio', 0.1, 10.0 ).onChange( reset );
-	cameraFolder.add( perspectiveCamera, 'bokehSize', 0, 50 ).onChange( reset ).listen();
-	cameraFolder.add( perspectiveCamera, 'fStop', 0.3, 20 ).onChange( reset ).listen();
-	cameraFolder.add( perspectiveCamera, 'fov', 25, 100 ).onChange( () => {
-
-		perspectiveCamera.updateProjectionMatrix();
-		reset();
-
-	} ).listen();
-
-	const matFolder1 = gui.addFolder( 'Shell Material' );
-	matFolder1.addColor( params.material1, 'color' ).onChange( reset );
-	matFolder1.addColor( params.material1, 'emissive' ).onChange( reset );
-	matFolder1.add( params.material1, 'emissiveIntensity', 0.0, 50.0, 0.01 ).onChange( reset );
-	matFolder1.add( params.material1, 'roughness', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'metalness', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'opacity', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'transmission', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'ior', 0.9, 3.0 ).onChange( reset );
-	matFolder1.add( params.material1, 'clearcoat', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'clearcoatRoughness', 0, 1 ).onChange( reset );
-	matFolder1.addColor( params.material1, 'sheenColor' ).onChange( reset );
-	matFolder1.add( params.material1, 'sheenRoughness', 0, 1 ).onChange( reset );
-	matFolder1.add( params.material1, 'iridescence', 0.0, 1.0 ).onChange( reset );
-	matFolder1.add( params.material1, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
-	matFolder1.add( params.material1, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
-	matFolder1.addColor( params.material1, 'specularColor' ).onChange( reset );
-	matFolder1.add( params.material1, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
-	matFolder1.add( params.material1, 'matte' ).onChange( reset );
-	matFolder1.add( params.material1, 'castShadow' ).onChange( reset );
+	});
+
+	const cameraFolder = gui.addFolder("Camera");
+	cameraFolder
+		.add(params, "cameraProjection", [
+			"Perspective",
+			"Orthographic",
+			"Equirectangular",
+		])
+		.onChange((v) => {
+			updateCamera(v);
+		});
+	cameraFolder.add(perspectiveCamera, "focusDistance", 1, 100).onChange(reset);
+	cameraFolder
+		.add(perspectiveCamera, "apertureBlades", 0, 10, 1)
+		.onChange(function (v) {
+			perspectiveCamera.apertureBlades = v === 0 ? 0 : Math.max(v, 3);
+			this.updateDisplay();
+			reset();
+		});
+	cameraFolder
+		.add(perspectiveCamera, "apertureRotation", 0, 12.5)
+		.onChange(reset);
+	cameraFolder
+		.add(perspectiveCamera, "anamorphicRatio", 0.1, 10.0)
+		.onChange(reset);
+	cameraFolder
+		.add(perspectiveCamera, "bokehSize", 0, 50)
+		.onChange(reset)
+		.listen();
+	cameraFolder
+		.add(perspectiveCamera, "fStop", 0.3, 20)
+		.onChange(reset)
+		.listen();
+	cameraFolder
+		.add(perspectiveCamera, "fov", 25, 100)
+		.onChange(() => {
+			perspectiveCamera.updateProjectionMatrix();
+			reset();
+		})
+		.listen();
+
+	const matFolder1 = gui.addFolder("Shell Material");
+	matFolder1.addColor(params.material1, "color").onChange(reset);
+	matFolder1.addColor(params.material1, "emissive").onChange(reset);
+	matFolder1
+		.add(params.material1, "emissiveIntensity", 0.0, 50.0, 0.01)
+		.onChange(reset);
+	matFolder1.add(params.material1, "roughness", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "metalness", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "opacity", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "transmission", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "ior", 0.9, 3.0).onChange(reset);
+	matFolder1.add(params.material1, "clearcoat", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "clearcoatRoughness", 0, 1).onChange(reset);
+	matFolder1.addColor(params.material1, "sheenColor").onChange(reset);
+	matFolder1.add(params.material1, "sheenRoughness", 0, 1).onChange(reset);
+	matFolder1.add(params.material1, "iridescence", 0.0, 1.0).onChange(reset);
+	matFolder1.add(params.material1, "iridescenceIOR", 0.1, 3.0).onChange(reset);
+	matFolder1
+		.add(params.material1, "iridescenceThickness", 0.0, 1200.0)
+		.onChange(reset);
+	matFolder1.addColor(params.material1, "specularColor").onChange(reset);
+	matFolder1
+		.add(params.material1, "specularIntensity", 0.0, 1.0)
+		.onChange(reset);
+	matFolder1.add(params.material1, "matte").onChange(reset);
+	matFolder1.add(params.material1, "castShadow").onChange(reset);
 	matFolder1.close();
 
-	const matFolder2 = gui.addFolder( 'Ball Material' );
-	matFolder2.addColor( params.material2, 'color' ).onChange( reset );
-	matFolder2.addColor( params.material2, 'emissive' ).onChange( reset );
-	matFolder2.add( params.material2, 'emissiveIntensity', 0.0, 50.0, 0.01 ).onChange( reset );
-	matFolder2.add( params.material2, 'roughness', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'metalness', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'opacity', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'transmission', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'ior', 0.9, 3.0 ).onChange( reset );
-	matFolder2.add( params.material2, 'clearcoat', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'clearcoatRoughness', 0, 1 ).onChange( reset );
-	matFolder2.addColor( params.material2, 'sheenColor' ).onChange( reset );
-	matFolder2.add( params.material2, 'sheenRoughness', 0, 1 ).onChange( reset );
-	matFolder2.add( params.material2, 'iridescence', 0.0, 1.0 ).onChange( reset );
-	matFolder2.add( params.material2, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
-	matFolder2.add( params.material2, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
-	matFolder2.addColor( params.material2, 'specularColor' ).onChange( reset );
-	matFolder2.add( params.material2, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
-	matFolder2.add( params.material2, 'matte' ).onChange( reset );
-	matFolder2.add( params.material2, 'castShadow' ).onChange( reset );
+	const matFolder2 = gui.addFolder("Ball Material");
+	matFolder2.addColor(params.material2, "color").onChange(reset);
+	matFolder2.addColor(params.material2, "emissive").onChange(reset);
+	matFolder2
+		.add(params.material2, "emissiveIntensity", 0.0, 50.0, 0.01)
+		.onChange(reset);
+	matFolder2.add(params.material2, "roughness", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "metalness", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "opacity", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "transmission", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "ior", 0.9, 3.0).onChange(reset);
+	matFolder2.add(params.material2, "clearcoat", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "clearcoatRoughness", 0, 1).onChange(reset);
+	matFolder2.addColor(params.material2, "sheenColor").onChange(reset);
+	matFolder2.add(params.material2, "sheenRoughness", 0, 1).onChange(reset);
+	matFolder2.add(params.material2, "iridescence", 0.0, 1.0).onChange(reset);
+	matFolder2.add(params.material2, "iridescenceIOR", 0.1, 3.0).onChange(reset);
+	matFolder2
+		.add(params.material2, "iridescenceThickness", 0.0, 1200.0)
+		.onChange(reset);
+	matFolder2.addColor(params.material2, "specularColor").onChange(reset);
+	matFolder2
+		.add(params.material2, "specularIntensity", 0.0, 1.0)
+		.onChange(reset);
+	matFolder2.add(params.material2, "matte").onChange(reset);
+	matFolder2.add(params.material2, "castShadow").onChange(reset);
 	matFolder2.close();
 
-	const matFolder3 = gui.addFolder( 'Floor Material' );
-	matFolder3.addColor( params.material3, 'color' ).onChange( reset );
-	matFolder3.add( params.material3, 'roughness', 0, 1 ).onChange( reset );
-	matFolder3.add( params.material3, 'metalness', 0, 1 ).onChange( reset );
-	matFolder3.add( params.material3, 'clearcoat', 0, 1 ).onChange( reset );
-	matFolder3.add( params.material3, 'clearcoatRoughness', 0, 1 ).onChange( reset );
-	matFolder3.addColor( params.material3, 'sheenColor' ).onChange( reset );
-	matFolder3.add( params.material3, 'sheenRoughness', 0, 1 ).onChange( reset );
-	matFolder3.add( params.material3, 'matte' ).onChange( reset );
-	matFolder3.add( params.material3, 'castShadow' ).onChange( reset );
-	matFolder3.add( params.material3, 'iridescence', 0.0, 1.0 ).onChange( reset );
-	matFolder3.add( params.material3, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
-	matFolder3.add( params.material3, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
-	matFolder3.addColor( params.material3, 'specularColor' ).onChange( reset );
-	matFolder3.add( params.material3, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
+	const matFolder3 = gui.addFolder("Floor Material");
+	matFolder3.addColor(params.material3, "color").onChange(reset);
+	matFolder3.add(params.material3, "roughness", 0, 1).onChange(reset);
+	matFolder3.add(params.material3, "metalness", 0, 1).onChange(reset);
+	matFolder3.add(params.material3, "clearcoat", 0, 1).onChange(reset);
+	matFolder3.add(params.material3, "clearcoatRoughness", 0, 1).onChange(reset);
+	matFolder3.addColor(params.material3, "sheenColor").onChange(reset);
+	matFolder3.add(params.material3, "sheenRoughness", 0, 1).onChange(reset);
+	matFolder3.add(params.material3, "matte").onChange(reset);
+	matFolder3.add(params.material3, "castShadow").onChange(reset);
+	matFolder3.add(params.material3, "iridescence", 0.0, 1.0).onChange(reset);
+	matFolder3.add(params.material3, "iridescenceIOR", 0.1, 3.0).onChange(reset);
+	matFolder3
+		.add(params.material3, "iridescenceThickness", 0.0, 1200.0)
+		.onChange(reset);
+	matFolder3.addColor(params.material3, "specularColor").onChange(reset);
+	matFolder3
+		.add(params.material3, "specularIntensity", 0.0, 1.0)
+		.onChange(reset);
 	matFolder3.close();
 
 	animate();
-
 }
 
 function onResize() {
-
 	const w = window.innerWidth;
 	const h = window.innerHeight;
 	const scale = params.resolutionScale;
 	const dpr = window.devicePixelRatio;
 
-	ptRenderer.setSize( w * scale * dpr, h * scale * dpr );
+	ptRenderer.setSize(w * scale * dpr, h * scale * dpr);
 	ptRenderer.reset();
 
-	renderer.setSize( w, h );
-	renderer.setPixelRatio( window.devicePixelRatio * scale );
+	renderer.setSize(w, h);
+	renderer.setPixelRatio(window.devicePixelRatio * scale);
 
 	const aspect = w / h;
 
@@ -492,58 +505,42 @@ function onResize() {
 
 	const orthoHeight = orthoWidth / aspect;
 	orthoCamera.top = orthoHeight / 2;
-	orthoCamera.bottom = orthoHeight / - 2;
+	orthoCamera.bottom = orthoHeight / -2;
 	orthoCamera.updateProjectionMatrix();
-
 }
 
 function reset() {
-
 	ptRenderer.reset();
-
 }
 
 function updateEnvBlur() {
-
-	const blurredTex = envMapGenerator.generate( envMap, params.environmentBlur );
-	ptRenderer.material.envMapInfo.updateFrom( blurredTex );
+	const blurredTex = envMapGenerator.generate(envMap, params.environmentBlur);
+	ptRenderer.material.envMapInfo.updateFrom(blurredTex);
 	scene.environment = blurredTex;
 	ptRenderer.reset();
-
 }
 
-function updateCamera( cameraProjection ) {
-
-	if ( cameraProjection === 'Perspective' ) {
-
-		if ( activeCamera ) {
-
-			perspectiveCamera.position.copy( activeCamera.position );
-
+function updateCamera(cameraProjection) {
+	if (cameraProjection === "Perspective") {
+		if (activeCamera) {
+			perspectiveCamera.position.copy(activeCamera.position);
 		}
 
 		activeCamera = perspectiveCamera;
-
-	} else if ( cameraProjection === 'Orthographic' ) {
-
-		if ( activeCamera ) {
-
-			orthoCamera.position.copy( activeCamera.position );
-
+	} else if (cameraProjection === "Orthographic") {
+		if (activeCamera) {
+			orthoCamera.position.copy(activeCamera.position);
 		}
 
 		activeCamera = orthoCamera;
+	} else {
+		// Equirect
 
-	} else { // Equirect
-
-		if ( activeCamera ) {
-
-			equirectCamera.position.copy( activeCamera.position );
-
+		if (activeCamera) {
+			equirectCamera.position.copy(activeCamera.position);
 		}
 
 		activeCamera = equirectCamera;
-
 	}
 
 	controls.object = activeCamera;
@@ -552,16 +549,14 @@ function updateCamera( cameraProjection ) {
 	controls.update();
 
 	reset();
-
 }
 
 function animate() {
+	requestAnimationFrame(animate);
 
-	requestAnimationFrame( animate );
-
-	const m1 = materials[ 0 ];
-	m1.color.set( params.material1.color ).convertSRGBToLinear();
-	m1.emissive.set( params.material1.emissive ).convertSRGBToLinear();
+	const m1 = materials[0];
+	m1.color.set(params.material1.color).convertSRGBToLinear();
+	m1.emissive.set(params.material1.emissive).convertSRGBToLinear();
 	m1.emissiveIntensity = params.material1.emissiveIntensity;
 	m1.metalness = params.material1.metalness;
 	m1.roughness = params.material1.roughness;
@@ -570,17 +565,17 @@ function animate() {
 	m1.opacity = params.material1.opacity;
 	m1.clearcoat = params.material1.clearcoat;
 	m1.clearcoatRoughness = params.material1.clearcoatRoughness;
-	m1.sheenColor.set( params.material1.sheenColor ).convertSRGBToLinear();
+	m1.sheenColor.set(params.material1.sheenColor).convertSRGBToLinear();
 	m1.sheenRoughness = params.material1.sheenRoughness;
 	m1.iridescence = params.material1.iridescence;
 	m1.iridescenceIOR = params.material1.iridescenceIOR;
-	m1.iridescenceThicknessRange = [ 0, params.material1.iridescenceThickness ];
-	m1.specularColor.set( params.material1.specularColor ).convertSRGBToLinear();
+	m1.iridescenceThicknessRange = [0, params.material1.iridescenceThickness];
+	m1.specularColor.set(params.material1.specularColor).convertSRGBToLinear();
 	m1.specularIntensity = params.material1.specularIntensity;
 
-	const m2 = materials[ 1 ];
-	m2.color.set( params.material2.color ).convertSRGBToLinear();
-	m2.emissive.set( params.material2.emissive ).convertSRGBToLinear();
+	const m2 = materials[1];
+	m2.color.set(params.material2.color).convertSRGBToLinear();
+	m2.emissive.set(params.material2.emissive).convertSRGBToLinear();
 	m2.emissiveIntensity = params.material2.emissiveIntensity;
 	m2.metalness = params.material2.metalness;
 	m2.roughness = params.material2.roughness;
@@ -589,86 +584,72 @@ function animate() {
 	m2.opacity = params.material2.opacity;
 	m2.clearcoat = params.material2.clearcoat;
 	m2.clearcoatRoughness = params.material2.clearcoatRoughness;
-	m2.sheenColor.set( params.material2.sheenColor ).convertSRGBToLinear();
+	m2.sheenColor.set(params.material2.sheenColor).convertSRGBToLinear();
 	m2.sheenRoughness = params.material2.sheenRoughness;
 	m2.iridescence = params.material2.iridescence;
 	m2.iridescenceIOR = params.material2.iridescenceIOR;
-	m2.iridescenceThicknessRange = [ 0, params.material2.iridescenceThickness ];
-	m2.specularColor.set( params.material2.specularColor ).convertSRGBToLinear();
+	m2.iridescenceThicknessRange = [0, params.material2.iridescenceThickness];
+	m2.specularColor.set(params.material2.specularColor).convertSRGBToLinear();
 	m2.specularIntensity = params.material2.specularIntensity;
 
-	const m3 = materials[ 2 ];
-	m3.color.set( params.material3.color ).convertSRGBToLinear();
+	const m3 = materials[2];
+	m3.color.set(params.material3.color).convertSRGBToLinear();
 	m3.metalness = params.material3.metalness;
 	m3.roughness = params.material3.roughness;
 	m3.clearcoat = params.material3.clearcoat;
 	m3.clearcoatRoughness = params.material3.clearcoatRoughness;
-	m3.sheenColor.set( params.material3.sheenColor ).convertSRGBToLinear();
+	m3.sheenColor.set(params.material3.sheenColor).convertSRGBToLinear();
 	m3.sheenRoughness = params.material3.sheenRoughness;
 	m3.iridescence = params.material3.iridescence;
 	m3.iridescenceIOR = params.material3.iridescenceIOR;
-	m3.iridescenceThicknessRange = [ 0, params.material3.iridescenceThickness ];
-	m3.specularColor.set( params.material3.specularColor ).convertSRGBToLinear();
+	m3.iridescenceThicknessRange = [0, params.material3.iridescenceThickness];
+	m3.specularColor.set(params.material3.specularColor).convertSRGBToLinear();
 	m3.specularIntensity = params.material3.specularIntensity;
 
-	ptRenderer.material.materials.updateFrom( sceneInfo.materials, sceneInfo.textures );
-	ptRenderer.material.materials.setMatte( 0, params.material1.matte );
-	ptRenderer.material.materials.setMatte( 1, params.material2.matte );
-	ptRenderer.material.materials.setMatte( 2, params.material3.matte );
-	ptRenderer.material.materials.setCastShadow( 0, params.material1.castShadow );
-	ptRenderer.material.materials.setCastShadow( 1, params.material2.castShadow );
-	ptRenderer.material.materials.setCastShadow( 2, params.material3.castShadow );
+	ptRenderer.material.materials.updateFrom(
+		sceneInfo.materials,
+		sceneInfo.textures
+	);
+	ptRenderer.material.materials.setMatte(0, params.material1.matte);
+	ptRenderer.material.materials.setMatte(1, params.material2.matte);
+	ptRenderer.material.materials.setMatte(2, params.material3.matte);
+	ptRenderer.material.materials.setCastShadow(0, params.material1.castShadow);
+	ptRenderer.material.materials.setCastShadow(1, params.material2.castShadow);
+	ptRenderer.material.materials.setCastShadow(2, params.material3.castShadow);
 
 	ptRenderer.material.filterGlossyFactor = params.filterGlossyFactor;
 	ptRenderer.material.environmentIntensity = params.environmentIntensity;
 	ptRenderer.material.backgroundBlur = params.backgroundBlur;
 	ptRenderer.material.bounces = params.bounces;
 	ptRenderer.material.backgroundAlpha = params.backgroundAlpha;
-	ptRenderer.material.physicalCamera.updateFrom( activeCamera );
+	ptRenderer.material.physicalCamera.updateFrom(activeCamera);
 
 	activeCamera.updateMatrixWorld();
 
-	if ( params.backgroundAlpha < 1.0 ) {
-
+	if (params.backgroundAlpha < 1.0) {
 		scene.background = null;
-
 	} else {
-
 		scene.background = scene.environment;
-
 	}
 
-	for ( let i = 0, l = params.samplesPerFrame; i < l; i ++ ) {
-
+	for (let i = 0, l = params.samplesPerFrame; i < l; i++) {
 		ptRenderer.update();
-
 	}
 
-	if ( ! params.temporalResolve && ptRenderer.samples < 1 ) {
-
-		renderer.render( scene, activeCamera );
-
+	if (!params.temporalResolve && ptRenderer.samples < 1) {
+		renderer.render(scene, activeCamera);
 	}
 
 	renderer.autoClear = false;
-	if ( params.temporalResolve ) {
-
+	if (params.temporalResolve) {
 		temporalResolve.update();
 		fsQuad.material.map = temporalResolve.target.texture;
-
 	} else {
-
 		fsQuad.material.map = ptRenderer.target.texture;
-
 	}
 
-	fsQuad.render( renderer );
+	fsQuad.render(renderer);
 	renderer.autoClear = true;
 
-	samplesEl.innerText = `Samples: ${ Math.floor( ptRenderer.samples ) }`;
-
+	samplesEl.innerText = `Samples: ${Math.floor(ptRenderer.samples)}`;
 }
-
-
-
-
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 2dfa2684b..647433d45 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -1,27 +1,25 @@
-import { HalfFloatType, LinearFilter, WebGLRenderTarget } from 'three';
-import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
-import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial';
-import { TemporalResolvePass } from './passes/TemporalResolvePass';
+import { HalfFloatType, LinearFilter, WebGLRenderTarget } from "three";
+import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass";
+import { ComposeTemporalResolveMaterial } from "./materials/ComposeTemporalResolveMaterial";
+import { TemporalResolvePass } from "./passes/TemporalResolvePass";
 
 export class TemporalResolve {
-
-	constructor( ptRenderer, scene, camera ) {
-
+	constructor(ptRenderer, scene, camera) {
 		this.ptRenderer = ptRenderer;
 		this.scene = scene;
 
 		this.temporalResolveMix = 0.75;
-		this.clampRing = 1;
+		this.clampRadius = 1;
 		this.newSamplesSmoothing = 0.5;
 		this.newSamplesCorrection = 0.75;
 
 		this.fullscreenMaterial = new ComposeTemporalResolveMaterial();
 
-		this.fsQuad = new FullScreenQuad( this.fullscreenMaterial );
+		this.fsQuad = new FullScreenQuad(this.fullscreenMaterial);
 
 		this.renderTarget = new WebGLRenderTarget(
-			typeof window !== 'undefined' ? window.innerWidth : 2000,
-			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			typeof window !== "undefined" ? window.innerWidth : 2000,
+			typeof window !== "undefined" ? window.innerHeight : 1000,
 			{
 				minFilter: LinearFilter,
 				magFilter: LinearFilter,
@@ -32,13 +30,11 @@ export class TemporalResolve {
 
 		this.lastSize = { width: 0, height: 0 };
 
-		this.initNewCamera( camera );
-		this.initNewSize( window.innerWidth, window.innerHeight );
-
+		this.initNewCamera(camera);
+		this.initNewSize(window.innerWidth, window.innerHeight);
 	}
 
-	initNewCamera( camera ) {
-
+	initNewCamera(camera) {
 		this.activeCamera = camera;
 
 		this.temporalResolvePass = new TemporalResolvePass(
@@ -51,56 +47,44 @@ export class TemporalResolve {
 
 		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
 			this.temporalResolvePass.renderTarget.texture;
-
 	}
 
-	initNewSize( width, height ) {
-
+	initNewSize(width, height) {
 		this.lastSize.width = width;
 		this.lastSize.height = height;
 
-		this.temporalResolvePass.setSize( width, height );
-
+		this.temporalResolvePass.setSize(width, height);
 	}
 
 	get target() {
-
 		return this.renderTarget;
-
 	}
 
 	update() {
-
 		const renderer = this.ptRenderer._renderer;
 
 		const origRenderTarget = renderer.getRenderTarget();
 
 		const { camera } = this.ptRenderer;
-		if ( camera !== this.activeCamera ) {
-
-			this.initNewCamera( camera );
-
+		if (camera !== this.activeCamera) {
+			this.initNewCamera(camera);
 		}
 
 		const { width, height } = this.ptRenderer.target;
-		if ( width !== this.lastSize.width || height !== this.lastSize.height ) {
-
-			this.initNewSize( width, height );
-
+		if (width !== this.lastSize.width || height !== this.lastSize.height) {
+			this.initNewSize(width, height);
 		}
 
 		// ensure that the scene's objects' matrices are updated for the VelocityPass
 		this.scene.updateMatrixWorld();
 
-		this.scene.traverse( ( c ) => {
-
+		this.scene.traverse((c) => {
 			// update the modelViewMatrix which is used by the VelocityPass
 			c.modelViewMatrix.multiplyMatrices(
 				this.activeCamera.matrixWorldInverse,
 				c.matrixWorld
 			);
-
-		} );
+		});
 
 		// keep uniforms updated
 		this.temporalResolvePass.fullscreenMaterial.uniforms.samples.value =
@@ -109,8 +93,8 @@ export class TemporalResolve {
 		this.temporalResolvePass.fullscreenMaterial.uniforms.temporalResolveMix.value =
 			this.temporalResolveMix;
 
-		this.temporalResolvePass.fullscreenMaterial.uniforms.clampRing.value =
-			parseInt( this.clampRing );
+		this.temporalResolvePass.fullscreenMaterial.uniforms.clampRadius.value =
+			parseInt(this.clampRadius);
 
 		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesSmoothing.value =
 			this.newSamplesSmoothing;
@@ -118,13 +102,11 @@ export class TemporalResolve {
 		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesCorrection.value =
 			this.newSamplesCorrection;
 
-		this.temporalResolvePass.render( renderer );
+		this.temporalResolvePass.render(renderer);
 
-		renderer.setRenderTarget( this.renderTarget );
-		this.fsQuad.render( renderer );
-
-		renderer.setRenderTarget( origRenderTarget );
+		renderer.setRenderTarget(this.renderTarget);
+		this.fsQuad.render(renderer);
 
+		renderer.setRenderTarget(origRenderTarget);
 	}
-
 }
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index ee18b6698..70a02a7ce 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -1,13 +1,11 @@
-import { Matrix4, ShaderMaterial } from 'three';
-import { fragmentShader } from './shaders/temporalResolveFragment';
-import { vertexShader } from './shaders/temporalResolveVertex';
+import { Matrix4, ShaderMaterial } from "three";
+import { fragmentShader } from "./shaders/temporalResolveFragment";
+import { vertexShader } from "./shaders/temporalResolveVertex";
 
 export class TemporalResolveMaterial extends ShaderMaterial {
-
 	constructor() {
-
-		super( {
-			type: 'TemporalResolveMaterial',
+		super({
+			type: "TemporalResolveMaterial",
 			uniforms: {
 				inputTexture: { value: null },
 				samplesTexture: { value: null },
@@ -17,7 +15,7 @@ export class TemporalResolveMaterial extends ShaderMaterial {
 				lastDepthTexture: { value: null },
 				samples: { value: 0 },
 				temporalResolveMix: { value: 0 },
-				clampRing: { value: 0 },
+				clampRadius: { value: 0 },
 				newSamplesSmoothing: { value: 0 },
 				newSamplesCorrection: { value: 0 },
 				curInverseProjectionMatrix: { value: new Matrix4() },
@@ -29,8 +27,6 @@ export class TemporalResolveMaterial extends ShaderMaterial {
 			},
 			vertexShader,
 			fragmentShader,
-		} );
-
+		});
 	}
-
 }
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index ae756849f..e9831d842 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -9,7 +9,7 @@ uniform sampler2D lastDepthTexture;
 uniform float samples;
 
 uniform float temporalResolveMix;
-uniform int clampRing;
+uniform int clampRadius;
 uniform float newSamplesSmoothing;
 uniform float newSamplesCorrection;
 
@@ -95,7 +95,7 @@ void main() {
 		vec3 totalColor;
 
 		// use a small ring if there is a lot of movement otherwise there will be more smearing
-		int ring = movement > 1. ? 1 : clampRing;
+		int ring = movement > 1. ? 1 : clampRadius;
 		
 		for(int x = -ring; x <= ring; x++){
 			for(int y = -ring; y <= ring; y++){
From 77dfe49e6638135d11bf8eebc77326f35549ca91 Mon Sep 17 00:00:00 2001
From: 0beqz <000beqz@gmail.com>
Date: Thu, 28 Jul 2022 19:13:20 +0200
Subject: [PATCH 04/25] Update README.md
---
 README.md | 80 ++++++++++++++++++++++++++++---------------------------
 1 file changed, 41 insertions(+), 39 deletions(-)
diff --git a/README.md b/README.md
index f4efe4241..0ad02d415 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,14 @@ _More features and capabilities in progress!_
 
 [Ambient Occlusion Material](https://gkjohnson.github.io/three-gpu-pathtracer/example/bundle/aoRender.html)
 
+
 ## Running examples locally
 
-To run and modify the examples locally, make sure you have Node and NPM installed. Check the supported versions in [the test configuration](./.github/workflows/node.js.yml).
+To run and modify the examples locally, make sure you have Node and NPM installed.  Check the supported versions in [the test configuration](./.github/workflows/node.js.yml).
 
 In order to install dependencies, you will need `make` and a C++ compiler available.
 
-On Debian or Ubuntu, run `sudo apt install build-essential`. It should just work on MacOS.
+On Debian or Ubuntu, run `sudo apt install build-essential`.  It should just work on MacOS.
 
 - To install dependencies, run `npm install`
 - To start the demos run `npm start`
@@ -62,13 +63,13 @@ On Debian or Ubuntu, run `sudo apt install build-essential`. It should just work
 **Basic Renderer**
 
 ```js
-import * as THREE from "three";
-import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
+import * as THREE from 'three';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
 import {
 	PathTracingSceneGenerator,
 	PathTracingRenderer,
 	PhysicalPathTracingMaterial,
-} from "three-gpu-pathtracer";
+} from 'three-gpu-pathtracer';
 
 // init scene, renderer, camera, controls, etc
 
@@ -77,8 +78,8 @@ renderer.toneMapping = THREE.ACESFilmicToneMapping;
 
 // initialize the path tracing material and renderer
 const ptMaterial = new PhysicalPathTracingMaterial();
-const ptRenderer = new PathTracingRenderer(renderer);
-ptRenderer.setSize(window.innerWidth, window.innerHeight);
+const ptRenderer = new PathTracingRenderer( renderer );
+ptRenderer.setSize( window.innerWidth, window.innerHeight );
 ptRenderer.camera = camera;
 ptRenderer.material = ptMaterial;
 
@@ -86,46 +87,45 @@ ptRenderer.material = ptMaterial;
 ptRenderer.alpha = true;
 
 // init quad for rendering to the canvas
-const fsQuad = new FullScreenQuad(
-	new THREE.MeshBasicMaterial({
-		map: ptRenderer.target.texture,
+const fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
+	map: ptRenderer.target.texture,
 
-		// if rendering transparent background
-		blending: THREE.CustomBlending,
-	})
-);
+	// if rendering transparent background
+	blending: THREE.CustomBlending,
+} ) );
 
 // ensure scene matrices are up to date
 scene.updateMatrixWorld();
 
 // initialize the scene and update the material properties with the bvh, materials, etc
 const generator = new PathTracingSceneGenerator();
-const { bvh, textures, materials, lights } = generator.generate(scene);
+const { bvh, textures, materials, lights } = generator.generate( scene );
 
 // update bvh and geometry attribute textures
-ptMaterial.bvh.updateFrom(bvh);
-ptMaterial.normalAttribute.updateFrom(geometry.attributes.normal);
-ptMaterial.tangentAttribute.updateFrom(geometry.attributes.tangent);
-ptMaterial.uvAttribute.updateFrom(geometry.attributes.uv);
+ptMaterial.bvh.updateFrom( bvh );
+ptMaterial.normalAttribute.updateFrom( geometry.attributes.normal );
+ptMaterial.tangentAttribute.updateFrom( geometry.attributes.tangent );
+ptMaterial.uvAttribute.updateFrom( geometry.attributes.uv );
 
 // update materials and texture arrays
-ptMaterial.materialIndexAttribute.updateFrom(geometry.attributes.materialIndex);
-ptMaterial.textures.setTextures(renderer, 2048, 2048, textures);
-ptMaterial.materials.updateFrom(materials, textures);
+ptMaterial.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
+ptMaterial.textures.setTextures( renderer, 2048, 2048, textures );
+ptMaterial.materials.updateFrom( materials, textures );
 
 // update the lights
-ptMaterial.lights.updateFrom(lights);
+ptMaterial.lights.updateFrom( lights );
 ptMaterial.lightCount = lights.length;
 
 // set the environment map
-const texture = await new RGBELoader().loadAsync(envMapUrl);
-ptRenderer.material.envMapInfo.updateFrom(texture);
+const texture = await new RGBELoader().loadAsync( envMapUrl );
+ptRenderer.material.envMapInfo.updateFrom( texture );
 
 animate();
 
 // ...
 
 function animate() {
+
 	// if the camera position changes call "ptRenderer.reset()"
 
 	// update the camera and render one sample
@@ -137,7 +137,8 @@ function animate() {
 	fsQuad.material.map = ptRenderer.target.texture;
 
 	// copy the current state of the path tracer to canvas to display
-	fsQuad.render(renderer);
+	fsQuad.render( renderer );
+
 }
 ```
 
@@ -146,15 +147,16 @@ function animate() {
 Using a pre blurred envioronment map can help improve frame convergence time at the cost of sharp environment reflections. If performance is concern then multiple importance sampling can be disabled and blurred environment map used.
 
 ```js
-import { BlurredEnvMapGenerator } from "three-gpu-pathtracer";
+import { BlurredEnvMapGenerator } from 'three-gpu-pathtracer';
 
 // ...
 
-const envMap = await new RGBELoader().loadAsync(envMapUrl);
-const generator = new BlurredEnvMapGenerator(renderer);
-const blurredEnvMap = generator.generate(envMap, 0.35);
+const envMap = await new RGBELoader().loadAsync( envMapUrl );
+const generator = new BlurredEnvMapGenerator( renderer );
+const blurredEnvMap = generator.generate( envMap, 0.35 );
 
 // render!
+
 ```
 
 ## Dynamic Scenes
@@ -162,12 +164,12 @@ const blurredEnvMap = generator.generate(envMap, 0.35);
 Using the dynamic scene generator the same, frequently updated scene can be converted into a single reusable geometry multiple times and BVH refit which greatly improves subsequent scene updates. See `DynamicPathTracingSceneGenerator` docs for more info.
 
 ```js
-import { DynamicPathTracingSceneGenerator } from "three-gpu-pathtracer";
+import { DynamicPathTracingSceneGenerator } from 'three-gpu-pathtracer';
 
 // ... initialize scene etc
 
-const generator = new DynamicPathTracingSceneGenerator(scene);
-const { bvh, textures, materials } = generator.generate(scene);
+const generator = new DynamicPathTracingSceneGenerator( scene );
+const { bvh, textures, materials } = generator.generate( scene );
 
 // ... update path tracer and render
 ```
@@ -177,13 +179,13 @@ const { bvh, textures, materials } = generator.generate(scene);
 _NOTE WebWorker syntax is inconsistently supported across bundlers and sometimes not supported at all so the PathTracingSceneWorker class is not exported from the package root. If needed the code from src/worker can be copied and modified to accomodate a particular build process._
 
 ```js
-import { PathTracingSceneWorker } from "three-gpu-pathtracer/src/workers/PathTracingSceneWorker.js";
+import { PathTracingSceneWorker } from 'three-gpu-pathtracer/src/workers/PathTracingSceneWorker.js';
 
 // ...
 
 // initialize the scene and update the material properties with the bvh, materials, etc
 const generator = new PathTracingSceneWorker();
-const { bvh, textures, materials, lights } = await generator.generate(scene);
+const { bvh, textures, materials, lights } = await generator.generate( scene );
 
 // ...
 ```
@@ -267,7 +269,7 @@ Sets the size of the target to render to.
 ### .update
 
 ```js
-update();
+update()
 ```
 
 Renders a single sample to the target.
@@ -345,7 +347,7 @@ The fstop value of the camera. If this is changed then the `bokehSize` field is
 ### .bokehSize
 
 ```js
-bokehSize: Number;
+bokehSize : Number
 ```
 
 The bokeh size as derived from the fStop and focal length in millimeters. If this is set then the fStop is implicitly updated.
@@ -828,13 +830,11 @@ Set of randomness and other light transport utilities for use in a shader. See t
 
 
 
-
 
 Botanists Study model by riikkakilpelainen
 
 
 
-
 
 Japanese Bridge Garden model by kristenlee
 
@@ -846,3 +846,5 @@ Set of randomness and other light transport utilities for use in a shader. See t
 [PBR Book](https://pbr-book.org/)
 
 [knightcrawler25/GLSL-PathTracer](https://github.com/knightcrawler25/GLSL-PathTracer/)
+
+
From 80b5e85fcd228e5ba72f147748fa7740d55dbdfb Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 19:27:04 +0200
Subject: [PATCH 05/25] Add TemporalResolve to exports
---
 src/index.js | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/src/index.js b/src/index.js
index be7d87ad7..1c45e0690 100644
--- a/src/index.js
+++ b/src/index.js
@@ -32,3 +32,6 @@ export * from './materials/PhysicalPathTracingMaterial.js';
 export * from './shader/shaderMaterialSampling.js';
 export * from './shader/shaderUtils.js';
 export * from './shader/shaderStructs.js';
+
+// temporal resolve
+export * from './temporal-resolve/TemporalResolve';
From 3e57f8ee1f1fa125c1aaa5f4d244fa712828d5b9 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 19:33:49 +0200
Subject: [PATCH 06/25] Revert demos
---
 example/index.js        | 849 +++++++++++++++++++++++-----------------
 example/materialBall.js | 708 +++++++++++++++++----------------
 2 files changed, 845 insertions(+), 712 deletions(-)
diff --git a/example/index.js b/example/index.js
index 90d4a132c..5a0dc0642 100644
--- a/example/index.js
+++ b/example/index.js
@@ -18,65 +18,53 @@ import {
 	MeshBasicMaterial,
 	sRGBEncoding,
 	CustomBlending,
-	Matrix4,
-} from "three";
-import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
-import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
-import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
-import { LDrawLoader } from "three/examples/jsm/loaders/LDrawLoader.js";
-import { LDrawUtils } from "three/examples/jsm/utils/LDrawUtils.js";
-import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
-import Stats from "three/examples/jsm/libs/stats.module.js";
-import { generateRadialFloorTexture } from "./utils/generateRadialFloorTexture.js";
-import { PathTracingSceneWorker } from "../src/workers/PathTracingSceneWorker.js";
-import {
-	PhysicalPathTracingMaterial,
-	PathTracingRenderer,
-	MaterialReducer,
-	BlurredEnvMapGenerator,
-} from "../src/index.js";
-import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
-import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
-import { TemporalResolve } from "../src/temporal-resolve/TemporalResolve.js";
+	Matrix4
+} from 'three';
+import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
+import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { LDrawLoader } from 'three/examples/jsm/loaders/LDrawLoader.js';
+import { LDrawUtils } from 'three/examples/jsm/utils/LDrawUtils.js';
+import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
+import Stats from 'three/examples/jsm/libs/stats.module.js';
+import { generateRadialFloorTexture } from './utils/generateRadialFloorTexture.js';
+import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js';
+import { PhysicalPathTracingMaterial, PathTracingRenderer, MaterialReducer, BlurredEnvMapGenerator } from '../src/index.js';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
 
 const envMaps = {
-	"Royal Esplanade":
-		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr",
-	"Moonless Golf":
-		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/moonless_golf_1k.hdr",
-	Overpass:
-		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/pedestrian_overpass_1k.hdr",
-	"Venice Sunset":
-		"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/venice_sunset_1k.hdr",
-	"Small Studio":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/studio_small_05_1k.hdr",
-	"Pfalzer Forest":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/phalzer_forest_01_1k.hdr",
-	"Leadenhall Market":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/leadenhall_market_1k.hdr",
-	Kloppenheim:
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/kloppenheim_05_1k.hdr",
-	"Hilly Terrain":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/hilly_terrain_01_1k.hdr",
-	"Circus Arena":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/circus_arena_1k.hdr",
-	"Chinese Garden":
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/chinese_garden_1k.hdr",
-	Autoshop:
-		"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/autoshop_01_1k.hdr",
+	'Royal Esplanade': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr',
+	'Moonless Golf': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/moonless_golf_1k.hdr',
+	'Overpass': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/pedestrian_overpass_1k.hdr',
+	'Venice Sunset': 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/venice_sunset_1k.hdr',
+	'Small Studio': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/studio_small_05_1k.hdr',
+	'Pfalzer Forest': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/phalzer_forest_01_1k.hdr',
+	'Leadenhall Market': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/leadenhall_market_1k.hdr',
+	'Kloppenheim': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/kloppenheim_05_1k.hdr',
+	'Hilly Terrain': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/hilly_terrain_01_1k.hdr',
+	'Circus Arena': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/circus_arena_1k.hdr',
+	'Chinese Garden': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/chinese_garden_1k.hdr',
+	'Autoshop': 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/autoshop_01_1k.hdr',
 };
 
 const models = window.MODEL_LIST || {};
 
-let initialModel = Object.keys(models)[0];
-if (window.location.hash) {
-	const modelName = window.location.hash.substring(1).replaceAll("%20", " ");
-	if (modelName in models) {
+let initialModel = Object.keys( models )[ 0 ];
+if ( window.location.hash ) {
+
+	const modelName = window.location.hash.substring( 1 ).replaceAll( '%20', ' ' );
+	if ( modelName in models ) {
+
 		initialModel = modelName;
+
 	}
+
 }
 
 const params = {
+
 	multipleImportanceSampling: true,
 	acesToneMapping: true,
 	resolutionScale: 1 / window.devicePixelRatio,
@@ -92,20 +80,20 @@ const params = {
 
 	model: initialModel,
 
-	envMap: envMaps["Royal Esplanade"],
+	envMap: envMaps[ 'Royal Esplanade' ],
 
-	gradientTop: "#bfd8ff",
-	gradientBottom: "#ffffff",
+	gradientTop: '#bfd8ff',
+	gradientBottom: '#ffffff',
 
 	environmentIntensity: 3.0,
 	environmentBlur: 0.0,
 	environmentRotation: 0,
 
-	cameraProjection: "Perspective",
+	cameraProjection: 'Perspective',
 
-	backgroundType: "Gradient",
-	bgGradientTop: "#111111",
-	bgGradientBottom: "#000000",
+	backgroundType: 'Gradient',
+	bgGradientTop: '#111111',
+	bgGradientBottom: '#000000',
 	backgroundAlpha: 1.0,
 	checkerboardTransparency: true,
 
@@ -113,10 +101,11 @@ const params = {
 	bounces: 3,
 	pause: false,
 
-	floorColor: "#080808",
+	floorColor: '#080808',
 	floorOpacity: 1.0,
 	floorRoughness: 0.1,
-	floorMetalness: 0.0,
+	floorMetalness: 0.0
+
 };
 
 let creditEl, loadingEl, samplesEl;
@@ -132,164 +121,176 @@ const orthoWidth = 2;
 init();
 
 async function init() {
-	creditEl = document.getElementById("credits");
-	loadingEl = document.getElementById("loading");
-	samplesEl = document.getElementById("samples");
 
-	renderer = new WebGLRenderer({ antialias: true });
+	creditEl = document.getElementById( 'credits' );
+	loadingEl = document.getElementById( 'loading' );
+	samplesEl = document.getElementById( 'samples' );
+
+	renderer = new WebGLRenderer( { antialias: true } );
 	renderer.outputEncoding = sRGBEncoding;
 	renderer.toneMapping = ACESFilmicToneMapping;
-	document.body.appendChild(renderer.domElement);
+	document.body.appendChild( renderer.domElement );
 
 	scene = new Scene();
 
 	const aspect = window.innerWidth / window.innerHeight;
-	perspectiveCamera = new PerspectiveCamera(60, aspect, 0.025, 500);
-	perspectiveCamera.position.set(-1, 0.25, 1);
+	perspectiveCamera = new PerspectiveCamera( 60, aspect, 0.025, 500 );
+	perspectiveCamera.position.set( - 1, 0.25, 1 );
 
 	const orthoHeight = orthoWidth / aspect;
-	orthoCamera = new OrthographicCamera(
-		orthoWidth / -2,
-		orthoWidth / 2,
-		orthoHeight / 2,
-		orthoHeight / -2,
-		0,
-		100
-	);
-	orthoCamera.position.set(-1, 0.25, 1);
+	orthoCamera = new OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
+	orthoCamera.position.set( - 1, 0.25, 1 );
 
-	ptRenderer = new PathTracingRenderer(renderer);
+	ptRenderer = new PathTracingRenderer( renderer );
 	ptRenderer.alpha = true;
 	ptRenderer.material = new PhysicalPathTracingMaterial();
-	ptRenderer.tiles.set(params.tiles, params.tiles);
-	ptRenderer.material.setDefine("FEATURE_GRADIENT_BG", 1);
-	ptRenderer.material.setDefine(
-		"FEATURE_MIS",
-		Number(params.multipleImportanceSampling)
-	);
-	ptRenderer.material.bgGradientTop.set(params.bgGradientTop);
-	ptRenderer.material.bgGradientBottom.set(params.bgGradientBottom);
+	ptRenderer.tiles.set( params.tiles, params.tiles );
+	ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', 1 );
+	ptRenderer.material.setDefine( 'FEATURE_MIS', Number( params.multipleImportanceSampling ) );
+	ptRenderer.material.bgGradientTop.set( params.bgGradientTop );
+	ptRenderer.material.bgGradientBottom.set( params.bgGradientBottom );
 
-	temporalResolve = new TemporalResolve(ptRenderer, scene, activeCamera);
+	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
 	temporalResolve.temporalResolveMix = 0.75;
 	temporalResolve.clampRadius = 1;
 	temporalResolve.newSamplesSmoothing = 0.5;
 	temporalResolve.newSamplesCorrection = 0.75;
 
-	fsQuad = new FullScreenQuad(
-		new MeshBasicMaterial({
-			map: ptRenderer.target.texture,
-			blending: CustomBlending,
-		})
-	);
+	fsQuad = new FullScreenQuad( new MeshBasicMaterial( {
+		map: ptRenderer.target.texture,
+		blending: CustomBlending
+	} ) );
 
-	controls = new OrbitControls(perspectiveCamera, renderer.domElement);
-	controls.addEventListener("change", resetRenderer);
+	controls = new OrbitControls( perspectiveCamera, renderer.domElement );
+	controls.addEventListener( 'change', resetRenderer );
 
-	envMapGenerator = new BlurredEnvMapGenerator(renderer);
+	envMapGenerator = new BlurredEnvMapGenerator( renderer );
 
-	const floorTex = generateRadialFloorTexture(2048);
+	const floorTex = generateRadialFloorTexture( 2048 );
 	floorPlane = new Mesh(
 		new PlaneBufferGeometry(),
-		new MeshStandardMaterial({
+		new MeshStandardMaterial( {
 			map: floorTex,
 			transparent: true,
 			color: 0x080808,
 			roughness: 0.1,
-			metalness: 0.0,
-		})
+			metalness: 0.0
+		} )
 	);
-	floorPlane.scale.setScalar(3);
-	floorPlane.rotation.x = -Math.PI / 2;
+	floorPlane.scale.setScalar( 3 );
+	floorPlane.rotation.x = - Math.PI / 2;
 
 	stats = new Stats();
-	document.body.appendChild(stats.dom);
+	document.body.appendChild( stats.dom );
 	renderer.physicallyCorrectLights = true;
 	renderer.toneMapping = ACESFilmicToneMapping;
-	ptRenderer.material.setDefine("FEATURE_GRADIENT_BG", 1);
-	scene.background = new Color(0x060606);
-	ptRenderer.tiles.set(params.tilesX, params.tilesY);
+	ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', 1 );
+	scene.background = new Color( 0x060606 );
+	ptRenderer.tiles.set( params.tilesX, params.tilesY );
 
-	updateCamera(params.cameraProjection);
+	updateCamera( params.cameraProjection );
 	updateModel();
 	updateEnvMap();
 	onResize();
 
 	animate();
 
-	window.addEventListener("resize", onResize);
+	window.addEventListener( 'resize', onResize );
+
 }
 
 function animate() {
-	requestAnimationFrame(animate);
+
+	requestAnimationFrame( animate );
 
 	stats.update();
 
-	if (loadingModel) {
+	if ( loadingModel ) {
+
 		return;
+
 	}
 
-	if (ptRenderer.samples < 1.0 || !params.enable) {
-		renderer.render(scene, activeCamera);
+	if ( ptRenderer.samples < 1.0 || ! params.enable ) {
+
+		renderer.render( scene, activeCamera );
+
 	}
 
-	if (params.enable && delaySamples === 0) {
-		const samples = Math.floor(ptRenderer.samples);
-		samplesEl.innerText = `samples: ${samples}`;
+	if ( params.enable && delaySamples === 0 ) {
+
+		const samples = Math.floor( ptRenderer.samples );
+		samplesEl.innerText = `samples: ${ samples }`;
 
-		ptRenderer.material.materials.updateFrom(
-			sceneInfo.materials,
-			sceneInfo.textures
-		);
+		ptRenderer.material.materials.updateFrom( sceneInfo.materials, sceneInfo.textures );
 		ptRenderer.material.filterGlossyFactor = 0.5;
 		ptRenderer.material.environmentIntensity = params.environmentIntensity;
 		ptRenderer.material.bounces = params.bounces;
-		ptRenderer.material.physicalCamera.updateFrom(activeCamera);
+		ptRenderer.material.physicalCamera.updateFrom( activeCamera );
 
 		activeCamera.updateMatrixWorld();
 
-		if (!params.pause || ptRenderer.samples < 1) {
-			for (let i = 0, l = params.samplesPerFrame; i < l; i++) {
+
+
+		if ( ! params.pause || ptRenderer.samples < 1 ) {
+
+			for ( let i = 0, l = params.samplesPerFrame; i < l; i ++ ) {
+
 				ptRenderer.update();
+
 			}
+
 		}
 
 		renderer.autoClear = false;
-		if (params.temporalResolve) {
+		if ( params.temporalResolve ) {
+
 			temporalResolve.update();
 			fsQuad.material.map = temporalResolve.target.texture;
+
 		} else {
+
 			fsQuad.material.map = ptRenderer.target.texture;
+
 		}
 
-		fsQuad.render(renderer);
+		fsQuad.render( renderer );
 		renderer.autoClear = true;
-	} else if (delaySamples > 0) {
-		delaySamples--;
+
+	} else if ( delaySamples > 0 ) {
+
+		delaySamples --;
+
 	}
 
-	samplesEl.innerText = `Samples: ${Math.floor(ptRenderer.samples)}`;
+	samplesEl.innerText = `Samples: ${ Math.floor( ptRenderer.samples ) }`;
+
 }
 
 function resetRenderer() {
-	if (params.tilesX * params.tilesY !== 1.0) {
+
+	if ( params.tilesX * params.tilesY !== 1.0 ) {
+
 		delaySamples = 1;
+
 	}
 
 	ptRenderer.reset();
+
 }
 
 function onResize() {
+
 	const w = window.innerWidth;
 	const h = window.innerHeight;
 	const scale = params.resolutionScale;
 	const dpr = window.devicePixelRatio;
 
-	ptRenderer.setSize(w * scale * dpr, h * scale * dpr);
+	ptRenderer.setSize( w * scale * dpr, h * scale * dpr );
 	ptRenderer.reset();
 
-	renderer.setSize(w, h);
-	renderer.setPixelRatio(window.devicePixelRatio * scale);
+	renderer.setSize( w, h );
+	renderer.setPixelRatio( window.devicePixelRatio * scale );
 
 	const aspect = w / h;
 	perspectiveCamera.aspect = aspect;
@@ -297,188 +298,230 @@ function onResize() {
 
 	const orthoHeight = orthoWidth / aspect;
 	orthoCamera.top = orthoHeight / 2;
-	orthoCamera.bottom = orthoHeight / -2;
+	orthoCamera.bottom = orthoHeight / - 2;
 	orthoCamera.updateProjectionMatrix();
+
 }
 
 function buildGui() {
-	if (gui) {
+
+	if ( gui ) {
+
 		gui.destroy();
+
 	}
 
 	gui = new GUI();
 
-	gui.add(params, "model", Object.keys(models)).onChange(updateModel);
+	gui.add( params, 'model', Object.keys( models ) ).onChange( updateModel );
 
-	const pathTracingFolder = gui.addFolder("path tracing");
-	pathTracingFolder.add(params, "enable");
-	pathTracingFolder.add(params, "pause");
-	pathTracingFolder.add(params, "multipleImportanceSampling").onChange((v) => {
-		ptRenderer.material.setDefine("FEATURE_MIS", Number(v));
+	const pathTracingFolder = gui.addFolder( 'path tracing' );
+	pathTracingFolder.add( params, 'enable' );
+	pathTracingFolder.add( params, 'pause' );
+	pathTracingFolder.add( params, 'multipleImportanceSampling' ).onChange( v => {
+
+		ptRenderer.material.setDefine( 'FEATURE_MIS', Number( v ) );
 		ptRenderer.reset();
-	});
-	pathTracingFolder.add(params, "acesToneMapping").onChange((v) => {
+
+	} );
+	pathTracingFolder.add( params, 'acesToneMapping' ).onChange( v => {
+
 		renderer.toneMapping = v ? ACESFilmicToneMapping : NoToneMapping;
-	});
-	pathTracingFolder.add(params, "bounces", 1, 20, 1).onChange(() => {
+
+	} );
+	pathTracingFolder.add( params, 'bounces', 1, 20, 1 ).onChange( () => {
+
 		ptRenderer.reset();
-	});
 
-	const trFolder = gui.addFolder("Temporal Resolve");
-	trFolder.add(params, "temporalResolve");
+	} );
+
+	const trFolder = gui.addFolder( 'Temporal Resolve' );
+	trFolder.add( params, 'temporalResolve' );
 	trFolder
-		.add(params, "temporalResolveMix", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.temporalResolveMix = value));
+		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
 	trFolder
-		.add(params, "clampRadius", 1, 8, 1)
-		.onChange((value) => (temporalResolve.clampRadius = value));
+		.add( params, 'clampRadius', 1, 8, 1 )
+		.onChange( ( value ) => ( temporalResolve.clampRadius = value ) );
 	trFolder
-		.add(params, "newSamplesSmoothing", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.newSamplesSmoothing = value));
+		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
 	trFolder
-		.add(params, "newSamplesCorrection", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.newSamplesCorrection = value));
-
-	const resolutionFolder = gui.addFolder("resolution");
-	resolutionFolder
-		.add(params, "resolutionScale", 0.1, 1.0, 0.01)
-		.onChange(() => {
-			onResize();
-		});
-	resolutionFolder.add(params, "samplesPerFrame", 1, 10, 1);
-	resolutionFolder.add(params, "tilesX", 1, 10, 1).onChange((v) => {
+		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+
+	const resolutionFolder = gui.addFolder( 'resolution' );
+	resolutionFolder.add( params, 'resolutionScale', 0.1, 1.0, 0.01 ).onChange( () => {
+
+		onResize();
+
+	} );
+	resolutionFolder.add( params, 'samplesPerFrame', 1, 10, 1 );
+	resolutionFolder.add( params, 'tilesX', 1, 10, 1 ).onChange( v => {
+
 		ptRenderer.tiles.x = v;
-	});
-	resolutionFolder.add(params, "tilesY", 1, 10, 1).onChange((v) => {
+
+	} );
+	resolutionFolder.add( params, 'tilesY', 1, 10, 1 ).onChange( v => {
+
 		ptRenderer.tiles.y = v;
-	});
-	resolutionFolder
-		.add(params, "cameraProjection", ["Perspective", "Orthographic"])
-		.onChange((v) => {
-			updateCamera(v);
-		});
+
+	} );
+	resolutionFolder.add( params, 'cameraProjection', [ 'Perspective', 'Orthographic' ] ).onChange( v => {
+
+		updateCamera( v );
+
+	} );
 	resolutionFolder.open();
 
-	const environmentFolder = gui.addFolder("environment");
-	environmentFolder
-		.add(params, "envMap", envMaps)
-		.name("map")
-		.onChange(updateEnvMap);
-	environmentFolder
-		.add(params, "environmentBlur", 0.0, 1.0)
-		.onChange(() => {
-			updateEnvBlur();
-			ptRenderer.reset();
-		})
-		.name("env map blur");
-	environmentFolder
-		.add(params, "environmentIntensity", 0.0, 10.0)
-		.onChange(() => {
-			ptRenderer.reset();
-		})
-		.name("intensity");
-	environmentFolder
-		.add(params, "environmentRotation", 0, 2 * Math.PI)
-		.onChange((v) => {
-			ptRenderer.material.environmentRotation.setFromMatrix4(
-				new Matrix4().makeRotationY(v)
-			);
-			ptRenderer.reset();
-		});
+	const environmentFolder = gui.addFolder( 'environment' );
+	environmentFolder.add( params, 'envMap', envMaps ).name( 'map' ).onChange( updateEnvMap );
+	environmentFolder.add( params, 'environmentBlur', 0.0, 1.0 ).onChange( () => {
+
+		updateEnvBlur();
+		ptRenderer.reset();
+
+	} ).name( 'env map blur' );
+	environmentFolder.add( params, 'environmentIntensity', 0.0, 10.0 ).onChange( () => {
+
+		ptRenderer.reset();
+
+	} ).name( 'intensity' );
+	environmentFolder.add( params, 'environmentRotation', 0, 2 * Math.PI ).onChange( v => {
+
+		ptRenderer.material.environmentRotation.setFromMatrix4( new Matrix4().makeRotationY( v ) );
+		ptRenderer.reset();
+
+	} );
 	environmentFolder.open();
 
-	const backgroundFolder = gui.addFolder("background");
-	backgroundFolder
-		.add(params, "backgroundType", ["Environment", "Gradient"])
-		.onChange((v) => {
-			ptRenderer.material.setDefine(
-				"FEATURE_GRADIENT_BG",
-				Number(v === "Gradient")
-			);
-			if (v === "Gradient") {
-				scene.background = new Color(0x060606);
-			} else {
-				scene.background = scene.environment;
-			}
+	const backgroundFolder = gui.addFolder( 'background' );
+	backgroundFolder.add( params, 'backgroundType', [ 'Environment', 'Gradient' ] ).onChange( v => {
+
+		ptRenderer.material.setDefine( 'FEATURE_GRADIENT_BG', Number( v === 'Gradient' ) );
+		if ( v === 'Gradient' ) {
+
+			scene.background = new Color( 0x060606 );
+
+		} else {
+
+			scene.background = scene.environment;
+
+		}
 
-			ptRenderer.reset();
-		});
-	backgroundFolder.addColor(params, "bgGradientTop").onChange((v) => {
-		ptRenderer.material.bgGradientTop.set(v);
 		ptRenderer.reset();
-	});
-	backgroundFolder.addColor(params, "bgGradientBottom").onChange((v) => {
-		ptRenderer.material.bgGradientBottom.set(v);
+
+	} );
+	backgroundFolder.addColor( params, 'bgGradientTop' ).onChange( v => {
+
+		ptRenderer.material.bgGradientTop.set( v );
 		ptRenderer.reset();
-	});
-	backgroundFolder.add(params, "backgroundAlpha", 0, 1).onChange((v) => {
+
+	} );
+	backgroundFolder.addColor( params, 'bgGradientBottom' ).onChange( v => {
+
+		ptRenderer.material.bgGradientBottom.set( v );
+		ptRenderer.reset();
+
+	} );
+	backgroundFolder.add( params, 'backgroundAlpha', 0, 1 ).onChange( v => {
+
 		ptRenderer.material.backgroundAlpha = v;
 		ptRenderer.reset();
-	});
-	backgroundFolder.add(params, "checkerboardTransparency").onChange((v) => {
-		if (v) document.body.classList.add("checkerboard");
-		else document.body.classList.remove("checkerboard");
-	});
-
-	const floorFolder = gui.addFolder("floor");
-	floorFolder.addColor(params, "floorColor").onChange((v) => {
-		floorPlane.material.color.set(v);
+
+	} );
+	backgroundFolder.add( params, 'checkerboardTransparency' ).onChange( v => {
+
+		if ( v ) document.body.classList.add( 'checkerboard' );
+		else document.body.classList.remove( 'checkerboard' );
+
+	} );
+
+	const floorFolder = gui.addFolder( 'floor' );
+	floorFolder.addColor( params, 'floorColor' ).onChange( v => {
+
+		floorPlane.material.color.set( v );
 		ptRenderer.reset();
-	});
-	floorFolder.add(params, "floorRoughness", 0, 1).onChange((v) => {
+
+	} );
+	floorFolder.add( params, 'floorRoughness', 0, 1 ).onChange( v => {
+
 		floorPlane.material.roughness = v;
 		ptRenderer.reset();
-	});
-	floorFolder.add(params, "floorMetalness", 0, 1).onChange((v) => {
+
+	} );
+	floorFolder.add( params, 'floorMetalness', 0, 1 ).onChange( v => {
+
 		floorPlane.material.metalness = v;
 		ptRenderer.reset();
-	});
-	floorFolder.add(params, "floorOpacity", 0, 1).onChange((v) => {
+
+	} );
+	floorFolder.add( params, 'floorOpacity', 0, 1 ).onChange( v => {
+
 		floorPlane.material.opacity = v;
 		ptRenderer.reset();
-	});
+
+	} );
 	floorFolder.close();
+
 }
 
 function updateEnvMap() {
-	new RGBELoader().load(params.envMap, (texture) => {
-		if (scene.environmentMap) {
-			scene.environment.dispose();
-			envMap.dispose();
-		}
 
-		envMap = texture;
-		updateEnvBlur();
-		ptRenderer.reset();
-	});
+	new RGBELoader()
+		.load( params.envMap, texture => {
+
+			if ( scene.environmentMap ) {
+
+				scene.environment.dispose();
+				envMap.dispose();
+
+			}
+
+			envMap = texture;
+			updateEnvBlur();
+			ptRenderer.reset();
+
+		} );
+
 }
 
 function updateEnvBlur() {
-	const blurredEnvMap = envMapGenerator.generate(
-		envMap,
-		params.environmentBlur
-	);
-	ptRenderer.material.envMapInfo.updateFrom(blurredEnvMap);
+
+	const blurredEnvMap = envMapGenerator.generate( envMap, params.environmentBlur );
+	ptRenderer.material.envMapInfo.updateFrom( blurredEnvMap );
 
 	scene.environment = blurredEnvMap;
-	if (params.backgroundType !== "Gradient") {
+	if ( params.backgroundType !== 'Gradient' ) {
+
 		scene.background = blurredEnvMap;
+
 	}
+
 }
 
-function updateCamera(cameraProjection) {
-	if (cameraProjection === "Perspective") {
-		if (activeCamera) {
-			perspectiveCamera.position.copy(activeCamera.position);
+function updateCamera( cameraProjection ) {
+
+	if ( cameraProjection === 'Perspective' ) {
+
+		if ( activeCamera ) {
+
+			perspectiveCamera.position.copy( activeCamera.position );
+
 		}
 
 		activeCamera = perspectiveCamera;
+
 	} else {
-		if (activeCamera) {
-			orthoCamera.position.copy(activeCamera.position);
+
+		if ( activeCamera ) {
+
+			orthoCamera.position.copy( activeCamera.position );
+
 		}
 
 		activeCamera = orthoCamera;
+
 	}
 
 	controls.object = activeCamera;
@@ -487,212 +530,284 @@ function updateCamera(cameraProjection) {
 	controls.update();
 
 	resetRenderer();
+
 }
 
-function convertOpacityToTransmission(model) {
-	model.traverse((c) => {
-		if (c.material) {
+function convertOpacityToTransmission( model ) {
+
+	model.traverse( c => {
+
+		if ( c.material ) {
+
 			const material = c.material;
-			if (material.opacity < 0.65 && material.opacity > 0.2) {
+			if ( material.opacity < 0.65 && material.opacity > 0.2 ) {
+
 				const newMaterial = new MeshPhysicalMaterial();
-				for (const key in material) {
-					if (key in material) {
-						if (material[key] === null) {
+				for ( const key in material ) {
+
+					if ( key in material ) {
+
+						if ( material[ key ] === null ) {
+
 							continue;
+
 						}
 
-						if (material[key].isTexture) {
-							newMaterial[key] = material[key];
-						} else if (
-							material[key].copy &&
-							material[key].constructor === newMaterial[key].constructor
-						) {
-							newMaterial[key].copy(material[key]);
-						} else if (typeof material[key] === "number") {
-							newMaterial[key] = material[key];
+						if ( material[ key ].isTexture ) {
+
+							newMaterial[ key ] = material[ key ];
+
+						} else if ( material[ key ].copy && material[ key ].constructor === newMaterial[ key ].constructor ) {
+
+							newMaterial[ key ].copy( material[ key ] );
+
+						} else if ( ( typeof material[ key ] ) === 'number' ) {
+
+							newMaterial[ key ] = material[ key ];
+
 						}
+
 					}
+
 				}
 
 				newMaterial.opacity = 1.0;
 				newMaterial.transmission = 1.0;
 				c.material = newMaterial;
+
 			}
+
 		}
-	});
+
+	} );
+
 }
 
 async function updateModel() {
-	if (gui) {
-		document.body.classList.remove("checkerboard");
+
+	if ( gui ) {
+
+		document.body.classList.remove( 'checkerboard' );
 		gui.destroy();
 		gui = null;
+
 	}
 
 	let model;
 	const manager = new LoadingManager();
-	const modelInfo = models[params.model];
+	const modelInfo = models[ params.model ];
 
 	loadingModel = true;
-	renderer.domElement.style.visibility = "hidden";
-	samplesEl.innerText = "--";
-	creditEl.innerText = "--";
-	loadingEl.innerText = "Loading";
-	loadingEl.style.visibility = "visible";
-
-	scene.traverse((c) => {
-		if (c.material) {
+	renderer.domElement.style.visibility = 'hidden';
+	samplesEl.innerText = '--';
+	creditEl.innerText = '--';
+	loadingEl.innerText = 'Loading';
+	loadingEl.style.visibility = 'visible';
+
+	scene.traverse( c => {
+
+		if ( c.material ) {
+
 			const material = c.material;
-			for (const key in material) {
-				if (material[key] && material[key].isTexture) {
-					material[key].dispose();
+			for ( const key in material ) {
+
+				if ( material[ key ] && material[ key ].isTexture ) {
+
+					material[ key ].dispose();
+
 				}
+
 			}
+
 		}
-	});
 
-	if (sceneInfo) {
-		scene.remove(sceneInfo.scene);
+	} );
+
+	if ( sceneInfo ) {
+
+		scene.remove( sceneInfo.scene );
+
 	}
 
+
 	const onFinish = async () => {
-		if (modelInfo.removeEmission) {
-			model.traverse((c) => {
-				if (c.material) {
+
+		if ( modelInfo.removeEmission ) {
+
+			model.traverse( c => {
+
+				if ( c.material ) {
+
 					c.material.emissiveMap = null;
 					c.material.emissiveIntensity = 0;
+
 				}
-			});
+
+			} );
+
 		}
 
-		if (modelInfo.opacityToTransmission) {
-			convertOpacityToTransmission(model);
+		if ( modelInfo.opacityToTransmission ) {
+
+			convertOpacityToTransmission( model );
+
 		}
 
-		model.traverse((c) => {
-			if (c.material) {
+		model.traverse( c => {
+
+			if ( c.material ) {
+
 				c.material.side = DoubleSide;
+
 			}
-		});
 
-		if (modelInfo.postProcess) {
-			modelInfo.postProcess(model);
+		} );
+
+		if ( modelInfo.postProcess ) {
+
+			modelInfo.postProcess( model );
+
 		}
 
 		// rotate model after so it doesn't affect the bounding sphere scale
-		if (modelInfo.rotation) {
-			model.rotation.set(...modelInfo.rotation);
+		if ( modelInfo.rotation ) {
+
+			model.rotation.set( ...modelInfo.rotation );
+
 		}
 
 		// center the model
 		const box = new Box3();
-		box.setFromObject(model);
+		box.setFromObject( model );
 		model.position
-			.addScaledVector(box.min, -0.5)
-			.addScaledVector(box.max, -0.5);
+			.addScaledVector( box.min, - 0.5 )
+			.addScaledVector( box.max, - 0.5 );
 
 		const sphere = new Sphere();
-		box.getBoundingSphere(sphere);
+		box.getBoundingSphere( sphere );
 
-		model.scale.setScalar(1 / sphere.radius);
-		model.position.multiplyScalar(1 / sphere.radius);
+		model.scale.setScalar( 1 / sphere.radius );
+		model.position.multiplyScalar( 1 / sphere.radius );
 
-		box.setFromObject(model);
+		box.setFromObject( model );
 
 		model.updateMatrixWorld();
 
 		const group = new Group();
 		floorPlane.position.y = box.min.y;
-		group.add(model, floorPlane);
+		group.add( model, floorPlane );
 
 		const reducer = new MaterialReducer();
-		reducer.process(group);
+		reducer.process( group );
 
 		const generator = new PathTracingSceneWorker();
-		const result = await generator.generate(group, {
-			onProgress: (v) => {
-				const percent = Math.floor(100 * v);
-				loadingEl.innerText = `Building BVH : ${percent}%`;
-			},
-		});
+		const result = await generator.generate( group, { onProgress: v => {
+
+			const percent = Math.floor( 100 * v );
+			loadingEl.innerText = `Building BVH : ${ percent }%`;
+
+		} } );
 
 		sceneInfo = result;
-		scene.add(sceneInfo.scene);
+		scene.add( sceneInfo.scene );
 
 		const { bvh, textures, materials } = result;
 		const geometry = bvh.geometry;
 		const material = ptRenderer.material;
 
-		material.bvh.updateFrom(bvh);
-		material.normalAttribute.updateFrom(geometry.attributes.normal);
-		material.tangentAttribute.updateFrom(geometry.attributes.tangent);
-		material.uvAttribute.updateFrom(geometry.attributes.uv);
-		material.materialIndexAttribute.updateFrom(
-			geometry.attributes.materialIndex
-		);
-		material.textures.setTextures(renderer, 2048, 2048, textures);
-		material.materials.updateFrom(materials, textures);
+		material.bvh.updateFrom( bvh );
+		material.normalAttribute.updateFrom( geometry.attributes.normal );
+		material.tangentAttribute.updateFrom( geometry.attributes.tangent );
+		material.uvAttribute.updateFrom( geometry.attributes.uv );
+		material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
+		material.textures.setTextures( renderer, 2048, 2048, textures );
+		material.materials.updateFrom( materials, textures );
 
 		generator.dispose();
 
-		loadingEl.style.visibility = "hidden";
+		loadingEl.style.visibility = 'hidden';
 
-		creditEl.innerHTML = modelInfo.credit || "";
-		creditEl.style.visibility = modelInfo.credit ? "visible" : "hidden";
+		creditEl.innerHTML = modelInfo.credit || '';
+		creditEl.style.visibility = modelInfo.credit ? 'visible' : 'hidden';
 		params.bounces = modelInfo.bounces || 3;
 		buildGui();
 
 		loadingModel = false;
-		renderer.domElement.style.visibility = "visible";
-		if (params.checkerboardTransparency) {
-			document.body.classList.add("checkerboard");
+		renderer.domElement.style.visibility = 'visible';
+		if ( params.checkerboardTransparency ) {
+
+			document.body.classList.add( 'checkerboard' );
+
 		}
 
 		ptRenderer.reset();
+
 	};
 
 	const url = modelInfo.url;
-	if (/(gltf|glb)$/i.test(url)) {
+	if ( /(gltf|glb)$/i.test( url ) ) {
+
 		manager.onLoad = onFinish;
-		new GLTFLoader(manager).setMeshoptDecoder(MeshoptDecoder).load(
-			url,
-			(gltf) => {
-				model = gltf.scene;
-			},
-			(progress) => {
-				if (progress.total !== 0 && progress.total >= progress.loaded) {
-					const percent = Math.floor((100 * progress.loaded) / progress.total);
-					loadingEl.innerText = `Loading : ${percent}%`;
-				}
-			}
-		);
-	} else if (/mpd$/i.test(url)) {
-		manager.onProgress = (url, loaded, total) => {
-			const percent = Math.floor((100 * loaded) / total);
-			loadingEl.innerText = `Loading : ${percent}%`;
+		new GLTFLoader( manager )
+			.setMeshoptDecoder( MeshoptDecoder )
+			.load(
+				url,
+				gltf => {
+
+					model = gltf.scene;
+
+				},
+				progress => {
+
+					if ( progress.total !== 0 && progress.total >= progress.loaded ) {
+
+						const percent = Math.floor( 100 * progress.loaded / progress.total );
+						loadingEl.innerText = `Loading : ${ percent }%`;
+
+					}
+
+				},
+			);
+
+	} else if ( /mpd$/i.test( url ) ) {
+
+		manager.onProgress = ( url, loaded, total ) => {
+
+			const percent = Math.floor( 100 * loaded / total );
+			loadingEl.innerText = `Loading : ${ percent }%`;
+
 		};
 
-		const loader = new LDrawLoader(manager);
-		await loader.preloadMaterials(
-			"https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/colors/ldcfgalt.ldr"
-		);
+		const loader = new LDrawLoader( manager );
+		await loader.preloadMaterials( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/colors/ldcfgalt.ldr' );
 		loader
-			.setPartsLibraryPath(
-				"https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/complete/ldraw/"
-			)
-			.load(url, (result) => {
-				model = LDrawUtils.mergeObject(result);
-				model.rotation.set(Math.PI, 0, 0);
-				model.traverse((c) => {
-					if (c.isLineSegments) {
-						c.visible = false;
-					}
+			.setPartsLibraryPath( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/complete/ldraw/' )
+			.load(
+				url,
+				result => {
+
+					model = LDrawUtils.mergeObject( result );
+					model.rotation.set( Math.PI, 0, 0 );
+					model.traverse( c => {
+
+						if ( c.isLineSegments ) {
+
+							c.visible = false;
+
+						}
+
+						if ( c.isMesh ) {
+
+							c.material.roughness *= 0.01;
+
+						}
+
+					} );
+					onFinish();
+
+				},
+			);
 
-					if (c.isMesh) {
-						c.material.roughness *= 0.01;
-					}
-				});
-				onFinish();
-			});
 	}
+
 }
diff --git a/example/materialBall.js b/example/materialBall.js
index cba1106dd..e34ad0d17 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -1,28 +1,15 @@
-import * as THREE from "three";
-import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass.js";
-import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
-import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
-import {
-	PathTracingRenderer,
-	PhysicalPathTracingMaterial,
-	PhysicalCamera,
-	BlurredEnvMapGenerator,
-	EquirectCamera,
-} from "../src/index.js";
-import { PathTracingSceneWorker } from "../src/workers/PathTracingSceneWorker.js";
-import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
-import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
-import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
-import { TemporalResolve } from "../src/temporal-resolve/TemporalResolve.js";
-
-let renderer,
-	controls,
-	sceneInfo,
-	ptRenderer,
-	activeCamera,
-	fsQuad,
-	materials,
-	temporalResolve;
+import * as THREE from 'three';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { PathTracingRenderer, PhysicalPathTracingMaterial, PhysicalCamera, BlurredEnvMapGenerator, EquirectCamera } from '../src/index.js';
+import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js';
+import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
+import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
+import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
+import { TemporalResolve } from '../src/temporal-resolve/TemporalResolve.js';
+
+let renderer, controls, sceneInfo, ptRenderer, activeCamera, fsQuad, materials, temporalResolve;
 let perspectiveCamera, orthoCamera, equirectCamera;
 let envMap, envMapGenerator, scene;
 let samplesEl;
@@ -30,9 +17,10 @@ let samplesEl;
 const orthoWidth = 5;
 
 const params = {
+
 	material1: {
-		color: "#ffc766",
-		emissive: "#000000",
+		color: '#ffc766',
+		emissive: '#000000',
 		emissiveIntensity: 1,
 		roughness: 0.1,
 		metalness: 0.8,
@@ -41,19 +29,19 @@ const params = {
 		opacity: 1.0,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: "#000000",
+		sheenColor: '#000000',
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: "#ffffff",
+		specularColor: '#ffffff',
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
 	},
 	material2: {
-		color: "#db7157",
-		emissive: "#000000",
+		color: '#db7157',
+		emissive: '#000000',
 		emissiveIntensity: 1,
 		roughness: 0.8,
 		metalness: 0.1,
@@ -62,28 +50,28 @@ const params = {
 		opacity: 1.0,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: "#000000",
+		sheenColor: '#000000',
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: "#ffffff",
+		specularColor: '#ffffff',
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
 	},
 	material3: {
-		color: "#000000",
+		color: '#000000',
 		roughness: 0.01,
 		metalness: 0.05,
 		clearcoat: 0.0,
 		clearcoatRoughness: 0.0,
-		sheenColor: "#000000",
+		sheenColor: '#000000',
 		sheenRoughness: 0.0,
 		iridescence: 0.0,
 		iridescenceIOR: 1.5,
 		iridescenceThickness: 400,
-		specularColor: "#ffffff",
+		specularColor: '#ffffff',
 		specularIntensity: 1.0,
 		matte: false,
 		castShadow: true,
@@ -108,395 +96,394 @@ const params = {
 	tiles: 1,
 	backgroundAlpha: 1,
 	checkerboardTransparency: true,
-	cameraProjection: "Perspective",
+	cameraProjection: 'Perspective',
 };
 
-if (window.location.hash.includes("transmission")) {
+if ( window.location.hash.includes( 'transmission' ) ) {
+
 	params.material1.metalness = 0.0;
 	params.material1.roughness = 0.05;
 	params.material1.transmission = 1.0;
-	params.material1.color = "#ffffff";
+	params.material1.color = '#ffffff';
 	params.bounces = 10;
+
 }
 
 // adjust performance parameters for mobile
 const aspectRatio = window.innerWidth / window.innerHeight;
-if (aspectRatio < 0.65) {
-	params.bounces = Math.max(params.bounces, 6);
+if ( aspectRatio < 0.65 ) {
+
+	params.bounces = Math.max( params.bounces, 6 );
 	params.resolutionScale *= 0.5;
 	params.tiles = 2;
 	params.multipleImportanceSampling = false;
 	params.environmentBlur = 0.35;
+
 }
 
 init();
 
 async function init() {
-	renderer = new THREE.WebGLRenderer({ antialias: true });
+
+	renderer = new THREE.WebGLRenderer( { antialias: true } );
 	renderer.toneMapping = THREE.ACESFilmicToneMapping;
 	renderer.outputEncoding = THREE.sRGBEncoding;
-	renderer.setClearColor(0, 0);
-	document.body.appendChild(renderer.domElement);
+	renderer.setClearColor( 0, 0 );
+	document.body.appendChild( renderer.domElement );
 
 	const aspect = window.innerWidth / window.innerHeight;
-	perspectiveCamera = new PhysicalCamera(75, aspect, 0.025, 500);
-	perspectiveCamera.position.set(-4, 2, 3);
+	perspectiveCamera = new PhysicalCamera( 75, aspect, 0.025, 500 );
+	perspectiveCamera.position.set( - 4, 2, 3 );
 
 	const orthoHeight = orthoWidth / aspect;
-	orthoCamera = new THREE.OrthographicCamera(
-		orthoWidth / -2,
-		orthoWidth / 2,
-		orthoHeight / 2,
-		orthoHeight / -2,
-		0,
-		100
-	);
-	orthoCamera.position.set(-4, 2, 3);
+	orthoCamera = new THREE.OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
+	orthoCamera.position.set( - 4, 2, 3 );
 
 	equirectCamera = new EquirectCamera();
-	equirectCamera.position.set(-4, 2, 3);
+	equirectCamera.position.set( - 4, 2, 3 );
 
-	ptRenderer = new PathTracingRenderer(renderer);
+	ptRenderer = new PathTracingRenderer( renderer );
 	ptRenderer.alpha = true;
 	ptRenderer.material = new PhysicalPathTracingMaterial();
-	ptRenderer.material.setDefine(
-		"TRANSPARENT_TRAVERSALS",
-		params.transparentTraversals
-	);
-	ptRenderer.material.setDefine(
-		"FEATURE_MIS",
-		Number(params.multipleImportanceSampling)
-	);
-	ptRenderer.tiles.set(params.tiles, params.tiles);
-
-	fsQuad = new FullScreenQuad(
-		new THREE.MeshBasicMaterial({
-			map: ptRenderer.target.texture,
-			blending: THREE.CustomBlending,
-		})
-	);
-
-	controls = new OrbitControls(perspectiveCamera, renderer.domElement);
-	controls.addEventListener("change", () => {
+	ptRenderer.material.setDefine( 'TRANSPARENT_TRAVERSALS', params.transparentTraversals );
+	ptRenderer.material.setDefine( 'FEATURE_MIS', Number( params.multipleImportanceSampling ) );
+	ptRenderer.tiles.set( params.tiles, params.tiles );
+
+	fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
+		map: ptRenderer.target.texture,
+		blending: THREE.CustomBlending,
+	} ) );
+
+	controls = new OrbitControls( perspectiveCamera, renderer.domElement );
+	controls.addEventListener( 'change', () => {
+
 		ptRenderer.reset();
-	});
+
+	} );
 
 	scene = new THREE.Scene();
 
-	temporalResolve = new TemporalResolve(ptRenderer, scene, activeCamera);
+	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
 	temporalResolve.temporalResolveMix = 0.875;
 	temporalResolve.clampRadius = 2;
 	temporalResolve.newSamplesSmoothing = 0.5;
 	temporalResolve.newSamplesCorrection = 0.75;
 
-	samplesEl = document.getElementById("samples");
+	samplesEl = document.getElementById( 'samples' );
 
-	envMapGenerator = new BlurredEnvMapGenerator(renderer);
+	envMapGenerator = new BlurredEnvMapGenerator( renderer );
+
+	const envMapPromise = new Promise( resolve => {
+
+		new RGBELoader()
+			.load( 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr', texture => {
 
-	const envMapPromise = new Promise((resolve) => {
-		new RGBELoader().load(
-			"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/equirectangular/royal_esplanade_1k.hdr",
-			(texture) => {
 				envMap = texture;
 
 				updateEnvBlur();
 				resolve();
-			}
-		);
-	});
+
+			} );
+
+	} );
 
 	const generator = new PathTracingSceneWorker();
 	const gltfPromise = new GLTFLoader()
-		.setMeshoptDecoder(MeshoptDecoder)
-		.loadAsync(
-			"https://raw.githubusercontent.com/gkjohnson/3d-demo-data/main/models/material-balls/material_ball_v2.glb"
-		)
-		.then((gltf) => {
+		.setMeshoptDecoder( MeshoptDecoder )
+		.loadAsync( 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/main/models/material-balls/material_ball_v2.glb' )
+		.then( gltf => {
+
 			const group = new THREE.Group();
 
-			gltf.scene.scale.setScalar(0.01);
+			gltf.scene.scale.setScalar( 0.01 );
 			gltf.scene.updateMatrixWorld();
-			group.add(gltf.scene);
+			group.add( gltf.scene );
 
 			const box = new THREE.Box3();
-			box.setFromObject(gltf.scene);
+			box.setFromObject( gltf.scene );
 
 			const floor = new THREE.Mesh(
-				new THREE.CylinderBufferGeometry(3, 3, 0.05, 200),
-				new THREE.MeshPhysicalMaterial({
-					color: 0xffffff,
-					roughness: 0,
-					metalness: 0.25,
-				})
+				new THREE.CylinderBufferGeometry( 3, 3, 0.05, 200 ),
+				new THREE.MeshPhysicalMaterial( { color: 0xffffff, roughness: 0, metalness: 0.25 } ),
 			);
 			floor.geometry = floor.geometry.toNonIndexed();
 			floor.geometry.clearGroups();
 			floor.position.y = box.min.y - 0.03;
-			group.add(floor);
+			group.add( floor );
 
 			const material1 = new THREE.MeshPhysicalMaterial();
 			const material2 = new THREE.MeshPhysicalMaterial();
 
-			gltf.scene.traverse((c) => {
+			gltf.scene.traverse( c => {
+
 				// the vertex normals on the material ball are off...
 				// TODO: precompute the vertex normals so they are correct on load
-				if (c.geometry) {
+				if ( c.geometry ) {
+
 					c.geometry.computeVertexNormals();
+
 				}
 
-				if (c.name === "Sphere_1") {
+				if ( c.name === 'Sphere_1' ) {
+
 					c.material = material2;
+
 				} else {
+
 					c.material = material1;
+
 				}
 
-				if (c.name === "subsphere_1") {
+				if ( c.name === 'subsphere_1' ) {
+
 					c.material = material2;
+
 				}
-			});
 
-			materials = [material1, material2, floor.material];
+			} );
+
+			materials = [ material1, material2, floor.material ];
+
+			return generator.generate( group );
+
+		} )
+		.then( result => {
 
-			return generator.generate(group);
-		})
-		.then((result) => {
 			sceneInfo = result;
 
-			scene.add(result.scene);
+			scene.add( result.scene );
 
 			const { bvh, textures, materials } = result;
 			const geometry = bvh.geometry;
 			const material = ptRenderer.material;
 
-			material.bvh.updateFrom(bvh);
-			material.normalAttribute.updateFrom(geometry.attributes.normal);
-			material.tangentAttribute.updateFrom(geometry.attributes.tangent);
-			material.uvAttribute.updateFrom(geometry.attributes.uv);
-			material.materialIndexAttribute.updateFrom(
-				geometry.attributes.materialIndex
-			);
-			material.textures.setTextures(renderer, 2048, 2048, textures);
-			material.materials.updateFrom(materials, textures);
+			material.bvh.updateFrom( bvh );
+			material.normalAttribute.updateFrom( geometry.attributes.normal );
+			material.tangentAttribute.updateFrom( geometry.attributes.tangent );
+			material.uvAttribute.updateFrom( geometry.attributes.uv );
+			material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
+			material.textures.setTextures( renderer, 2048, 2048, textures );
+			material.materials.updateFrom( materials, textures );
 
 			generator.dispose();
-		});
 
-	await Promise.all([gltfPromise, envMapPromise]);
+		} );
+
+	await Promise.all( [ gltfPromise, envMapPromise ] );
 
-	document.getElementById("loading").remove();
-	document.body.classList.add("checkerboard");
+	document.getElementById( 'loading' ).remove();
+	document.body.classList.add( 'checkerboard' );
 
 	onResize();
-	window.addEventListener("resize", onResize);
+	window.addEventListener( 'resize', onResize );
 	const gui = new GUI();
 
-	updateCamera(params.cameraProjection);
+	updateCamera( params.cameraProjection );
 
-	const ptFolder = gui.addFolder("Path Tracing");
-	ptFolder.add(params, "acesToneMapping").onChange((value) => {
-		renderer.toneMapping = value
-			? THREE.ACESFilmicToneMapping
-			: THREE.NoToneMapping;
+	const ptFolder = gui.addFolder( 'Path Tracing' );
+	ptFolder.add( params, 'acesToneMapping' ).onChange( value => {
+
+		renderer.toneMapping = value ? THREE.ACESFilmicToneMapping : THREE.NoToneMapping;
 		fsQuad.material.needsUpdate = true;
-	});
-	ptFolder.add(params, "stableNoise").onChange((value) => {
+
+	} );
+	ptFolder.add( params, 'stableNoise' ).onChange( value => {
+
 		ptRenderer.stableNoise = value;
-	});
-	ptFolder.add(params, "multipleImportanceSampling").onChange((value) => {
-		ptRenderer.material.setDefine("FEATURE_MIS", Number(value));
+
+	} );
+	ptFolder.add( params, 'multipleImportanceSampling' ).onChange( value => {
+
+		ptRenderer.material.setDefine( 'FEATURE_MIS', Number( value ) );
 		ptRenderer.reset();
-	});
-	ptFolder.add(params, "tiles", 1, 4, 1).onChange((value) => {
-		ptRenderer.tiles.set(value, value);
-	});
-	ptFolder.add(params, "samplesPerFrame", 1, 10, 1);
-	ptFolder.add(params, "filterGlossyFactor", 0, 1).onChange(() => {
+
+	} );
+	ptFolder.add( params, 'tiles', 1, 4, 1 ).onChange( value => {
+
+		ptRenderer.tiles.set( value, value );
+
+	} );
+	ptFolder.add( params, 'samplesPerFrame', 1, 10, 1 );
+	ptFolder.add( params, 'filterGlossyFactor', 0, 1 ).onChange( () => {
+
 		ptRenderer.reset();
-	});
-	ptFolder.add(params, "bounces", 1, 30, 1).onChange(() => {
+
+	} );
+	ptFolder.add( params, 'bounces', 1, 30, 1 ).onChange( () => {
+
 		ptRenderer.reset();
-	});
-	ptFolder.add(params, "transparentTraversals", 0, 40, 1).onChange((value) => {
-		ptRenderer.material.setDefine("TRANSPARENT_TRAVERSALS", value);
+
+	} );
+	ptFolder.add( params, 'transparentTraversals', 0, 40, 1 ).onChange( value => {
+
+		ptRenderer.material.setDefine( 'TRANSPARENT_TRAVERSALS', value );
 		ptRenderer.reset();
-	});
-	ptFolder.add(params, "resolutionScale", 0.1, 1).onChange(() => {
+
+	} );
+	ptFolder.add( params, 'resolutionScale', 0.1, 1 ).onChange( () => {
+
 		onResize();
-	});
 
-	const trFolder = gui.addFolder("Temporal Resolve");
-	trFolder.add(params, "temporalResolve");
+	} );
+
+	const trFolder = gui.addFolder( 'Temporal Resolve' );
+	trFolder.add( params, 'temporalResolve' );
 	trFolder
-		.add(params, "temporalResolveMix", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.temporalResolveMix = value));
+		.add( params, 'temporalResolveMix', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.temporalResolveMix = value ) );
 	trFolder
-		.add(params, "clampRadius", 1, 8, 1)
-		.onChange((value) => (temporalResolve.clampRadius = value));
+		.add( params, 'clampRadius', 1, 8, 1 )
+		.onChange( ( value ) => ( temporalResolve.clampRadius = value ) );
 	trFolder
-		.add(params, "newSamplesSmoothing", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.newSamplesSmoothing = value));
+		.add( params, 'newSamplesSmoothing', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesSmoothing = value ) );
 	trFolder
-		.add(params, "newSamplesCorrection", 0, 1, 0.025)
-		.onChange((value) => (temporalResolve.newSamplesCorrection = value));
+		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+
+	const envFolder = gui.addFolder( 'Environment' );
+	envFolder.add( params, 'environmentIntensity', 0, 10 ).onChange( () => {
 
-	const envFolder = gui.addFolder("Environment");
-	envFolder.add(params, "environmentIntensity", 0, 10).onChange(() => {
 		ptRenderer.reset();
-	});
-	envFolder.add(params, "environmentRotation", 0, 2 * Math.PI).onChange((v) => {
-		ptRenderer.material.environmentRotation.setFromMatrix4(
-			new THREE.Matrix4().makeRotationY(v)
-		);
+
+	} );
+	envFolder.add( params, 'environmentRotation', 0, 2 * Math.PI ).onChange( v => {
+
+		ptRenderer.material.environmentRotation.setFromMatrix4( new THREE.Matrix4().makeRotationY( v ) );
 		ptRenderer.reset();
-	});
-	envFolder.add(params, "environmentBlur", 0, 1).onChange(() => {
+
+	} );
+	envFolder.add( params, 'environmentBlur', 0, 1 ).onChange( () => {
+
 		updateEnvBlur();
-	});
-	envFolder.add(params, "backgroundBlur", 0, 1).onChange(() => {
+
+	} );
+	envFolder.add( params, 'backgroundBlur', 0, 1 ).onChange( () => {
+
 		ptRenderer.reset();
-	});
-	envFolder.add(params, "backgroundAlpha", 0, 1).onChange(() => {
+
+	} );
+	envFolder.add( params, 'backgroundAlpha', 0, 1 ).onChange( () => {
+
 		ptRenderer.reset();
-	});
-	envFolder.add(params, "checkerboardTransparency").onChange((v) => {
-		if (v) {
-			document.body.classList.add("checkerboard");
+
+	} );
+	envFolder.add( params, 'checkerboardTransparency' ).onChange( v => {
+
+		if ( v ) {
+
+			document.body.classList.add( 'checkerboard' );
+
 		} else {
-			document.body.classList.remove("checkerboard");
+
+			document.body.classList.remove( 'checkerboard' );
+
 		}
-	});
-
-	const cameraFolder = gui.addFolder("Camera");
-	cameraFolder
-		.add(params, "cameraProjection", [
-			"Perspective",
-			"Orthographic",
-			"Equirectangular",
-		])
-		.onChange((v) => {
-			updateCamera(v);
-		});
-	cameraFolder.add(perspectiveCamera, "focusDistance", 1, 100).onChange(reset);
-	cameraFolder
-		.add(perspectiveCamera, "apertureBlades", 0, 10, 1)
-		.onChange(function (v) {
-			perspectiveCamera.apertureBlades = v === 0 ? 0 : Math.max(v, 3);
-			this.updateDisplay();
-			reset();
-		});
-	cameraFolder
-		.add(perspectiveCamera, "apertureRotation", 0, 12.5)
-		.onChange(reset);
-	cameraFolder
-		.add(perspectiveCamera, "anamorphicRatio", 0.1, 10.0)
-		.onChange(reset);
-	cameraFolder
-		.add(perspectiveCamera, "bokehSize", 0, 50)
-		.onChange(reset)
-		.listen();
-	cameraFolder
-		.add(perspectiveCamera, "fStop", 0.3, 20)
-		.onChange(reset)
-		.listen();
-	cameraFolder
-		.add(perspectiveCamera, "fov", 25, 100)
-		.onChange(() => {
-			perspectiveCamera.updateProjectionMatrix();
-			reset();
-		})
-		.listen();
-
-	const matFolder1 = gui.addFolder("Shell Material");
-	matFolder1.addColor(params.material1, "color").onChange(reset);
-	matFolder1.addColor(params.material1, "emissive").onChange(reset);
-	matFolder1
-		.add(params.material1, "emissiveIntensity", 0.0, 50.0, 0.01)
-		.onChange(reset);
-	matFolder1.add(params.material1, "roughness", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "metalness", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "opacity", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "transmission", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "ior", 0.9, 3.0).onChange(reset);
-	matFolder1.add(params.material1, "clearcoat", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "clearcoatRoughness", 0, 1).onChange(reset);
-	matFolder1.addColor(params.material1, "sheenColor").onChange(reset);
-	matFolder1.add(params.material1, "sheenRoughness", 0, 1).onChange(reset);
-	matFolder1.add(params.material1, "iridescence", 0.0, 1.0).onChange(reset);
-	matFolder1.add(params.material1, "iridescenceIOR", 0.1, 3.0).onChange(reset);
-	matFolder1
-		.add(params.material1, "iridescenceThickness", 0.0, 1200.0)
-		.onChange(reset);
-	matFolder1.addColor(params.material1, "specularColor").onChange(reset);
-	matFolder1
-		.add(params.material1, "specularIntensity", 0.0, 1.0)
-		.onChange(reset);
-	matFolder1.add(params.material1, "matte").onChange(reset);
-	matFolder1.add(params.material1, "castShadow").onChange(reset);
+
+	} );
+
+	const cameraFolder = gui.addFolder( 'Camera' );
+	cameraFolder.add( params, 'cameraProjection', [ 'Perspective', 'Orthographic', 'Equirectangular' ] ).onChange( v => {
+
+		updateCamera( v );
+
+	} );
+	cameraFolder.add( perspectiveCamera, 'focusDistance', 1, 100 ).onChange( reset );
+	cameraFolder.add( perspectiveCamera, 'apertureBlades', 0, 10, 1 ).onChange( function ( v ) {
+
+		perspectiveCamera.apertureBlades = v === 0 ? 0 : Math.max( v, 3 );
+		this.updateDisplay();
+		reset();
+
+	} );
+	cameraFolder.add( perspectiveCamera, 'apertureRotation', 0, 12.5 ).onChange( reset );
+	cameraFolder.add( perspectiveCamera, 'anamorphicRatio', 0.1, 10.0 ).onChange( reset );
+	cameraFolder.add( perspectiveCamera, 'bokehSize', 0, 50 ).onChange( reset ).listen();
+	cameraFolder.add( perspectiveCamera, 'fStop', 0.3, 20 ).onChange( reset ).listen();
+	cameraFolder.add( perspectiveCamera, 'fov', 25, 100 ).onChange( () => {
+
+		perspectiveCamera.updateProjectionMatrix();
+		reset();
+
+	} ).listen();
+
+	const matFolder1 = gui.addFolder( 'Shell Material' );
+	matFolder1.addColor( params.material1, 'color' ).onChange( reset );
+	matFolder1.addColor( params.material1, 'emissive' ).onChange( reset );
+	matFolder1.add( params.material1, 'emissiveIntensity', 0.0, 50.0, 0.01 ).onChange( reset );
+	matFolder1.add( params.material1, 'roughness', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'metalness', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'opacity', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'transmission', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'ior', 0.9, 3.0 ).onChange( reset );
+	matFolder1.add( params.material1, 'clearcoat', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'clearcoatRoughness', 0, 1 ).onChange( reset );
+	matFolder1.addColor( params.material1, 'sheenColor' ).onChange( reset );
+	matFolder1.add( params.material1, 'sheenRoughness', 0, 1 ).onChange( reset );
+	matFolder1.add( params.material1, 'iridescence', 0.0, 1.0 ).onChange( reset );
+	matFolder1.add( params.material1, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
+	matFolder1.add( params.material1, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
+	matFolder1.addColor( params.material1, 'specularColor' ).onChange( reset );
+	matFolder1.add( params.material1, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
+	matFolder1.add( params.material1, 'matte' ).onChange( reset );
+	matFolder1.add( params.material1, 'castShadow' ).onChange( reset );
 	matFolder1.close();
 
-	const matFolder2 = gui.addFolder("Ball Material");
-	matFolder2.addColor(params.material2, "color").onChange(reset);
-	matFolder2.addColor(params.material2, "emissive").onChange(reset);
-	matFolder2
-		.add(params.material2, "emissiveIntensity", 0.0, 50.0, 0.01)
-		.onChange(reset);
-	matFolder2.add(params.material2, "roughness", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "metalness", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "opacity", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "transmission", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "ior", 0.9, 3.0).onChange(reset);
-	matFolder2.add(params.material2, "clearcoat", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "clearcoatRoughness", 0, 1).onChange(reset);
-	matFolder2.addColor(params.material2, "sheenColor").onChange(reset);
-	matFolder2.add(params.material2, "sheenRoughness", 0, 1).onChange(reset);
-	matFolder2.add(params.material2, "iridescence", 0.0, 1.0).onChange(reset);
-	matFolder2.add(params.material2, "iridescenceIOR", 0.1, 3.0).onChange(reset);
-	matFolder2
-		.add(params.material2, "iridescenceThickness", 0.0, 1200.0)
-		.onChange(reset);
-	matFolder2.addColor(params.material2, "specularColor").onChange(reset);
-	matFolder2
-		.add(params.material2, "specularIntensity", 0.0, 1.0)
-		.onChange(reset);
-	matFolder2.add(params.material2, "matte").onChange(reset);
-	matFolder2.add(params.material2, "castShadow").onChange(reset);
+	const matFolder2 = gui.addFolder( 'Ball Material' );
+	matFolder2.addColor( params.material2, 'color' ).onChange( reset );
+	matFolder2.addColor( params.material2, 'emissive' ).onChange( reset );
+	matFolder2.add( params.material2, 'emissiveIntensity', 0.0, 50.0, 0.01 ).onChange( reset );
+	matFolder2.add( params.material2, 'roughness', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'metalness', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'opacity', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'transmission', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'ior', 0.9, 3.0 ).onChange( reset );
+	matFolder2.add( params.material2, 'clearcoat', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'clearcoatRoughness', 0, 1 ).onChange( reset );
+	matFolder2.addColor( params.material2, 'sheenColor' ).onChange( reset );
+	matFolder2.add( params.material2, 'sheenRoughness', 0, 1 ).onChange( reset );
+	matFolder2.add( params.material2, 'iridescence', 0.0, 1.0 ).onChange( reset );
+	matFolder2.add( params.material2, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
+	matFolder2.add( params.material2, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
+	matFolder2.addColor( params.material2, 'specularColor' ).onChange( reset );
+	matFolder2.add( params.material2, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
+	matFolder2.add( params.material2, 'matte' ).onChange( reset );
+	matFolder2.add( params.material2, 'castShadow' ).onChange( reset );
 	matFolder2.close();
 
-	const matFolder3 = gui.addFolder("Floor Material");
-	matFolder3.addColor(params.material3, "color").onChange(reset);
-	matFolder3.add(params.material3, "roughness", 0, 1).onChange(reset);
-	matFolder3.add(params.material3, "metalness", 0, 1).onChange(reset);
-	matFolder3.add(params.material3, "clearcoat", 0, 1).onChange(reset);
-	matFolder3.add(params.material3, "clearcoatRoughness", 0, 1).onChange(reset);
-	matFolder3.addColor(params.material3, "sheenColor").onChange(reset);
-	matFolder3.add(params.material3, "sheenRoughness", 0, 1).onChange(reset);
-	matFolder3.add(params.material3, "matte").onChange(reset);
-	matFolder3.add(params.material3, "castShadow").onChange(reset);
-	matFolder3.add(params.material3, "iridescence", 0.0, 1.0).onChange(reset);
-	matFolder3.add(params.material3, "iridescenceIOR", 0.1, 3.0).onChange(reset);
-	matFolder3
-		.add(params.material3, "iridescenceThickness", 0.0, 1200.0)
-		.onChange(reset);
-	matFolder3.addColor(params.material3, "specularColor").onChange(reset);
-	matFolder3
-		.add(params.material3, "specularIntensity", 0.0, 1.0)
-		.onChange(reset);
+	const matFolder3 = gui.addFolder( 'Floor Material' );
+	matFolder3.addColor( params.material3, 'color' ).onChange( reset );
+	matFolder3.add( params.material3, 'roughness', 0, 1 ).onChange( reset );
+	matFolder3.add( params.material3, 'metalness', 0, 1 ).onChange( reset );
+	matFolder3.add( params.material3, 'clearcoat', 0, 1 ).onChange( reset );
+	matFolder3.add( params.material3, 'clearcoatRoughness', 0, 1 ).onChange( reset );
+	matFolder3.addColor( params.material3, 'sheenColor' ).onChange( reset );
+	matFolder3.add( params.material3, 'sheenRoughness', 0, 1 ).onChange( reset );
+	matFolder3.add( params.material3, 'matte' ).onChange( reset );
+	matFolder3.add( params.material3, 'castShadow' ).onChange( reset );
+	matFolder3.add( params.material3, 'iridescence', 0.0, 1.0 ).onChange( reset );
+	matFolder3.add( params.material3, 'iridescenceIOR', 0.1, 3.0 ).onChange( reset );
+	matFolder3.add( params.material3, 'iridescenceThickness', 0.0, 1200.0 ).onChange( reset );
+	matFolder3.addColor( params.material3, 'specularColor' ).onChange( reset );
+	matFolder3.add( params.material3, 'specularIntensity', 0.0, 1.0 ).onChange( reset );
 	matFolder3.close();
 
 	animate();
+
 }
 
 function onResize() {
+
 	const w = window.innerWidth;
 	const h = window.innerHeight;
 	const scale = params.resolutionScale;
 	const dpr = window.devicePixelRatio;
 
-	ptRenderer.setSize(w * scale * dpr, h * scale * dpr);
+	ptRenderer.setSize( w * scale * dpr, h * scale * dpr );
 	ptRenderer.reset();
 
-	renderer.setSize(w, h);
-	renderer.setPixelRatio(window.devicePixelRatio * scale);
+	renderer.setSize( w, h );
+	renderer.setPixelRatio( window.devicePixelRatio * scale );
 
 	const aspect = w / h;
 
@@ -505,42 +492,58 @@ function onResize() {
 
 	const orthoHeight = orthoWidth / aspect;
 	orthoCamera.top = orthoHeight / 2;
-	orthoCamera.bottom = orthoHeight / -2;
+	orthoCamera.bottom = orthoHeight / - 2;
 	orthoCamera.updateProjectionMatrix();
+
 }
 
 function reset() {
+
 	ptRenderer.reset();
+
 }
 
 function updateEnvBlur() {
-	const blurredTex = envMapGenerator.generate(envMap, params.environmentBlur);
-	ptRenderer.material.envMapInfo.updateFrom(blurredTex);
+
+	const blurredTex = envMapGenerator.generate( envMap, params.environmentBlur );
+	ptRenderer.material.envMapInfo.updateFrom( blurredTex );
 	scene.environment = blurredTex;
 	ptRenderer.reset();
+
 }
 
-function updateCamera(cameraProjection) {
-	if (cameraProjection === "Perspective") {
-		if (activeCamera) {
-			perspectiveCamera.position.copy(activeCamera.position);
+function updateCamera( cameraProjection ) {
+
+	if ( cameraProjection === 'Perspective' ) {
+
+		if ( activeCamera ) {
+
+			perspectiveCamera.position.copy( activeCamera.position );
+
 		}
 
 		activeCamera = perspectiveCamera;
-	} else if (cameraProjection === "Orthographic") {
-		if (activeCamera) {
-			orthoCamera.position.copy(activeCamera.position);
+
+	} else if ( cameraProjection === 'Orthographic' ) {
+
+		if ( activeCamera ) {
+
+			orthoCamera.position.copy( activeCamera.position );
+
 		}
 
 		activeCamera = orthoCamera;
-	} else {
-		// Equirect
 
-		if (activeCamera) {
-			equirectCamera.position.copy(activeCamera.position);
+	} else { // Equirect
+
+		if ( activeCamera ) {
+
+			equirectCamera.position.copy( activeCamera.position );
+
 		}
 
 		activeCamera = equirectCamera;
+
 	}
 
 	controls.object = activeCamera;
@@ -549,14 +552,16 @@ function updateCamera(cameraProjection) {
 	controls.update();
 
 	reset();
+
 }
 
 function animate() {
-	requestAnimationFrame(animate);
 
-	const m1 = materials[0];
-	m1.color.set(params.material1.color).convertSRGBToLinear();
-	m1.emissive.set(params.material1.emissive).convertSRGBToLinear();
+	requestAnimationFrame( animate );
+
+	const m1 = materials[ 0 ];
+	m1.color.set( params.material1.color ).convertSRGBToLinear();
+	m1.emissive.set( params.material1.emissive ).convertSRGBToLinear();
 	m1.emissiveIntensity = params.material1.emissiveIntensity;
 	m1.metalness = params.material1.metalness;
 	m1.roughness = params.material1.roughness;
@@ -565,17 +570,17 @@ function animate() {
 	m1.opacity = params.material1.opacity;
 	m1.clearcoat = params.material1.clearcoat;
 	m1.clearcoatRoughness = params.material1.clearcoatRoughness;
-	m1.sheenColor.set(params.material1.sheenColor).convertSRGBToLinear();
+	m1.sheenColor.set( params.material1.sheenColor ).convertSRGBToLinear();
 	m1.sheenRoughness = params.material1.sheenRoughness;
 	m1.iridescence = params.material1.iridescence;
 	m1.iridescenceIOR = params.material1.iridescenceIOR;
-	m1.iridescenceThicknessRange = [0, params.material1.iridescenceThickness];
-	m1.specularColor.set(params.material1.specularColor).convertSRGBToLinear();
+	m1.iridescenceThicknessRange = [ 0, params.material1.iridescenceThickness ];
+	m1.specularColor.set( params.material1.specularColor ).convertSRGBToLinear();
 	m1.specularIntensity = params.material1.specularIntensity;
 
-	const m2 = materials[1];
-	m2.color.set(params.material2.color).convertSRGBToLinear();
-	m2.emissive.set(params.material2.emissive).convertSRGBToLinear();
+	const m2 = materials[ 1 ];
+	m2.color.set( params.material2.color ).convertSRGBToLinear();
+	m2.emissive.set( params.material2.emissive ).convertSRGBToLinear();
 	m2.emissiveIntensity = params.material2.emissiveIntensity;
 	m2.metalness = params.material2.metalness;
 	m2.roughness = params.material2.roughness;
@@ -584,72 +589,85 @@ function animate() {
 	m2.opacity = params.material2.opacity;
 	m2.clearcoat = params.material2.clearcoat;
 	m2.clearcoatRoughness = params.material2.clearcoatRoughness;
-	m2.sheenColor.set(params.material2.sheenColor).convertSRGBToLinear();
+	m2.sheenColor.set( params.material2.sheenColor ).convertSRGBToLinear();
 	m2.sheenRoughness = params.material2.sheenRoughness;
 	m2.iridescence = params.material2.iridescence;
 	m2.iridescenceIOR = params.material2.iridescenceIOR;
-	m2.iridescenceThicknessRange = [0, params.material2.iridescenceThickness];
-	m2.specularColor.set(params.material2.specularColor).convertSRGBToLinear();
+	m2.iridescenceThicknessRange = [ 0, params.material2.iridescenceThickness ];
+	m2.specularColor.set( params.material2.specularColor ).convertSRGBToLinear();
 	m2.specularIntensity = params.material2.specularIntensity;
 
-	const m3 = materials[2];
-	m3.color.set(params.material3.color).convertSRGBToLinear();
+	const m3 = materials[ 2 ];
+	m3.color.set( params.material3.color ).convertSRGBToLinear();
 	m3.metalness = params.material3.metalness;
 	m3.roughness = params.material3.roughness;
 	m3.clearcoat = params.material3.clearcoat;
 	m3.clearcoatRoughness = params.material3.clearcoatRoughness;
-	m3.sheenColor.set(params.material3.sheenColor).convertSRGBToLinear();
+	m3.sheenColor.set( params.material3.sheenColor ).convertSRGBToLinear();
 	m3.sheenRoughness = params.material3.sheenRoughness;
 	m3.iridescence = params.material3.iridescence;
 	m3.iridescenceIOR = params.material3.iridescenceIOR;
-	m3.iridescenceThicknessRange = [0, params.material3.iridescenceThickness];
-	m3.specularColor.set(params.material3.specularColor).convertSRGBToLinear();
+	m3.iridescenceThicknessRange = [ 0, params.material3.iridescenceThickness ];
+	m3.specularColor.set( params.material3.specularColor ).convertSRGBToLinear();
 	m3.specularIntensity = params.material3.specularIntensity;
 
-	ptRenderer.material.materials.updateFrom(
-		sceneInfo.materials,
-		sceneInfo.textures
-	);
-	ptRenderer.material.materials.setMatte(0, params.material1.matte);
-	ptRenderer.material.materials.setMatte(1, params.material2.matte);
-	ptRenderer.material.materials.setMatte(2, params.material3.matte);
-	ptRenderer.material.materials.setCastShadow(0, params.material1.castShadow);
-	ptRenderer.material.materials.setCastShadow(1, params.material2.castShadow);
-	ptRenderer.material.materials.setCastShadow(2, params.material3.castShadow);
+	ptRenderer.material.materials.updateFrom( sceneInfo.materials, sceneInfo.textures );
+	ptRenderer.material.materials.setMatte( 0, params.material1.matte );
+	ptRenderer.material.materials.setMatte( 1, params.material2.matte );
+	ptRenderer.material.materials.setMatte( 2, params.material3.matte );
+	ptRenderer.material.materials.setCastShadow( 0, params.material1.castShadow );
+	ptRenderer.material.materials.setCastShadow( 1, params.material2.castShadow );
+	ptRenderer.material.materials.setCastShadow( 2, params.material3.castShadow );
 
 	ptRenderer.material.filterGlossyFactor = params.filterGlossyFactor;
 	ptRenderer.material.environmentIntensity = params.environmentIntensity;
 	ptRenderer.material.backgroundBlur = params.backgroundBlur;
 	ptRenderer.material.bounces = params.bounces;
 	ptRenderer.material.backgroundAlpha = params.backgroundAlpha;
-	ptRenderer.material.physicalCamera.updateFrom(activeCamera);
+	ptRenderer.material.physicalCamera.updateFrom( activeCamera );
 
 	activeCamera.updateMatrixWorld();
 
-	if (params.backgroundAlpha < 1.0) {
+	if ( params.backgroundAlpha < 1.0 ) {
+
 		scene.background = null;
+
 	} else {
+
 		scene.background = scene.environment;
+
 	}
 
-	for (let i = 0, l = params.samplesPerFrame; i < l; i++) {
+	for ( let i = 0, l = params.samplesPerFrame; i < l; i ++ ) {
+
 		ptRenderer.update();
+
 	}
 
-	if (!params.temporalResolve && ptRenderer.samples < 1) {
-		renderer.render(scene, activeCamera);
+	if ( ! params.temporalResolve && ptRenderer.samples < 1 ) {
+
+		renderer.render( scene, activeCamera );
+
 	}
 
 	renderer.autoClear = false;
-	if (params.temporalResolve) {
+	if ( params.temporalResolve ) {
+
 		temporalResolve.update();
 		fsQuad.material.map = temporalResolve.target.texture;
+
 	} else {
+
 		fsQuad.material.map = ptRenderer.target.texture;
+
 	}
 
-	fsQuad.render(renderer);
+	fsQuad.render( renderer );
 	renderer.autoClear = true;
 
-	samplesEl.innerText = `Samples: ${Math.floor(ptRenderer.samples)}`;
+	samplesEl.innerText = `Samples: ${ Math.floor( ptRenderer.samples ) }`;
+
 }
+
+
+
From 63d58c0d114ec687e842ed88e11cbe2629eef904 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 28 Jul 2022 21:40:45 +0200
Subject: [PATCH 07/25] Fix formatting
---
 src/index.js                            |  2 +-
 src/temporal-resolve/TemporalResolve.js | 66 ++++++++++++++++---------
 2 files changed, 43 insertions(+), 25 deletions(-)
diff --git a/src/index.js b/src/index.js
index 1c45e0690..a13f25b57 100644
--- a/src/index.js
+++ b/src/index.js
@@ -34,4 +34,4 @@ export * from './shader/shaderUtils.js';
 export * from './shader/shaderStructs.js';
 
 // temporal resolve
-export * from './temporal-resolve/TemporalResolve';
+export * from './temporal-resolve/TemporalResolve.js';
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 647433d45..8586742e0 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -1,10 +1,12 @@
-import { HalfFloatType, LinearFilter, WebGLRenderTarget } from "three";
-import { FullScreenQuad } from "three/examples/jsm/postprocessing/Pass";
-import { ComposeTemporalResolveMaterial } from "./materials/ComposeTemporalResolveMaterial";
-import { TemporalResolvePass } from "./passes/TemporalResolvePass";
+import { HalfFloatType, LinearFilter, WebGLRenderTarget } from 'three';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
+import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial';
+import { TemporalResolvePass } from './passes/TemporalResolvePass';
 
 export class TemporalResolve {
-	constructor(ptRenderer, scene, camera) {
+
+	constructor( ptRenderer, scene, camera ) {
+
 		this.ptRenderer = ptRenderer;
 		this.scene = scene;
 
@@ -15,11 +17,11 @@ export class TemporalResolve {
 
 		this.fullscreenMaterial = new ComposeTemporalResolveMaterial();
 
-		this.fsQuad = new FullScreenQuad(this.fullscreenMaterial);
+		this.fsQuad = new FullScreenQuad( this.fullscreenMaterial );
 
 		this.renderTarget = new WebGLRenderTarget(
-			typeof window !== "undefined" ? window.innerWidth : 2000,
-			typeof window !== "undefined" ? window.innerHeight : 1000,
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
 			{
 				minFilter: LinearFilter,
 				magFilter: LinearFilter,
@@ -30,11 +32,13 @@ export class TemporalResolve {
 
 		this.lastSize = { width: 0, height: 0 };
 
-		this.initNewCamera(camera);
-		this.initNewSize(window.innerWidth, window.innerHeight);
+		this.initNewCamera( camera );
+		this.initNewSize( window.innerWidth, window.innerHeight );
+
 	}
 
-	initNewCamera(camera) {
+	initNewCamera( camera ) {
+
 		this.activeCamera = camera;
 
 		this.temporalResolvePass = new TemporalResolvePass(
@@ -47,44 +51,56 @@ export class TemporalResolve {
 
 		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
 			this.temporalResolvePass.renderTarget.texture;
+
 	}
 
-	initNewSize(width, height) {
+	initNewSize( width, height ) {
+
 		this.lastSize.width = width;
 		this.lastSize.height = height;
 
-		this.temporalResolvePass.setSize(width, height);
+		this.temporalResolvePass.setSize( width, height );
+
 	}
 
 	get target() {
+
 		return this.renderTarget;
+
 	}
 
 	update() {
+
 		const renderer = this.ptRenderer._renderer;
 
 		const origRenderTarget = renderer.getRenderTarget();
 
 		const { camera } = this.ptRenderer;
-		if (camera !== this.activeCamera) {
-			this.initNewCamera(camera);
+		if ( camera !== this.activeCamera ) {
+
+			this.initNewCamera( camera );
+
 		}
 
 		const { width, height } = this.ptRenderer.target;
-		if (width !== this.lastSize.width || height !== this.lastSize.height) {
-			this.initNewSize(width, height);
+		if ( width !== this.lastSize.width || height !== this.lastSize.height ) {
+
+			this.initNewSize( width, height );
+
 		}
 
 		// ensure that the scene's objects' matrices are updated for the VelocityPass
 		this.scene.updateMatrixWorld();
 
-		this.scene.traverse((c) => {
+		this.scene.traverse( ( c ) => {
+
 			// update the modelViewMatrix which is used by the VelocityPass
 			c.modelViewMatrix.multiplyMatrices(
 				this.activeCamera.matrixWorldInverse,
 				c.matrixWorld
 			);
-		});
+
+		} );
 
 		// keep uniforms updated
 		this.temporalResolvePass.fullscreenMaterial.uniforms.samples.value =
@@ -94,7 +110,7 @@ export class TemporalResolve {
 			this.temporalResolveMix;
 
 		this.temporalResolvePass.fullscreenMaterial.uniforms.clampRadius.value =
-			parseInt(this.clampRadius);
+			parseInt( this.clampRadius );
 
 		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesSmoothing.value =
 			this.newSamplesSmoothing;
@@ -102,11 +118,13 @@ export class TemporalResolve {
 		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesCorrection.value =
 			this.newSamplesCorrection;
 
-		this.temporalResolvePass.render(renderer);
+		this.temporalResolvePass.render( renderer );
 
-		renderer.setRenderTarget(this.renderTarget);
-		this.fsQuad.render(renderer);
+		renderer.setRenderTarget( this.renderTarget );
+		this.fsQuad.render( renderer );
+
+		renderer.setRenderTarget( origRenderTarget );
 
-		renderer.setRenderTarget(origRenderTarget);
 	}
+
 }
From b375544f98fd24d13fed3804e55849f764f28247 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Fri, 29 Jul 2022 13:16:35 +0200
Subject: [PATCH 08/25] Fix formatting in TemporalResolveMaterial
---
 .../materials/TemporalResolveMaterial.js         | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index 70a02a7ce..c06b20c81 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -1,11 +1,13 @@
-import { Matrix4, ShaderMaterial } from "three";
-import { fragmentShader } from "./shaders/temporalResolveFragment";
-import { vertexShader } from "./shaders/temporalResolveVertex";
+import { Matrix4, ShaderMaterial } from 'three';
+import { fragmentShader } from './shaders/temporalResolveFragment';
+import { vertexShader } from './shaders/temporalResolveVertex';
 
 export class TemporalResolveMaterial extends ShaderMaterial {
+
 	constructor() {
-		super({
-			type: "TemporalResolveMaterial",
+
+		super( {
+			type: 'TemporalResolveMaterial',
 			uniforms: {
 				inputTexture: { value: null },
 				samplesTexture: { value: null },
@@ -27,6 +29,8 @@ export class TemporalResolveMaterial extends ShaderMaterial {
 			},
 			vertexShader,
 			fragmentShader,
-		});
+		} );
+
 	}
+
 }
From 274e6e4f68b8832ae88f18dc65384403bfc74ddb Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Fri, 5 Aug 2022 05:42:11 +0200
Subject: [PATCH 09/25] Improve TR (dilating, different blending), change
 tiling
---
 example/index.js                              |  17 +-
 example/materialBall.js                       |  19 +-
 src/temporal-resolve/TemporalResolve.js       |  25 ++-
 .../materials/TemporalResolveMaterial.js      |   6 +-
 .../materials/VelocityShader.js               |  20 +-
 .../shaders/temporalResolveFragment.js        | 190 +++++++++++-------
 .../passes/TemporalResolvePass.js             |  38 +---
 src/temporal-resolve/passes/VelocityPass.js   |  76 +++++--
 8 files changed, 254 insertions(+), 137 deletions(-)
diff --git a/example/index.js b/example/index.js
index 5a0dc0642..be131837b 100644
--- a/example/index.js
+++ b/example/index.js
@@ -73,10 +73,11 @@ const params = {
 	samplesPerFrame: 1,
 
 	temporalResolve: true,
-	temporalResolveMix: 0.75,
+	temporalResolveMix: 0.9,
 	clampRadius: 1,
-	newSamplesSmoothing: 0.5,
-	newSamplesCorrection: 0.75,
+	newSamplesSmoothing: 0.675,
+	newSamplesCorrection: 1,
+	weightTransform: 0,
 
 	model: initialModel,
 
@@ -151,10 +152,11 @@ async function init() {
 	ptRenderer.material.bgGradientBottom.set( params.bgGradientBottom );
 
 	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
-	temporalResolve.temporalResolveMix = 0.75;
+	temporalResolve.temporalResolveMix = 0.9;
 	temporalResolve.clampRadius = 1;
 	temporalResolve.newSamplesSmoothing = 0.5;
 	temporalResolve.newSamplesCorrection = 0.75;
+	temporalResolve.weightTransform = 0;
 
 	fsQuad = new FullScreenQuad( new MeshBasicMaterial( {
 		map: ptRenderer.target.texture,
@@ -211,7 +213,7 @@ function animate() {
 
 	}
 
-	if ( ptRenderer.samples < 1.0 || ! params.enable ) {
+	if ( ( ! params.temporalResolve && ptRenderer.samples < 1.0 ) || ! params.enable ) {
 
 		renderer.render( scene, activeCamera );
 
@@ -269,7 +271,7 @@ function animate() {
 
 function resetRenderer() {
 
-	if ( params.tilesX * params.tilesY !== 1.0 ) {
+	if ( ! params.temporalResolve && params.tilesX * params.tilesY !== 1.0 ) {
 
 		delaySamples = 1;
 
@@ -349,6 +351,9 @@ function buildGui() {
 	trFolder
 		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
 		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+	trFolder
+		.add( params, 'weightTransform', 0, 0.5, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.weightTransform = value ) );
 
 	const resolutionFolder = gui.addFolder( 'resolution' );
 	resolutionFolder.add( params, 'resolutionScale', 0.1, 1.0, 0.01 ).onChange( () => {
diff --git a/example/materialBall.js b/example/materialBall.js
index e34ad0d17..ad2cef133 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -87,10 +87,11 @@ const params = {
 	acesToneMapping: true,
 	resolutionScale: 1 / window.devicePixelRatio,
 	temporalResolve: true,
-	temporalResolveMix: 0.875,
+	temporalResolveMix: 0.925,
 	clampRadius: 2,
-	newSamplesSmoothing: 0.5,
-	newSamplesCorrection: 0.75,
+	newSamplesSmoothing: 0.675,
+	newSamplesCorrection: 1,
+	weightTransform: 0,
 	transparentTraversals: 20,
 	filterGlossyFactor: 0.5,
 	tiles: 1,
@@ -133,7 +134,7 @@ async function init() {
 
 	const aspect = window.innerWidth / window.innerHeight;
 	perspectiveCamera = new PhysicalCamera( 75, aspect, 0.025, 500 );
-	perspectiveCamera.position.set( - 4, 2, 3 );
+	perspectiveCamera.position.set( - 4, 2, 7 );
 
 	const orthoHeight = orthoWidth / aspect;
 	orthoCamera = new THREE.OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
@@ -164,10 +165,11 @@ async function init() {
 	scene = new THREE.Scene();
 
 	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
-	temporalResolve.temporalResolveMix = 0.875;
+	temporalResolve.temporalResolveMix = 0.925;
 	temporalResolve.clampRadius = 2;
-	temporalResolve.newSamplesSmoothing = 0.5;
-	temporalResolve.newSamplesCorrection = 0.75;
+	temporalResolve.newSamplesSmoothing = 0.675;
+	temporalResolve.newSamplesCorrection = 1;
+	temporalResolve.weightTransform = 0;
 
 	samplesEl = document.getElementById( 'samples' );
 
@@ -340,6 +342,9 @@ async function init() {
 	trFolder
 		.add( params, 'newSamplesCorrection', 0, 1, 0.025 )
 		.onChange( ( value ) => ( temporalResolve.newSamplesCorrection = value ) );
+	trFolder
+		.add( params, 'weightTransform', 0, 0.5, 0.025 )
+		.onChange( ( value ) => ( temporalResolve.weightTransform = value ) );
 
 	const envFolder = gui.addFolder( 'Environment' );
 	envFolder.add( params, 'environmentIntensity', 0, 10 ).onChange( () => {
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 8586742e0..55aabd97e 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -10,10 +10,10 @@ export class TemporalResolve {
 		this.ptRenderer = ptRenderer;
 		this.scene = scene;
 
-		this.temporalResolveMix = 0.75;
+		this.temporalResolveMix = 0.9;
 		this.clampRadius = 1;
-		this.newSamplesSmoothing = 0.5;
-		this.newSamplesCorrection = 0.75;
+		this.newSamplesSmoothing = 0.675;
+		this.newSamplesCorrection = 1;
 
 		this.fullscreenMaterial = new ComposeTemporalResolveMaterial();
 
@@ -35,6 +35,23 @@ export class TemporalResolve {
 		this.initNewCamera( camera );
 		this.initNewSize( window.innerWidth, window.innerHeight );
 
+		let weightTransform = 0;
+		Object.defineProperty( this, 'weightTransform', {
+			set( value ) {
+
+				weightTransform = value;
+
+				this.temporalResolvePass.fullscreenMaterial.defines.WEIGHT_TRANSFORM = ( 1 - value ).toFixed( 5 );
+				this.temporalResolvePass.fullscreenMaterial.needsUpdate = true;
+
+			},
+			get() {
+
+				return weightTransform;
+
+			}
+		} );
+
 	}
 
 	initNewCamera( camera ) {
@@ -71,6 +88,8 @@ export class TemporalResolve {
 
 	update() {
 
+		while ( this.ptRenderer.samples < 1 ) this.ptRenderer.update();
+
 		const renderer = this.ptRenderer._renderer;
 
 		const origRenderTarget = renderer.getRenderTarget();
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index c06b20c81..b9f0c454d 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -9,10 +9,10 @@ export class TemporalResolveMaterial extends ShaderMaterial {
 		super( {
 			type: 'TemporalResolveMaterial',
 			uniforms: {
-				inputTexture: { value: null },
 				samplesTexture: { value: null },
 				accumulatedSamplesTexture: { value: null },
 				velocityTexture: { value: null },
+				lastVelocityTexture: { value: null },
 				depthTexture: { value: null },
 				lastDepthTexture: { value: null },
 				samples: { value: 0 },
@@ -27,6 +27,10 @@ export class TemporalResolveMaterial extends ShaderMaterial {
 				cameraNear: { value: 0 },
 				cameraFar: { value: 0 },
 			},
+			defines: {
+				DILATION: '',
+				WEIGHT_TRANSFORM: '1.0'
+			},
 			vertexShader,
 			fragmentShader,
 		} );
diff --git a/src/temporal-resolve/materials/VelocityShader.js b/src/temporal-resolve/materials/VelocityShader.js
index f491df9dc..3d5568bf4 100644
--- a/src/temporal-resolve/materials/VelocityShader.js
+++ b/src/temporal-resolve/materials/VelocityShader.js
@@ -1,4 +1,7 @@
-import { Matrix4, ShaderChunk } from 'three';
+// this shader is from: https://github.com/gkjohnson/threejs-sandbox
+
+/* eslint-disable camelcase */
+import { ShaderChunk, Matrix4 } from 'three';
 
 // Modified ShaderChunk.skinning_pars_vertex to handle
 // a second set of bone information from the previou frame
@@ -48,9 +51,7 @@ export const velocity_vertex = /* glsl */ `
 
 		// Get the previous vertex position
 		transformed = vec3( position );
-		${ShaderChunk.skinbase_vertex
-		.replace( /mat4 /g, '' )
-		.replace( /getBoneMatrix/g, 'getPrevBoneMatrix' )}
+		${ShaderChunk.skinbase_vertex.replace( /mat4 /g, '' ).replace( /getBoneMatrix/g, 'getPrevBoneMatrix' )}
 		${ShaderChunk.skinning_vertex.replace( /vec4 /g, '' )}
 		prevPosition = prevVelocityMatrix * vec4( transformed, 1.0 );
 
@@ -70,10 +71,12 @@ export const VelocityShader = {
 		alphaTest: { value: 0.0 },
 		map: { value: null },
 		alphaMap: { value: null },
-		opacity: { value: 1.0 },
+		opacity: { value: 1.0 }
 	},
 
 	vertexShader: /* glsl */ `
+			#define MAX_BONES 1024
+			
 			${ShaderChunk.skinning_pars_vertex}
 			${prev_skinning_pars_vertex}
 
@@ -101,11 +104,8 @@ export const VelocityShader = {
 
 				vec2 vel = pos1 - pos0;
 				
-				gl_FragColor = vec4( vel, 0., 1. );
+				gl_FragColor = vec4( vel, 1. - gl_FragCoord.z, 0. );
 
 			}
-		`,
-	defines: {
-		MAX_BONES: 256,
-	},
+		`
 };
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index e9831d842..5ac810ecc 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -1,5 +1,4 @@
 export const fragmentShader = /* glsl */ `
-uniform sampler2D inputTexture;
 uniform sampler2D samplesTexture;
 uniform sampler2D accumulatedSamplesTexture;
 uniform sampler2D velocityTexture;
@@ -9,7 +8,7 @@ uniform sampler2D lastDepthTexture;
 uniform float samples;
 
 uniform float temporalResolveMix;
-uniform int clampRadius;
+uniform float clampRadius;
 uniform float newSamplesSmoothing;
 uniform float newSamplesCorrection;
 
@@ -26,6 +25,9 @@ varying vec2 vUv;
 
 #include 
 
+#define FLOAT_EPSILON 0.00001
+#define BLUR_EXPONENT 0.25
+
 // credits for transforming screen position to world position: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532/2
 vec3 screenSpaceToWorldSpace(const vec2 uv, const float depth, mat4 inverseProjectionMatrix, mat4 cameraMatrixWorld) {
     vec4 ndc = vec4(
@@ -40,88 +42,136 @@ vec3 screenSpaceToWorldSpace(const vec2 uv, const float depth, mat4 inverseProje
     return view.xyz;
 }
 
-void main() {
-	vec4 samplesTexel = texture2D(samplesTexture, vUv);
+#ifdef DILATION
+// source: https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/ (modified to GLSL)
+vec4 getDilatedTexture(sampler2D tex, vec2 uv, vec2 texSize) {
+    float closestDepth = 0.;
+    vec2 closestUVOffset;
 
-	// in case this pixel is from a tile that wasn't rendered yet
-	if(samplesTexel.a == 0.){
-		gl_FragColor = vec4(texture2D(inputTexture, vUv).rgb, 0.);
-		return;
-	}
+    for (int j = -1; j <= 1; ++j) {
+        for (int i = -1; i <= 1; ++i) {
+            vec2 uvOffset = vec2(i, j) / texSize;
 
-	vec4 depthTexel = texture2D(depthTexture, vUv);
-		
-	// background doesn't need reprojection
-	if(length(depthTexel.xyz) == 0.){
-		gl_FragColor = samplesTexel;
-		return;
-	}
+            float neighborDepth = textureLod(tex, vUv + uvOffset, 0.).b;
 
-	float unpackedDepth = unpackRGBAToDepth(depthTexel);
-	vec3 curWorldPos = screenSpaceToWorldSpace(vUv, unpackedDepth, curInverseProjectionMatrix, curCameraMatrixWorld);
+            if (neighborDepth > closestDepth) {
+                closestUVOffset = uvOffset;
+                closestDepth = neighborDepth;
+            }
+        }
+    }
 
+    return textureLod(tex, vUv + closestUVOffset, 0.);
+}
+#endif
+
+// idea from: https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/
+vec3 transformColor(vec3 color) {
+    return pow(color, vec3(WEIGHT_TRANSFORM));
+}
+
+vec3 undoColorTransform(vec3 color) {
+	return pow(color, vec3(1. / WEIGHT_TRANSFORM));
+}
+
+void main() {
+	vec4 samplesTexel = texture2D(samplesTexture, vUv);
+	samplesTexel.rgb = transformColor(samplesTexel.rgb);
 
 	ivec2 size = textureSize(samplesTexture, 0);
-	vec2 pxSize = vec2(float(size.x), float(size.y));
+	vec2 pxSize = vec2(size.x, size.y);
 
-    vec4 velocityTexel = texture2D(velocityTexture, vUv);
+#ifdef DILATION
+    vec4 velocity = getDilatedTexture(velocityTexture, vUv, pxSize);
 
-    vec2 velUv = velocityTexel.xy;
-    float movement = length(velUv) * 100.;
+	vec2 velUv = velocity.xy;
+    vec2 reprojectedUv = vUv - velUv;
+#else
+    vec4 velocity = textureLod(velocityTexture, vUv, 0.);
 
+	vec2 velUv = velocity.xy;
     vec2 reprojectedUv = vUv - velUv;
+#endif
+
+	// background doesn't need reprojection
+	if(velocity.a == 1.){
+		samplesTexel.rgb = undoColorTransform(samplesTexel.rgb);
+		gl_FragColor = samplesTexel;
+		return;
+	}
 
-	float lastUnpackedDepth = unpackRGBAToDepth(texture2D(lastDepthTexture, reprojectedUv));
+	// depth textures should not be dilated
+	float unpackedDepth = unpackRGBAToDepth(textureLod(depthTexture, vUv, 0.));
+	float lastUnpackedDepth = unpackRGBAToDepth(textureLod(lastDepthTexture, reprojectedUv, 0.));
 
-	vec3 lastWorldPos = screenSpaceToWorldSpace(vUv, lastUnpackedDepth, prevInverseProjectionMatrix, prevCameraMatrixWorld);
+    float movement = length(velUv) * 100.;
 
-	float distToLastFrame = pow(distance(curWorldPos, lastWorldPos), 2.) * 0.25;
+	vec3 curWorldPos = screenSpaceToWorldSpace(vUv, unpackedDepth, curInverseProjectionMatrix, curCameraMatrixWorld);
+	vec3 lastWorldPos = screenSpaceToWorldSpace(vUv, lastUnpackedDepth, prevInverseProjectionMatrix, prevCameraMatrixWorld);
+	float distToLastFrame = length(curWorldPos - lastWorldPos) * 0.25;
 
 	vec4 accumulatedSamplesTexel;
-    vec3 newColor;
+    vec3 outputColor;
 	float alpha;
 
-	// check that the reprojected UV is valid
-	if (reprojectedUv.x >= 0. && reprojectedUv.x <= 1. && reprojectedUv.y >= 0. && reprojectedUv.y <= 1.) {
-		accumulatedSamplesTexel = texture2D(accumulatedSamplesTexture, reprojectedUv);
-		alpha = accumulatedSamplesTexel.a;
-        alpha = distToLastFrame < 0.05 ? (0.1 + alpha) : 0.;
-		
-		vec2 px = 1. / pxSize;
-
-		vec3 minNeighborColor = vec3(1., 1., 1.);
-		vec3 maxNeighborColor = vec3(0., 0., 0.);
+	bool canReproject = reprojectedUv.x >= 0. && reprojectedUv.x <= 1. && reprojectedUv.y >= 0. && reprojectedUv.y <= 1.;
 
-		vec3 totalColor;
-
-		// use a small ring if there is a lot of movement otherwise there will be more smearing
-		int ring = movement > 1. ? 1 : clampRadius;
+	// check that the reprojected UV is valid
+	if (canReproject) {
+		accumulatedSamplesTexel = textureLod(accumulatedSamplesTexture, reprojectedUv, 0.);
+		accumulatedSamplesTexel.rgb = transformColor(accumulatedSamplesTexel.rgb);
 		
-		for(int x = -ring; x <= ring; x++){
-			for(int y = -ring; y <= ring; y++){
-				vec3 col;
-
-				if(x == 0 && y == 0){
-					col = samplesTexel.rgb;
-				}else{
-					vec2 curOffset = vec2(float(x), float(y));
-
-					col = textureLod(samplesTexture, vUv + px * curOffset, 0.).rgb;
+        alpha = distToLastFrame < 0.05 ? (accumulatedSamplesTexel.a + 0.05) : 0.;
+		alpha = clamp(alpha, 0., 1.);
+
+		if(samplesTexel.a != 0.){
+			vec2 px = 1. / pxSize;
+
+			vec3 boxBlurredColor;
+			float totalWeight;
+
+			vec3 minNeighborColor = vec3(1., 1., 1.);
+			vec3 maxNeighborColor = vec3(0., 0., 0.);
+
+			// use a small ring if there is a lot of movement otherwise there will be more smearing
+			float radius = movement > 1. ? 1. : clampRadius;
+
+			vec3 col;
+			float weight;
+			vec2 neighborUv;
+			bool neighborUvValid;
+			
+			for(float x = -radius; x <= radius; x++){
+				for(float y = -radius; y <= radius; y++){
+					neighborUv = vUv + px * vec2(x, y);
+					neighborUvValid = neighborUv.x >= 0. && neighborUv.x <= 1. && neighborUv.y >= 0. && neighborUv.y <= 1.;
+
+					if(!neighborUvValid) continue;
+
+					col = textureLod(samplesTexture, neighborUv, 0.).rgb;
+					col = transformColor(col);
+
+					// box blur
+					if(abs(x) <= 1. && abs(y) <= 1.){
+						weight = 1.0 - abs(dot(col - samplesTexel.rgb, vec3(0.25)));
+						weight = pow(weight, BLUR_EXPONENT);
+						boxBlurredColor += col * weight;
+						totalWeight += weight;
+					}
+
+					minNeighborColor = min(col, minNeighborColor);
+					maxNeighborColor = max(col, maxNeighborColor);
 				}
-
-				minNeighborColor = min(col, minNeighborColor);
-				maxNeighborColor = max(col, maxNeighborColor);
-
-				if(x <= 1 && x >= -1 && y <= 1 && y >= -1) totalColor += col;
 			}
-		}
 
-		// clamp the reprojected frame (neighborhood clamping)
-		accumulatedSamplesTexel.rgb = clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor);
+			// clamp the reprojected frame (neighborhood clamping)
+			accumulatedSamplesTexel.rgb = clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor);
 
-		if(newSamplesSmoothing != 0. && alpha < 1.){
-			totalColor /= 9.;
-			samplesTexel.rgb = mix(samplesTexel.rgb, totalColor, newSamplesSmoothing);
+			// let's blur the input color to reduce noise for new samples
+			if(newSamplesSmoothing != 0. && alpha < 1. && totalWeight > FLOAT_EPSILON){
+				boxBlurredColor /= totalWeight;
+				samplesTexel.rgb = mix(samplesTexel.rgb, boxBlurredColor, newSamplesSmoothing);
+			}
 		}
 	} else {
 		// reprojected UV coordinates are outside of screen, so just use the current frame for it
@@ -129,20 +179,24 @@ void main() {
 		accumulatedSamplesTexel.rgb = samplesTexel.rgb;
 	}
 
-	float m = (1. - min(movement * 2., 1.) * (1. - temporalResolveMix)) - (samples - 1.) * 0.01 - 0.025;
-	
+	float m = temporalResolveMix - (samples - 1.) * 0.00675;
 	m = clamp(m, 0., 1.);
 	
-	newColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
+	outputColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
 
 	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
-	// so make the final color blend more towards the new pixel
+	// so make the final color blend more towards the new pixel	
 	if(alpha < 1.){
-		float correctionMix = min(movement, 0.5) * newSamplesCorrection;
+		m = (distToLastFrame * distToLastFrame - 0.01) * 50.;
+		m = clamp(m, 0.2, 0.6);
 
-		newColor = mix(newColor, samplesTexel.rgb, correctionMix);
+		outputColor = mix(accumulatedSamplesTexel.rgb, samplesTexel.rgb, m);
 	}
 
-    gl_FragColor = vec4(newColor, alpha);
+	if(movement > 3.) outputColor = mix(accumulatedSamplesTexel.rgb, samplesTexel.rgb, 0.6);
+
+	outputColor = undoColorTransform(outputColor);
+
+    gl_FragColor = vec4(outputColor, alpha);
 }
 `;
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
index 06533a637..8d522b594 100644
--- a/src/temporal-resolve/passes/TemporalResolvePass.js
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -4,7 +4,6 @@
 	HalfFloatType,
 	LinearFilter,
 	MeshDepthMaterial,
-	NearestFilter,
 	RGBADepthPacking,
 	RGBAFormat,
 	Vector2,
@@ -52,8 +51,8 @@ export class TemporalResolvePass {
 			typeof window !== 'undefined' ? window.innerWidth : 2000,
 			typeof window !== 'undefined' ? window.innerHeight : 1000,
 			{
-				minFilter: NearestFilter,
-				magFilter: NearestFilter,
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
 			}
 		);
 
@@ -63,10 +62,9 @@ export class TemporalResolvePass {
 
 		this.fullscreenMaterial.uniforms.velocityTexture.value =
 			this.velocityPass.renderTarget.texture;
+
 		this.fullscreenMaterial.uniforms.depthTexture.value =
 			this.depthRenderTarget.texture;
-		this.fullscreenMaterial.uniforms.lastDepthTexture.value =
-			this.lastDepthTexture;
 
 		this.fsQuad = new FullScreenQuad( null );
 		this.fsQuad.material = this.fullscreenMaterial;
@@ -106,14 +104,13 @@ export class TemporalResolvePass {
 			height,
 			RGBAFormat
 		);
-
 		this.accumulatedSamplesTexture.minFilter = LinearFilter;
 		this.accumulatedSamplesTexture.magFilter = LinearFilter;
 		this.accumulatedSamplesTexture.type = HalfFloatType;
 
 		this.lastDepthTexture = new FramebufferTexture( width, height, RGBAFormat );
-		this.lastDepthTexture.minFilter = NearestFilter;
-		this.lastDepthTexture.magFilter = NearestFilter;
+		this.lastDepthTexture.minFilter = LinearFilter;
+		this.lastDepthTexture.magFilter = LinearFilter;
 
 		this.fullscreenMaterial.uniforms.accumulatedSamplesTexture.value =
 			this.accumulatedSamplesTexture;
@@ -130,34 +127,21 @@ export class TemporalResolvePass {
 		this.scene.overrideMaterial = meshDepthMaterial;
 		renderer.setRenderTarget( this.depthRenderTarget );
 		renderer.clear();
-		const { background } = this.scene;
 
+		const { background } = this.scene;
 		this.scene.background = blackColor;
+
 		renderer.render( this.scene, this.camera );
+
 		this.scene.background = background;
 		this.scene.overrideMaterial = null;
 
 		// render velocity
 		this.velocityPass.render( renderer );
 
-		if ( this.ptRenderer.tiles.x !== 1 || this.ptRenderer.tiles.y !== 1 ) {
-
-			if ( this.ptRenderer.samples < 1 ) {
-
-				renderer.setRenderTarget( this.sceneRenderTarget );
-				renderer.clear();
-				renderer.render( this.scene, this.camera );
-
-				this.fullscreenMaterial.uniforms.inputTexture.value =
-					this.sceneRenderTarget.texture;
-
-			}
-
-		} else {
-
-			delete this.fullscreenMaterial.uniforms.inputTexture.value;
-
-		}
+		renderer.setRenderTarget( this.sceneRenderTarget );
+		renderer.clear();
+		renderer.render( this.scene, this.camera );
 
 		// update uniforms of this pass
 		this.fullscreenMaterial.uniforms.curInverseProjectionMatrix.value.copy(
diff --git a/src/temporal-resolve/passes/VelocityPass.js b/src/temporal-resolve/passes/VelocityPass.js
index dbd3b0e25..98ff9d4d3 100644
--- a/src/temporal-resolve/passes/VelocityPass.js
+++ b/src/temporal-resolve/passes/VelocityPass.js
@@ -1,12 +1,19 @@
 import {
+	Color,
+	DataTexture,
+	FloatType,
 	HalfFloatType,
-	NearestFilter,
+	LinearFilter,
+	RGBAFormat,
 	ShaderMaterial,
 	UniformsUtils,
 	WebGLRenderTarget
 } from 'three';
 import { VelocityShader } from '../materials/VelocityShader';
 
+const backgroundColor = new Color( 0 );
+const updateProperties = [ 'visible', 'wireframe', 'side' ];
+
 export class VelocityPass {
 
 	constructor( scene, camera ) {
@@ -20,8 +27,8 @@ export class VelocityPass {
 			typeof window !== 'undefined' ? window.innerWidth : 2000,
 			typeof window !== 'undefined' ? window.innerHeight : 1000,
 			{
-				minFilter: NearestFilter,
-				magFilter: NearestFilter,
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
 				type: HalfFloatType,
 			}
 		);
@@ -30,27 +37,25 @@ export class VelocityPass {
 
 	setVelocityMaterialInScene() {
 
-		this.scene.traverse( ( c ) => {
+		this.scene.traverse( c => {
 
 			if ( c.material ) {
 
 				const originalMaterial = c.material;
 
 				// eslint-disable-next-line prefer-const
-				let [ cachedOriginalMaterial, velocityMaterial ] =
-					this.cachedMaterials.get( c ) || [];
+				let [ cachedOriginalMaterial, velocityMaterial ] = this.cachedMaterials.get( c ) || [];
 
-				if (
-					! this.cachedMaterials.has( c ) ||
-					originalMaterial !== cachedOriginalMaterial
-				) {
+				if ( originalMaterial !== cachedOriginalMaterial ) {
 
 					velocityMaterial = new ShaderMaterial( {
 						uniforms: UniformsUtils.clone( VelocityShader.uniforms ),
 						vertexShader: VelocityShader.vertexShader,
-						fragmentShader: VelocityShader.fragmentShader,
+						fragmentShader: VelocityShader.fragmentShader
 					} );
 
+					if ( c.skeleton && c.skeleton.boneTexture ) this.saveBoneTexture( c );
+
 					this.cachedMaterials.set( c, [ originalMaterial, velocityMaterial ] );
 
 				}
@@ -60,6 +65,17 @@ export class VelocityPass {
 					c.modelViewMatrix
 				);
 
+				for ( const prop of updateProperties ) velocityMaterial[ prop ] = originalMaterial[ prop ];
+
+				if ( c.skeleton ) {
+
+					velocityMaterial.defines.USE_SKINNING = '';
+					velocityMaterial.defines.BONE_TEXTURE = '';
+
+					velocityMaterial.uniforms.boneTexture.value = c.skeleton.boneTexture;
+
+				}
+
 				c.material = velocityMaterial;
 
 			}
@@ -68,15 +84,40 @@ export class VelocityPass {
 
 	}
 
+	saveBoneTexture( object ) {
+
+		let boneTexture = object.material.uniforms.prevBoneTexture.value;
+
+		if ( boneTexture && boneTexture.image.width === object.skeleton.boneTexture.width ) {
+
+			boneTexture = object.material.uniforms.prevBoneTexture.value;
+			boneTexture.image.data.set( object.skeleton.boneTexture.image.data );
+
+		} else {
+
+			if ( boneTexture ) boneTexture.dispose();
+
+			const boneMatrices = object.skeleton.boneTexture.image.data.slice();
+			const size = object.skeleton.boneTexture.image.width;
+
+			boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
+			object.material.uniforms.prevBoneTexture.value = boneTexture;
+
+			boneTexture.needsUpdate = true;
+
+		}
+
+	}
+
 	unsetVelocityMaterialInScene() {
 
-		this.scene.traverse( ( c ) => {
+		this.scene.traverse( c => {
 
 			if ( c.material ) {
 
-				c.material.uniforms.prevVelocityMatrix.value.copy(
-					c.material.uniforms.velocityMatrix.value
-				);
+				c.material.uniforms.prevVelocityMatrix.value.multiplyMatrices( this.camera.projectionMatrix, c.modelViewMatrix );
+
+				if ( c.skeleton && c.skeleton.boneTexture ) this.saveBoneTexture( c );
 
 				const [ originalMaterial ] = this.cachedMaterials.get( c );
 
@@ -106,8 +147,13 @@ export class VelocityPass {
 
 		renderer.setRenderTarget( this.renderTarget );
 		renderer.clear();
+		const { background } = this.scene;
+		this.scene.background = backgroundColor;
+
 		renderer.render( this.scene, this.camera );
 
+		this.scene.background = background;
+
 		this.unsetVelocityMaterialInScene();
 
 	}
From 57b6b404ba7a94bd58773c903bd76d2b5ab65101 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Fri, 5 Aug 2022 05:58:55 +0200
Subject: [PATCH 10/25] Update readme
---
 README.md | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 0ad02d415..bcad92779 100644
--- a/README.md
+++ b/README.md
@@ -389,7 +389,7 @@ A class that implements temporal filtering to preserve samples when the camera i
 ### .temporalResolveMix
 
 ```javascript
-temporalResolveMix = 0.75 : Number
+temporalResolveMix = 0.9 : Number
 ```
 
 How much the last frame should be blended into the current one. Higher values will result in a less noisy look at the cost of more smearing.
@@ -405,7 +405,7 @@ An integer to set the radius of pixels to be used for neighborhood clamping. Hig
 ### .newSamplesSmoothing
 
 ```javascript
-newSamplesSmoothing = 0.5 : Number
+newSamplesSmoothing = 0.675 : Number
 ```
 
 To reduce noise for pixels that appeared recently, the average of multiple adjacent pixels can be used instead of a pixel itself. This factor determines the influence of the averaged pixel. Higher values will result in less noise but also less sharpness.
@@ -413,11 +413,19 @@ To reduce noise for pixels that appeared recently, the average of multiple adjac
 ### .newSamplesCorrection
 
 ```javascript
-newSamplesCorrection = 0.75 : Number
+newSamplesCorrection = 1 : Number
 ```
 
 Higher values will make pixels that appeared recently have a greater influence on the output. This will result in more noise but less smearing.
 
+### .weightTransform
+
+```javascript
+weightTransform = 0 : Number
+```
+
+This will potentiate the input color by `1 / (1 - weightTransform)` resulting in less contrast and noise when moving the camera. Higher values will highlight darker areas more.
+
 ### .constructor
 
 ```javascript
From f85758c13ad0b5f02f1e17fe13ebece52d5257a2 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Mon, 8 Aug 2022 18:31:05 +0200
Subject: [PATCH 11/25] Update
---
 example/materialBall.js                       |  8 +++++
 .../shaders/temporalResolveFragment.js        | 30 +++++++++----------
 2 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/example/materialBall.js b/example/materialBall.js
index 005af4b8c..8e717bb67 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -677,6 +677,14 @@ function animate() {
 
 	renderer.autoClear = false;
 	quad.material.map = ptRenderer.target.texture;
+
+	if ( params.temporalResolve ) {
+
+		temporalResolve.update();
+		quad.material.map = temporalResolve.target.texture;
+
+	}
+
 	quad.render( renderer );
 	renderer.autoClear = true;
 
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 5ac810ecc..59d4360ac 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -146,26 +146,26 @@ void main() {
 					neighborUv = vUv + px * vec2(x, y);
 					neighborUvValid = neighborUv.x >= 0. && neighborUv.x <= 1. && neighborUv.y >= 0. && neighborUv.y <= 1.;
 
-					if(!neighborUvValid) continue;
-
-					col = textureLod(samplesTexture, neighborUv, 0.).rgb;
-					col = transformColor(col);
-
-					// box blur
-					if(abs(x) <= 1. && abs(y) <= 1.){
-						weight = 1.0 - abs(dot(col - samplesTexel.rgb, vec3(0.25)));
-						weight = pow(weight, BLUR_EXPONENT);
-						boxBlurredColor += col * weight;
-						totalWeight += weight;
+					if(neighborUvValid){
+						col = textureLod(samplesTexture, neighborUv, 0.).rgb;
+						col = transformColor(col);
+
+						// box blur
+						if(abs(x) <= 1. && abs(y) <= 1.){
+							weight = 1.0 - abs(dot(col - samplesTexel.rgb, vec3(0.25)));
+							weight = pow(weight, BLUR_EXPONENT);
+							boxBlurredColor += col * weight;
+							totalWeight += weight;
+						}
+
+						minNeighborColor = min(col, minNeighborColor);
+						maxNeighborColor = max(col, maxNeighborColor);
 					}
-
-					minNeighborColor = min(col, minNeighborColor);
-					maxNeighborColor = max(col, maxNeighborColor);
 				}
 			}
 
 			// clamp the reprojected frame (neighborhood clamping)
-			accumulatedSamplesTexel.rgb = clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor);
+			accumulatedSamplesTexel.rgb = mix(accumulatedSamplesTexel.rgb, clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor), newSamplesCorrection);
 
 			// let's blur the input color to reduce noise for new samples
 			if(newSamplesSmoothing != 0. && alpha < 1. && totalWeight > FLOAT_EPSILON){
From f83b49beea1cbac8d326ddc58acef2ad18569683 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Mon, 8 Aug 2022 22:20:57 +0200
Subject: [PATCH 12/25] Switch back to old blending formula
---
 .../materials/shaders/temporalResolveFragment.js   | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 59d4360ac..839c74693 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -179,22 +179,18 @@ void main() {
 		accumulatedSamplesTexel.rgb = samplesTexel.rgb;
 	}
 
-	float m = temporalResolveMix - (samples - 1.) * 0.00675;
+	float m = (1. - min(movement * 2., 1.) * (1. - temporalResolveMix)) - (samples - 1.) * 0.01 - 0.025;
+	
 	m = clamp(m, 0., 1.);
 	
 	outputColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
-
 	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
-	// so make the final color blend more towards the new pixel	
+	// so make the final color blend more towards the new pixel
 	if(alpha < 1.){
-		m = (distToLastFrame * distToLastFrame - 0.01) * 50.;
-		m = clamp(m, 0.2, 0.6);
-
-		outputColor = mix(accumulatedSamplesTexel.rgb, samplesTexel.rgb, m);
+		float correctionMix = min(movement, 0.5) * newSamplesCorrection;
+		outputColor = mix(outputColor, samplesTexel.rgb, correctionMix);
 	}
 
-	if(movement > 3.) outputColor = mix(accumulatedSamplesTexel.rgb, samplesTexel.rgb, 0.6);
-
 	outputColor = undoColorTransform(outputColor);
 
     gl_FragColor = vec4(outputColor, alpha);
From 3006989dd3f1fdcfba5830add80c916f70319042 Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Sun, 7 Aug 2022 22:20:10 -0700
Subject: [PATCH 13/25] add file extensions
---
 src/temporal-resolve/TemporalResolve.js                     | 6 +++---
 src/temporal-resolve/materials/TemporalResolveMaterial.js   | 4 ++--
 .../materials/shaders/temporalResolveFragment.js            | 6 +++---
 src/temporal-resolve/passes/TemporalResolvePass.js          | 4 ++--
 src/temporal-resolve/passes/VelocityPass.js                 | 2 +-
 5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 55aabd97e..9ecf02eaa 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -1,7 +1,7 @@
 import { HalfFloatType, LinearFilter, WebGLRenderTarget } from 'three';
-import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
-import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial';
-import { TemporalResolvePass } from './passes/TemporalResolvePass';
+import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
+import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial.js';
+import { TemporalResolvePass } from './passes/TemporalResolvePass.js';
 
 export class TemporalResolve {
 
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index b9f0c454d..9875dbfc4 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -1,6 +1,6 @@
 import { Matrix4, ShaderMaterial } from 'three';
-import { fragmentShader } from './shaders/temporalResolveFragment';
-import { vertexShader } from './shaders/temporalResolveVertex';
+import { fragmentShader } from './shaders/temporalResolveFragment.js';
+import { vertexShader } from './shaders/temporalResolveVertex.js';
 
 export class TemporalResolveMaterial extends ShaderMaterial {
 
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 839c74693..44dfd86d0 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -120,7 +120,7 @@ void main() {
 	if (canReproject) {
 		accumulatedSamplesTexel = textureLod(accumulatedSamplesTexture, reprojectedUv, 0.);
 		accumulatedSamplesTexel.rgb = transformColor(accumulatedSamplesTexel.rgb);
-		
+
         alpha = distToLastFrame < 0.05 ? (accumulatedSamplesTexel.a + 0.05) : 0.;
 		alpha = clamp(alpha, 0., 1.);
 
@@ -140,7 +140,7 @@ void main() {
 			float weight;
 			vec2 neighborUv;
 			bool neighborUvValid;
-			
+
 			for(float x = -radius; x <= radius; x++){
 				for(float y = -radius; y <= radius; y++){
 					neighborUv = vUv + px * vec2(x, y);
@@ -182,7 +182,7 @@ void main() {
 	float m = (1. - min(movement * 2., 1.) * (1. - temporalResolveMix)) - (samples - 1.) * 0.01 - 0.025;
 	
 	m = clamp(m, 0., 1.);
-	
+
 	outputColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
 	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
 	// so make the final color blend more towards the new pixel
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
index 8d522b594..aa48d5df5 100644
--- a/src/temporal-resolve/passes/TemporalResolvePass.js
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -10,8 +10,8 @@
 	WebGLRenderTarget
 } from 'three';
 import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
-import { TemporalResolveMaterial } from '../materials/TemporalResolveMaterial';
-import { VelocityPass } from './VelocityPass';
+import { TemporalResolveMaterial } from '../materials/TemporalResolveMaterial.js';
+import { VelocityPass } from './VelocityPass.js';
 
 const zeroVec2 = new Vector2();
 const meshDepthMaterial = new MeshDepthMaterial( {
diff --git a/src/temporal-resolve/passes/VelocityPass.js b/src/temporal-resolve/passes/VelocityPass.js
index 98ff9d4d3..467a74034 100644
--- a/src/temporal-resolve/passes/VelocityPass.js
+++ b/src/temporal-resolve/passes/VelocityPass.js
@@ -9,7 +9,7 @@
 	UniformsUtils,
 	WebGLRenderTarget
 } from 'three';
-import { VelocityShader } from '../materials/VelocityShader';
+import { VelocityShader } from '../materials/VelocityShader.js';
 
 const backgroundColor = new Color( 0 );
 const updateProperties = [ 'visible', 'wireframe', 'side' ];
From 273a115e576f31c351b62238399c7b089c41dde0 Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Sun, 7 Aug 2022 22:23:47 -0700
Subject: [PATCH 14/25] Use MaterialBase
---
 src/temporal-resolve/TemporalResolve.js       | 22 +++++++++----------
 .../materials/TemporalResolveMaterial.js      |  5 +++--
 .../materials/VelocityShader.js               |  8 +++----
 3 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 9ecf02eaa..f2324d9f0 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -5,6 +5,12 @@ import { TemporalResolvePass } from './passes/TemporalResolvePass.js';
 
 export class TemporalResolve {
 
+	get target() {
+
+		return this.renderTarget;
+
+	}
+
 	constructor( ptRenderer, scene, camera ) {
 
 		this.ptRenderer = ptRenderer;
@@ -80,12 +86,6 @@ export class TemporalResolve {
 
 	}
 
-	get target() {
-
-		return this.renderTarget;
-
-	}
-
 	update() {
 
 		while ( this.ptRenderer.samples < 1 ) this.ptRenderer.update();
@@ -122,19 +122,19 @@ export class TemporalResolve {
 		} );
 
 		// keep uniforms updated
-		this.temporalResolvePass.fullscreenMaterial.uniforms.samples.value =
+		this.temporalResolvePass.fullscreenMaterial.samples =
 			this.ptRenderer.samples;
 
-		this.temporalResolvePass.fullscreenMaterial.uniforms.temporalResolveMix.value =
+		this.temporalResolvePass.fullscreenMaterial.temporalResolveMix =
 			this.temporalResolveMix;
 
-		this.temporalResolvePass.fullscreenMaterial.uniforms.clampRadius.value =
+		this.temporalResolvePass.fullscreenMaterial.clampRadius =
 			parseInt( this.clampRadius );
 
-		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesSmoothing.value =
+		this.temporalResolvePass.fullscreenMaterial.newSamplesSmoothing =
 			this.newSamplesSmoothing;
 
-		this.temporalResolvePass.fullscreenMaterial.uniforms.newSamplesCorrection.value =
+		this.temporalResolvePass.fullscreenMaterial.newSamplesCorrection =
 			this.newSamplesCorrection;
 
 		this.temporalResolvePass.render( renderer );
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index 9875dbfc4..d9397f20a 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -1,8 +1,9 @@
-import { Matrix4, ShaderMaterial } from 'three';
+import { Matrix4 } from 'three';
+import { MaterialBase } from '../../materials/MaterialBase.js';
 import { fragmentShader } from './shaders/temporalResolveFragment.js';
 import { vertexShader } from './shaders/temporalResolveVertex.js';
 
-export class TemporalResolveMaterial extends ShaderMaterial {
+export class TemporalResolveMaterial extends MaterialBase {
 
 	constructor() {
 
diff --git a/src/temporal-resolve/materials/VelocityShader.js b/src/temporal-resolve/materials/VelocityShader.js
index 3d5568bf4..9000f0ab1 100644
--- a/src/temporal-resolve/materials/VelocityShader.js
+++ b/src/temporal-resolve/materials/VelocityShader.js
@@ -1,10 +1,8 @@
 // this shader is from: https://github.com/gkjohnson/threejs-sandbox
-
-/* eslint-disable camelcase */
 import { ShaderChunk, Matrix4 } from 'three';
 
 // Modified ShaderChunk.skinning_pars_vertex to handle
-// a second set of bone information from the previou frame
+// a second set of bone information from the previous frame
 export const prev_skinning_pars_vertex = /* glsl */ `
 		#ifdef USE_SKINNING
 		#ifdef BONE_TEXTURE
@@ -76,7 +74,7 @@ export const VelocityShader = {
 
 	vertexShader: /* glsl */ `
 			#define MAX_BONES 1024
-			
+
 			${ShaderChunk.skinning_pars_vertex}
 			${prev_skinning_pars_vertex}
 
@@ -103,7 +101,7 @@ export const VelocityShader = {
 				vec2 pos1 = (newPosition.xy / newPosition.w) * 0.5 + 0.5;
 
 				vec2 vel = pos1 - pos0;
-				
+
 				gl_FragColor = vec4( vel, 1. - gl_FragCoord.z, 0. );
 
 			}
From 5bcb882a86436cde9a68d95968675cf8114fbcfd Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Sun, 7 Aug 2022 22:30:00 -0700
Subject: [PATCH 15/25] more clean up
---
 src/temporal-resolve/TemporalResolve.js | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index f2324d9f0..c7bec4887 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -16,6 +16,7 @@ export class TemporalResolve {
 		this.ptRenderer = ptRenderer;
 		this.scene = scene;
 
+		// parameters
 		this.temporalResolveMix = 0.9;
 		this.clampRadius = 1;
 		this.newSamplesSmoothing = 0.675;
@@ -25,16 +26,12 @@ export class TemporalResolve {
 
 		this.fsQuad = new FullScreenQuad( this.fullscreenMaterial );
 
-		this.renderTarget = new WebGLRenderTarget(
-			typeof window !== 'undefined' ? window.innerWidth : 2000,
-			typeof window !== 'undefined' ? window.innerHeight : 1000,
-			{
-				minFilter: LinearFilter,
-				magFilter: LinearFilter,
-				type: HalfFloatType,
-				depthBuffer: false,
-			}
-		);
+		this.renderTarget = new WebGLRenderTarget( 1, 1, {
+			minFilter: LinearFilter,
+			magFilter: LinearFilter,
+			type: HalfFloatType,
+			depthBuffer: false,
+		} );
 
 		this.lastSize = { width: 0, height: 0 };
 
@@ -69,10 +66,10 @@ export class TemporalResolve {
 			this.scene,
 			camera
 		);
-		this.temporalResolvePass.fullscreenMaterial.uniforms.samplesTexture.value =
+		this.temporalResolvePass.fullscreenMaterial.samplesTexture =
 			this.ptRenderer.target.texture;
 
-		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
+		this.fullscreenMaterial.temporalResolveTexture =
 			this.temporalResolvePass.renderTarget.texture;
 
 	}
From 97eadd97b61cd2f1902d6f65932d85038df88172 Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Sun, 7 Aug 2022 22:39:56 -0700
Subject: [PATCH 16/25] some shader style fixups
---
 .../materials/ComposeTemporalResolveMaterial.js           | 8 ++++++--
 .../materials/shaders/temporalResolveVertex.js            | 4 +++-
 2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
index d366eeb89..b30138c2e 100644
--- a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
@@ -4,8 +4,10 @@ const vertexShader = /* glsl */ `
     varying vec2 vUv;
 
     void main() {
+
         vUv = position.xy * 0.5 + 0.5;
-        gl_Position = vec4(position.xy, 1.0, 1.0);
+        gl_Position = vec4( position.xy, 1.0, 1.0 );
+
     }
 `;
 
@@ -15,7 +17,9 @@ const fragmentShader = /* glsl */ `
     uniform sampler2D temporalResolveTexture;
 
     void main() {
-        gl_FragColor = vec4(texture2D(temporalResolveTexture, vUv).rgb, 1.);
+
+        gl_FragColor = vec4( texture2D( temporalResolveTexture, vUv ).rgb, 1.0 );
+
     }
 `;
 
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveVertex.js b/src/temporal-resolve/materials/shaders/temporalResolveVertex.js
index ae9654b87..d30508647 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveVertex.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveVertex.js
@@ -2,7 +2,9 @@
 varying vec2 vUv;
 
 void main() {
+
     vUv = position.xy * 0.5 + 0.5;
-    gl_Position = vec4(position.xy, 1.0, 1.0);
+    gl_Position = vec4( position.xy, 1.0, 1.0 );
+
 }
 `;
From 379685ff26a81bae32d0e9882cfb7a067e8fe6fb Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Sun, 7 Aug 2022 22:44:49 -0700
Subject: [PATCH 17/25] more shader cleanup
---
 src/temporal-resolve/TemporalResolve.js       | 20 +++++++------
 .../materials/VelocityShader.js               | 28 +++++++++++--------
 2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index c7bec4887..ffd802a6c 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -26,12 +26,16 @@ export class TemporalResolve {
 
 		this.fsQuad = new FullScreenQuad( this.fullscreenMaterial );
 
-		this.renderTarget = new WebGLRenderTarget( 1, 1, {
-			minFilter: LinearFilter,
-			magFilter: LinearFilter,
-			type: HalfFloatType,
-			depthBuffer: false,
-		} );
+		this.renderTarget = new WebGLRenderTarget(
+			typeof window !== 'undefined' ? window.innerWidth : 2000,
+			typeof window !== 'undefined' ? window.innerHeight : 1000,
+			{
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
+				type: HalfFloatType,
+				depthBuffer: false,
+			}
+		);
 
 		this.lastSize = { width: 0, height: 0 };
 
@@ -66,10 +70,10 @@ export class TemporalResolve {
 			this.scene,
 			camera
 		);
-		this.temporalResolvePass.fullscreenMaterial.samplesTexture =
+		this.temporalResolvePass.fullscreenMaterial.uniforms.samplesTexture.value =
 			this.ptRenderer.target.texture;
 
-		this.fullscreenMaterial.temporalResolveTexture =
+		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
 			this.temporalResolvePass.renderTarget.texture;
 
 	}
diff --git a/src/temporal-resolve/materials/VelocityShader.js b/src/temporal-resolve/materials/VelocityShader.js
index 9000f0ab1..fdef3dfea 100644
--- a/src/temporal-resolve/materials/VelocityShader.js
+++ b/src/temporal-resolve/materials/VelocityShader.js
@@ -1,4 +1,5 @@
 // this shader is from: https://github.com/gkjohnson/threejs-sandbox
+
 import { ShaderChunk, Matrix4 } from 'three';
 
 // Modified ShaderChunk.skinning_pars_vertex to handle
@@ -8,6 +9,7 @@ export const prev_skinning_pars_vertex = /* glsl */ `
 		#ifdef BONE_TEXTURE
 			uniform sampler2D prevBoneTexture;
 			mat4 getPrevBoneMatrix( const in float i ) {
+
 				float j = i * 4.0;
 				float x = mod( j, float( boneTextureSize ) );
 				float y = floor( j / float( boneTextureSize ) );
@@ -20,12 +22,15 @@ export const prev_skinning_pars_vertex = /* glsl */ `
 				vec4 v4 = texture2D( prevBoneTexture, vec2( dx * ( x + 3.5 ), y ) );
 				mat4 bone = mat4( v1, v2, v3, v4 );
 				return bone;
+
 			}
 		#else
 			uniform mat4 prevBoneMatrices[ MAX_BONES ];
 			mat4 getPrevBoneMatrix( const in float i ) {
+
 				mat4 bone = prevBoneMatrices[ int(i) ];
 				return bone;
+
 			}
 		#endif
 		#endif
@@ -37,20 +42,20 @@ export const velocity_vertex = /* glsl */ `
 		vec3 transformed;
 
 		// Get the normal
-		${ShaderChunk.skinbase_vertex}
-		${ShaderChunk.beginnormal_vertex}
-		${ShaderChunk.skinnormal_vertex}
-		${ShaderChunk.defaultnormal_vertex}
+		${ ShaderChunk.skinbase_vertex }
+		${ ShaderChunk.beginnormal_vertex }
+		${ ShaderChunk.skinnormal_vertex }
+		${ ShaderChunk.defaultnormal_vertex }
 
 		// Get the current vertex position
 		transformed = vec3( position );
-		${ShaderChunk.skinning_vertex}
+		${ ShaderChunk.skinning_vertex }
 		newPosition = velocityMatrix * vec4( transformed, 1.0 );
 
 		// Get the previous vertex position
 		transformed = vec3( position );
-		${ShaderChunk.skinbase_vertex.replace( /mat4 /g, '' ).replace( /getBoneMatrix/g, 'getPrevBoneMatrix' )}
-		${ShaderChunk.skinning_vertex.replace( /vec4 /g, '' )}
+		${ ShaderChunk.skinbase_vertex.replace( /mat4 /g, '' ).replace( /getBoneMatrix/g, 'getPrevBoneMatrix' ) }
+		${ ShaderChunk.skinning_vertex.replace( /vec4 /g, '' ) }
 		prevPosition = prevVelocityMatrix * vec4( transformed, 1.0 );
 
 		gl_Position = newPosition;
@@ -86,7 +91,7 @@ export const VelocityShader = {
 
 			void main() {
 
-				${velocity_vertex}
+				${ velocity_vertex }
 
 			}
 		`,
@@ -97,12 +102,13 @@ export const VelocityShader = {
 			varying vec4 newPosition;
 
 			void main() {
-				vec2 pos0 = (prevPosition.xy / prevPosition.w) * 0.5 + 0.5;
-				vec2 pos1 = (newPosition.xy / newPosition.w) * 0.5 + 0.5;
+
+				vec2 pos0 = ( prevPosition.xy / prevPosition.w ) * 0.5 + 0.5;
+				vec2 pos1 = ( newPosition.xy / newPosition.w ) * 0.5 + 0.5;
 
 				vec2 vel = pos1 - pos0;
 
-				gl_FragColor = vec4( vel, 1. - gl_FragCoord.z, 0. );
+				gl_FragColor = vec4( vel, 1. - gl_FragCoord.z, 0.0 );
 
 			}
 		`
From 02753c310b6b3bad6022d972d0070d854374bb32 Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Mon, 8 Aug 2022 00:07:07 -0700
Subject: [PATCH 18/25] more clean up
---
 src/temporal-resolve/TemporalResolve.js       |  8 +--
 .../ComposeTemporalResolveMaterial.js         |  4 +-
 .../passes/TemporalResolvePass.js             | 69 +++++++------------
 3 files changed, 31 insertions(+), 50 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index ffd802a6c..7a6c632dc 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -42,6 +42,7 @@ export class TemporalResolve {
 		this.initNewCamera( camera );
 		this.initNewSize( window.innerWidth, window.innerHeight );
 
+		// TODO: move this to a getter / setter
 		let weightTransform = 0;
 		Object.defineProperty( this, 'weightTransform', {
 			set( value ) {
@@ -70,11 +71,8 @@ export class TemporalResolve {
 			this.scene,
 			camera
 		);
-		this.temporalResolvePass.fullscreenMaterial.uniforms.samplesTexture.value =
-			this.ptRenderer.target.texture;
-
-		this.fullscreenMaterial.uniforms.temporalResolveTexture.value =
-			this.temporalResolvePass.renderTarget.texture;
+		this.temporalResolvePass.fullscreenMaterial.samplesTexture = this.ptRenderer.target.texture;
+		this.fullscreenMaterial.temporalResolveTexture = this.temporalResolvePass.renderTarget.texture;
 
 	}
 
diff --git a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
index b30138c2e..e65a8d779 100644
--- a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
@@ -1,4 +1,4 @@
-import { ShaderMaterial } from 'three';
+import { MaterialBase } from '../../materials/MaterialBase.js';
 
 const vertexShader = /* glsl */ `
     varying vec2 vUv;
@@ -23,7 +23,7 @@ const fragmentShader = /* glsl */ `
     }
 `;
 
-export class ComposeTemporalResolveMaterial extends ShaderMaterial {
+export class ComposeTemporalResolveMaterial extends MaterialBase {
 
 	constructor() {
 
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
index aa48d5df5..2d8cb0888 100644
--- a/src/temporal-resolve/passes/TemporalResolvePass.js
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -27,44 +27,29 @@ export class TemporalResolvePass {
 		this.scene = scene;
 		this.camera = camera;
 
-		this.renderTarget = new WebGLRenderTarget(
-			typeof window !== 'undefined' ? window.innerWidth : 2000,
-			typeof window !== 'undefined' ? window.innerHeight : 1000,
-			{
-				minFilter: LinearFilter,
-				magFilter: LinearFilter,
-				type: HalfFloatType,
-				depthBuffer: false,
-			}
-		);
-
-		this.sceneRenderTarget = new WebGLRenderTarget(
-			typeof window !== 'undefined' ? window.innerWidth : 2000,
-			typeof window !== 'undefined' ? window.innerHeight : 1000,
-			{
-				minFilter: LinearFilter,
-				magFilter: LinearFilter,
-			}
-		);
-
-		this.depthRenderTarget = new WebGLRenderTarget(
-			typeof window !== 'undefined' ? window.innerWidth : 2000,
-			typeof window !== 'undefined' ? window.innerHeight : 1000,
-			{
-				minFilter: LinearFilter,
-				magFilter: LinearFilter,
-			}
-		);
+		this.renderTarget = new WebGLRenderTarget( 1, 1, {
+			minFilter: LinearFilter,
+			magFilter: LinearFilter,
+			type: HalfFloatType,
+			depthBuffer: false,
+		} );
+
+		this.sceneRenderTarget = new WebGLRenderTarget( 1, 1, {
+			minFilter: LinearFilter,
+			magFilter: LinearFilter,
+		} );
+
+		this.depthRenderTarget = new WebGLRenderTarget( 1, 1, {
+			minFilter: LinearFilter,
+			magFilter: LinearFilter,
+		} );
 
 		this.velocityPass = new VelocityPass( scene, camera );
 
 		this.fullscreenMaterial = new TemporalResolveMaterial();
 
-		this.fullscreenMaterial.uniforms.velocityTexture.value =
-			this.velocityPass.renderTarget.texture;
-
-		this.fullscreenMaterial.uniforms.depthTexture.value =
-			this.depthRenderTarget.texture;
+		this.fullscreenMaterial.velocityTexture = this.velocityPass.renderTarget.texture;
+		this.fullscreenMaterial.depthTexture = this.depthRenderTarget.texture;
 
 		this.fsQuad = new FullScreenQuad( null );
 		this.fsQuad.material = this.fullscreenMaterial;
@@ -112,10 +97,8 @@ export class TemporalResolvePass {
 		this.lastDepthTexture.minFilter = LinearFilter;
 		this.lastDepthTexture.magFilter = LinearFilter;
 
-		this.fullscreenMaterial.uniforms.accumulatedSamplesTexture.value =
-			this.accumulatedSamplesTexture;
-		this.fullscreenMaterial.uniforms.lastDepthTexture.value =
-			this.lastDepthTexture;
+		this.fullscreenMaterial.accumulatedSamplesTexture = this.accumulatedSamplesTexture;
+		this.fullscreenMaterial.lastDepthTexture = this.lastDepthTexture;
 
 		this.fullscreenMaterial.needsUpdate = true;
 
@@ -144,14 +127,14 @@ export class TemporalResolvePass {
 		renderer.render( this.scene, this.camera );
 
 		// update uniforms of this pass
-		this.fullscreenMaterial.uniforms.curInverseProjectionMatrix.value.copy(
+		this.fullscreenMaterial.curInverseProjectionMatrix.copy(
 			this.camera.projectionMatrixInverse
 		);
-		this.fullscreenMaterial.uniforms.curCameraMatrixWorld.value.copy(
+		this.fullscreenMaterial.curCameraMatrixWorld.copy(
 			this.camera.matrixWorld
 		);
-		this.fullscreenMaterial.uniforms.cameraNear.value = this.camera.near;
-		this.fullscreenMaterial.uniforms.cameraFar.value = this.camera.far;
+		this.fullscreenMaterial.cameraNear = this.camera.near;
+		this.fullscreenMaterial.cameraFar = this.camera.far;
 
 		// now render this fullscreen pass
 		renderer.setRenderTarget( this.renderTarget );
@@ -163,10 +146,10 @@ export class TemporalResolvePass {
 		renderer.setRenderTarget( this.depthRenderTarget );
 		renderer.copyFramebufferToTexture( zeroVec2, this.lastDepthTexture );
 
-		this.fullscreenMaterial.uniforms.prevInverseProjectionMatrix.value.copy(
+		this.fullscreenMaterial.prevInverseProjectionMatrix.copy(
 			this.camera.projectionMatrixInverse
 		);
-		this.fullscreenMaterial.uniforms.prevCameraMatrixWorld.value.copy(
+		this.fullscreenMaterial.prevCameraMatrixWorld.copy(
 			this.camera.matrixWorld
 		);
 
From 21e659c735e38ffec00d995451ca6839d166f897 Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Mon, 8 Aug 2022 00:36:49 -0700
Subject: [PATCH 19/25] minor spacing
---
 .../materials/ComposeTemporalResolveMaterial.js               | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
index e65a8d779..35a91fbff 100644
--- a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
@@ -1,6 +1,6 @@
 import { MaterialBase } from '../../materials/MaterialBase.js';
 
-const vertexShader = /* glsl */ `
+const vertexShader = /* glsl */`
     varying vec2 vUv;
 
     void main() {
@@ -11,7 +11,7 @@ const vertexShader = /* glsl */ `
     }
 `;
 
-const fragmentShader = /* glsl */ `
+const fragmentShader = /* glsl */`
     varying vec2 vUv;
 
     uniform sampler2D temporalResolveTexture;
From 88aa13516140edf0dc4139ac466eef4d114708eb Mon Sep 17 00:00:00 2001
From: Garrett Johnson 
Date: Mon, 8 Aug 2022 18:27:13 -0700
Subject: [PATCH 20/25] shader styles
---
 .../shaders/temporalResolveFragment.js        | 167 +++++++++++-------
 1 file changed, 100 insertions(+), 67 deletions(-)
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 44dfd86d0..74dd76839 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -1,4 +1,4 @@
-export const fragmentShader = /* glsl */ `
+export const fragmentShader = /* glsl */`
 uniform sampler2D samplesTexture;
 uniform sampler2D accumulatedSamplesTexture;
 uniform sampler2D velocityTexture;
@@ -29,170 +29,203 @@ varying vec2 vUv;
 #define BLUR_EXPONENT 0.25
 
 // credits for transforming screen position to world position: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532/2
-vec3 screenSpaceToWorldSpace(const vec2 uv, const float depth, mat4 inverseProjectionMatrix, mat4 cameraMatrixWorld) {
+vec3 screenSpaceToWorldSpace( const vec2 uv, const float depth, mat4 inverseProjectionMatrix, mat4 cameraMatrixWorld ) {
     vec4 ndc = vec4(
-        (uv.x - 0.5) * 2.0,
-        (uv.y - 0.5) * 2.0,
-        (depth - 0.5) * 2.0,
-        1.0);
+        ( uv.x - 0.5 ) * 2.0,
+        ( uv.y - 0.5 ) * 2.0,
+        ( depth - 0.5 ) * 2.0,
+        1.0
+	);
 
     vec4 clip = inverseProjectionMatrix * ndc;
-    vec4 view = cameraMatrixWorld * (clip / clip.w);
+    vec4 view = cameraMatrixWorld * ( clip / clip.w );
 
     return view.xyz;
 }
 
 #ifdef DILATION
 // source: https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/ (modified to GLSL)
-vec4 getDilatedTexture(sampler2D tex, vec2 uv, vec2 texSize) {
-    float closestDepth = 0.;
+vec4 getDilatedTexture( sampler2D tex, vec2 uv, vec2 texSize ) {
+
+    float closestDepth = 0.0;
     vec2 closestUVOffset;
 
-    for (int j = -1; j <= 1; ++j) {
-        for (int i = -1; i <= 1; ++i) {
-            vec2 uvOffset = vec2(i, j) / texSize;
+    for ( int j = - 1; j <= 1; ++ j ) {
+
+        for ( int i = - 1; i <= 1; ++ i ) {
 
-            float neighborDepth = textureLod(tex, vUv + uvOffset, 0.).b;
+            vec2 uvOffset = vec2( i, j ) / texSize;
+            float neighborDepth = textureLod( tex, vUv + uvOffset, 0.0 ).b;
+            if ( neighborDepth > closestDepth ) {
 
-            if (neighborDepth > closestDepth) {
                 closestUVOffset = uvOffset;
                 closestDepth = neighborDepth;
+
             }
+
         }
+
     }
 
-    return textureLod(tex, vUv + closestUVOffset, 0.);
+    return textureLod( tex, vUv + closestUVOffset, 0.0 );
+
 }
 #endif
 
 // idea from: https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/
-vec3 transformColor(vec3 color) {
-    return pow(color, vec3(WEIGHT_TRANSFORM));
+vec3 transformColor( vec3 color ) {
+
+    return pow( color, vec3( WEIGHT_TRANSFORM ) );
+
 }
 
-vec3 undoColorTransform(vec3 color) {
-	return pow(color, vec3(1. / WEIGHT_TRANSFORM));
+vec3 undoColorTransform( vec3 color ) {
+
+	return pow( color, vec3( 1. / WEIGHT_TRANSFORM ) );
+
 }
 
 void main() {
-	vec4 samplesTexel = texture2D(samplesTexture, vUv);
-	samplesTexel.rgb = transformColor(samplesTexel.rgb);
 
-	ivec2 size = textureSize(samplesTexture, 0);
-	vec2 pxSize = vec2(size.x, size.y);
+	vec4 samplesTexel = texture2D( samplesTexture, vUv );
+	samplesTexel.rgb = transformColor( samplesTexel.rgb );
+
+	ivec2 size = textureSize( samplesTexture, 0 );
+	vec2 pxSize = vec2( size.x, size.y );
 
 #ifdef DILATION
-    vec4 velocity = getDilatedTexture(velocityTexture, vUv, pxSize);
+    vec4 velocity = getDilatedTexture( velocityTexture, vUv, pxSize );
 
 	vec2 velUv = velocity.xy;
     vec2 reprojectedUv = vUv - velUv;
 #else
-    vec4 velocity = textureLod(velocityTexture, vUv, 0.);
+    vec4 velocity = textureLod( velocityTexture, vUv, 0.0 );
 
 	vec2 velUv = velocity.xy;
     vec2 reprojectedUv = vUv - velUv;
 #endif
 
 	// background doesn't need reprojection
-	if(velocity.a == 1.){
+	if( velocity.a == 1.0 ) {
+
 		samplesTexel.rgb = undoColorTransform(samplesTexel.rgb);
 		gl_FragColor = samplesTexel;
 		return;
+
 	}
 
 	// depth textures should not be dilated
-	float unpackedDepth = unpackRGBAToDepth(textureLod(depthTexture, vUv, 0.));
-	float lastUnpackedDepth = unpackRGBAToDepth(textureLod(lastDepthTexture, reprojectedUv, 0.));
+	float unpackedDepth = unpackRGBAToDepth( textureLod( depthTexture, vUv, 0.0 ) );
+	float lastUnpackedDepth = unpackRGBAToDepth( textureLod( lastDepthTexture, reprojectedUv, 0.0 ) );
 
-    float movement = length(velUv) * 100.;
+    float movement = length( velUv ) * 100.0;
 
-	vec3 curWorldPos = screenSpaceToWorldSpace(vUv, unpackedDepth, curInverseProjectionMatrix, curCameraMatrixWorld);
-	vec3 lastWorldPos = screenSpaceToWorldSpace(vUv, lastUnpackedDepth, prevInverseProjectionMatrix, prevCameraMatrixWorld);
-	float distToLastFrame = length(curWorldPos - lastWorldPos) * 0.25;
+	vec3 curWorldPos = screenSpaceToWorldSpace( vUv, unpackedDepth, curInverseProjectionMatrix, curCameraMatrixWorld );
+	vec3 lastWorldPos = screenSpaceToWorldSpace( vUv, lastUnpackedDepth, prevInverseProjectionMatrix, prevCameraMatrixWorld );
+	float distToLastFrame = length( curWorldPos - lastWorldPos ) * 0.25;
 
 	vec4 accumulatedSamplesTexel;
     vec3 outputColor;
 	float alpha;
 
-	bool canReproject = reprojectedUv.x >= 0. && reprojectedUv.x <= 1. && reprojectedUv.y >= 0. && reprojectedUv.y <= 1.;
+	bool canReproject = reprojectedUv.x >= 0.0 && reprojectedUv.x <= 1.0 && reprojectedUv.y >= 0.0 && reprojectedUv.y <= 1.0;
 
 	// check that the reprojected UV is valid
-	if (canReproject) {
-		accumulatedSamplesTexel = textureLod(accumulatedSamplesTexture, reprojectedUv, 0.);
-		accumulatedSamplesTexel.rgb = transformColor(accumulatedSamplesTexel.rgb);
+	if ( canReproject ) {
 
-        alpha = distToLastFrame < 0.05 ? (accumulatedSamplesTexel.a + 0.05) : 0.;
-		alpha = clamp(alpha, 0., 1.);
+		accumulatedSamplesTexel = textureLod( accumulatedSamplesTexture, reprojectedUv, 0.0 );
+		accumulatedSamplesTexel.rgb = transformColor( accumulatedSamplesTexel.rgb );
 
-		if(samplesTexel.a != 0.){
-			vec2 px = 1. / pxSize;
+        alpha = distToLastFrame < 0.05 ? ( accumulatedSamplesTexel.a + 0.05 ) : 0.0;
+		alpha = clamp( alpha, 0.0, 1.0 );
+
+		if( samplesTexel.a != 0.0 ) {
+
+			vec2 px = 1.0 / pxSize;
 
 			vec3 boxBlurredColor;
 			float totalWeight;
 
-			vec3 minNeighborColor = vec3(1., 1., 1.);
-			vec3 maxNeighborColor = vec3(0., 0., 0.);
+			vec3 minNeighborColor = vec3( 1.0, 1.0, 1.0 );
+			vec3 maxNeighborColor = vec3( 0.0, 0.0, 0.0 );
 
 			// use a small ring if there is a lot of movement otherwise there will be more smearing
-			float radius = movement > 1. ? 1. : clampRadius;
+			float radius = movement > 1.0 ? 1.0 : clampRadius;
 
 			vec3 col;
 			float weight;
 			vec2 neighborUv;
 			bool neighborUvValid;
 
-			for(float x = -radius; x <= radius; x++){
-				for(float y = -radius; y <= radius; y++){
-					neighborUv = vUv + px * vec2(x, y);
-					neighborUvValid = neighborUv.x >= 0. && neighborUv.x <= 1. && neighborUv.y >= 0. && neighborUv.y <= 1.;
+			for( float x = - radius; x <= radius; x ++ ) {
+
+				for( float y = - radius; y <= radius; y ++ ) {
+
+					neighborUv = vUv + px * vec2( x, y );
+					neighborUvValid = neighborUv.x >= 0.0 && neighborUv.x <= 1.0 && neighborUv.y >= 0.0 && neighborUv.y <= 1.0;
 
-					if(neighborUvValid){
-						col = textureLod(samplesTexture, neighborUv, 0.).rgb;
-						col = transformColor(col);
+					if( neighborUvValid ) {
+
+						col = textureLod( samplesTexture, neighborUv, 0.0 ).rgb;
+						col = transformColor( col );
 
 						// box blur
-						if(abs(x) <= 1. && abs(y) <= 1.){
-							weight = 1.0 - abs(dot(col - samplesTexel.rgb, vec3(0.25)));
-							weight = pow(weight, BLUR_EXPONENT);
+						if( abs( x ) <= 1.0 && abs( y ) <= 1.0 ) {
+
+							weight = 1.0 - abs( dot( col - samplesTexel.rgb, vec3( 0.25 ) ) );
+							weight = pow( weight, BLUR_EXPONENT );
 							boxBlurredColor += col * weight;
 							totalWeight += weight;
+
 						}
 
-						minNeighborColor = min(col, minNeighborColor);
-						maxNeighborColor = max(col, maxNeighborColor);
+						minNeighborColor = min( col, minNeighborColor );
+						maxNeighborColor = max( col, maxNeighborColor );
+
 					}
+
 				}
+
 			}
 
 			// clamp the reprojected frame (neighborhood clamping)
-			accumulatedSamplesTexel.rgb = mix(accumulatedSamplesTexel.rgb, clamp(accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor), newSamplesCorrection);
+			accumulatedSamplesTexel.rgb = mix( accumulatedSamplesTexel.rgb, clamp( accumulatedSamplesTexel.rgb, minNeighborColor, maxNeighborColor ), newSamplesCorrection );
 
 			// let's blur the input color to reduce noise for new samples
-			if(newSamplesSmoothing != 0. && alpha < 1. && totalWeight > FLOAT_EPSILON){
+			if ( newSamplesSmoothing != 0.0 && alpha < 1.0 && totalWeight > FLOAT_EPSILON ) {
+
 				boxBlurredColor /= totalWeight;
 				samplesTexel.rgb = mix(samplesTexel.rgb, boxBlurredColor, newSamplesSmoothing);
+
 			}
+
 		}
+
 	} else {
+
 		// reprojected UV coordinates are outside of screen, so just use the current frame for it
-		alpha = 0.;
+		alpha = 0.0;
 		accumulatedSamplesTexel.rgb = samplesTexel.rgb;
+
 	}
 
-	float m = (1. - min(movement * 2., 1.) * (1. - temporalResolveMix)) - (samples - 1.) * 0.01 - 0.025;
-	
-	m = clamp(m, 0., 1.);
+	float m = ( 1.0 - min( movement * 2.0, 1.0 ) * ( 1.0 - temporalResolveMix ) ) - ( samples - 1.0 ) * 0.01 - 0.025;
+
+	m = clamp( m, 0.0, 1.0 );
+
+	outputColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * ( 1.0 - m );
 
-	outputColor = accumulatedSamplesTexel.rgb * m + samplesTexel.rgb * (1. - m);
 	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
 	// so make the final color blend more towards the new pixel
-	if(alpha < 1.){
-		float correctionMix = min(movement, 0.5) * newSamplesCorrection;
-		outputColor = mix(outputColor, samplesTexel.rgb, correctionMix);
+	if( alpha < 1.0 ) {
+
+		float correctionMix = min( movement, 0.5 ) * newSamplesCorrection;
+		outputColor = mix( outputColor, samplesTexel.rgb, correctionMix );
+
 	}
 
-	outputColor = undoColorTransform(outputColor);
+	outputColor = undoColorTransform( outputColor );
 
-    gl_FragColor = vec4(outputColor, alpha);
+    gl_FragColor = vec4( outputColor, alpha );
 }
 `;
From 3279fa8dfc9fe7a597dcd1a6d31ae976809d1c10 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 11 Aug 2022 13:06:11 +0200
Subject: [PATCH 21/25] Fix tiling, improve TR blend formula
---
 example/index.js                              |  2 +-
 example/materialBall.js                       |  6 +--
 src/temporal-resolve/TemporalResolve.js       | 10 +++--
 .../ComposeTemporalResolveMaterial.js         |  6 ++-
 .../materials/TemporalResolveMaterial.js      |  1 +
 .../shaders/temporalResolveFragment.js        | 41 +++++++++++++++----
 .../passes/TemporalResolvePass.js             | 19 +++++----
 7 files changed, 59 insertions(+), 26 deletions(-)
diff --git a/example/index.js b/example/index.js
index be131837b..fd302d439 100644
--- a/example/index.js
+++ b/example/index.js
@@ -213,7 +213,7 @@ function animate() {
 
 	}
 
-	if ( ( ! params.temporalResolve && ptRenderer.samples < 1.0 ) || ! params.enable ) {
+	if ( ptRenderer.samples < 1.0 || ! params.enable ) {
 
 		renderer.render( scene, activeCamera );
 
diff --git a/example/materialBall.js b/example/materialBall.js
index 8e717bb67..05ab52791 100644
--- a/example/materialBall.js
+++ b/example/materialBall.js
@@ -91,7 +91,7 @@ const params = {
 	acesToneMapping: true,
 	resolutionScale: 1 / window.devicePixelRatio,
 	temporalResolve: true,
-	temporalResolveMix: 0.925,
+	temporalResolveMix: 0.9,
 	clampRadius: 2,
 	newSamplesSmoothing: 0.675,
 	newSamplesCorrection: 1,
@@ -174,7 +174,7 @@ async function init() {
 	scene = new THREE.Scene();
 
 	temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
-	temporalResolve.temporalResolveMix = 0.925;
+	temporalResolve.temporalResolveMix = 0.9;
 	temporalResolve.clampRadius = 2;
 	temporalResolve.newSamplesSmoothing = 0.675;
 	temporalResolve.newSamplesCorrection = 1;
@@ -663,7 +663,7 @@ function animate() {
 
 	}
 
-	if ( ! params.temporalResolve && ptRenderer.samples < 1 ) {
+	if ( ptRenderer.samples < 1 ) {
 
 		renderer.render( scene, activeCamera );
 
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index 7a6c632dc..b3ba5ca12 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -1,4 +1,4 @@
-import { HalfFloatType, LinearFilter, WebGLRenderTarget } from 'three';
+import { FloatType, LinearFilter, WebGLRenderTarget } from 'three';
 import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
 import { ComposeTemporalResolveMaterial } from './materials/ComposeTemporalResolveMaterial.js';
 import { TemporalResolvePass } from './passes/TemporalResolvePass.js';
@@ -32,7 +32,7 @@ export class TemporalResolve {
 			{
 				minFilter: LinearFilter,
 				magFilter: LinearFilter,
-				type: HalfFloatType,
+				type: FloatType,
 				depthBuffer: false,
 			}
 		);
@@ -87,12 +87,13 @@ export class TemporalResolve {
 
 	update() {
 
-		while ( this.ptRenderer.samples < 1 ) this.ptRenderer.update();
-
 		const renderer = this.ptRenderer._renderer;
 
+		// save original values
 		const origRenderTarget = renderer.getRenderTarget();
 
+		this.ptRenderer.stableTiles = false;
+
 		const { camera } = this.ptRenderer;
 		if ( camera !== this.activeCamera ) {
 
@@ -141,6 +142,7 @@ export class TemporalResolve {
 		renderer.setRenderTarget( this.renderTarget );
 		this.fsQuad.render( renderer );
 
+		// restore original values
 		renderer.setRenderTarget( origRenderTarget );
 
 	}
diff --git a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
index 35a91fbff..8df027b02 100644
--- a/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/ComposeTemporalResolveMaterial.js
@@ -18,7 +18,11 @@ const fragmentShader = /* glsl */`
 
     void main() {
 
-        gl_FragColor = vec4( texture2D( temporalResolveTexture, vUv ).rgb, 1.0 );
+        vec4 temporalResolveTexel = texture2D( temporalResolveTexture, vUv );
+
+        bool isBackgroundTile = temporalResolveTexel.a == 0.0;
+
+        gl_FragColor = vec4( temporalResolveTexel.rgb, isBackgroundTile ? 0.0 : 1.0 );
 
     }
 `;
diff --git a/src/temporal-resolve/materials/TemporalResolveMaterial.js b/src/temporal-resolve/materials/TemporalResolveMaterial.js
index d9397f20a..e257be71f 100644
--- a/src/temporal-resolve/materials/TemporalResolveMaterial.js
+++ b/src/temporal-resolve/materials/TemporalResolveMaterial.js
@@ -21,6 +21,7 @@ export class TemporalResolveMaterial extends MaterialBase {
 				clampRadius: { value: 0 },
 				newSamplesSmoothing: { value: 0 },
 				newSamplesCorrection: { value: 0 },
+				tileCount: { value: 0 },
 				curInverseProjectionMatrix: { value: new Matrix4() },
 				curCameraMatrixWorld: { value: new Matrix4() },
 				prevInverseProjectionMatrix: { value: new Matrix4() },
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 74dd76839..546a34d1e 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -11,6 +11,7 @@ uniform float temporalResolveMix;
 uniform float clampRadius;
 uniform float newSamplesSmoothing;
 uniform float newSamplesCorrection;
+uniform float tileCount;
 
 uniform mat4 curInverseProjectionMatrix;
 uniform mat4 curCameraMatrixWorld;
@@ -27,6 +28,7 @@ varying vec2 vUv;
 
 #define FLOAT_EPSILON 0.00001
 #define BLUR_EXPONENT 0.25
+#define MAX_NEIGHBOR_DEPTH_DIFFERENCE 0.001
 
 // credits for transforming screen position to world position: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532/2
 vec3 screenSpaceToWorldSpace( const vec2 uv, const float depth, mat4 inverseProjectionMatrix, mat4 cameraMatrixWorld ) {
@@ -81,7 +83,7 @@ vec3 transformColor( vec3 color ) {
 
 vec3 undoColorTransform( vec3 color ) {
 
-	return pow( color, vec3( 1. / WEIGHT_TRANSFORM ) );
+	return max( vec3( 0.0 ), pow( color, vec3( 1.0 / WEIGHT_TRANSFORM ) ) );
 
 }
 
@@ -105,11 +107,14 @@ void main() {
     vec2 reprojectedUv = vUv - velUv;
 #endif
 
+	bool isTileRendered = samplesTexel.a != 0.0;
+
 	// background doesn't need reprojection
 	if( velocity.a == 1.0 ) {
 
 		samplesTexel.rgb = undoColorTransform(samplesTexel.rgb);
-		gl_FragColor = samplesTexel;
+		gl_FragColor = vec4(samplesTexel.rgb, isTileRendered ? 1.0 : 0.0);
+		
 		return;
 
 	}
@@ -137,7 +142,9 @@ void main() {
 		accumulatedSamplesTexel.rgb = transformColor( accumulatedSamplesTexel.rgb );
 
         alpha = distToLastFrame < 0.05 ? ( accumulatedSamplesTexel.a + 0.05 ) : 0.0;
-		alpha = clamp( alpha, 0.0, 1.0 );
+
+		// alpha = 0.0 is reserved for the background (to find out if the tile hasn't been rendered yet)
+		alpha = clamp( alpha, FLOAT_EPSILON, 1.0 );
 
 		if( samplesTexel.a != 0.0 ) {
 
@@ -156,6 +163,7 @@ void main() {
 			float weight;
 			vec2 neighborUv;
 			bool neighborUvValid;
+			float neighborUnpackedDepth;
 
 			for( float x = - radius; x <= radius; x ++ ) {
 
@@ -169,8 +177,10 @@ void main() {
 						col = textureLod( samplesTexture, neighborUv, 0.0 ).rgb;
 						col = transformColor( col );
 
+						neighborUnpackedDepth = unpackRGBAToDepth( textureLod( lastDepthTexture, neighborUv, 0.0 ) );
+
 						// box blur
-						if( abs( x ) <= 1.0 && abs( y ) <= 1.0 ) {
+						if( abs( x ) <= 3.0 && abs( y ) <= 3.0 && abs( unpackedDepth - neighborUnpackedDepth ) < MAX_NEIGHBOR_DEPTH_DIFFERENCE ) {
 
 							weight = 1.0 - abs( dot( col - samplesTexel.rgb, vec3( 0.25 ) ) );
 							weight = pow( weight, BLUR_EXPONENT );
@@ -204,12 +214,23 @@ void main() {
 	} else {
 
 		// reprojected UV coordinates are outside of screen, so just use the current frame for it
-		alpha = 0.0;
+		alpha = FLOAT_EPSILON;
 		accumulatedSamplesTexel.rgb = samplesTexel.rgb;
 
 	}
 
-	float m = ( 1.0 - min( movement * 2.0, 1.0 ) * ( 1.0 - temporalResolveMix ) ) - ( samples - 1.0 ) * 0.01 - 0.025;
+	// in case this pixel is from a tile that wasn't rendered yet
+	if( !isTileRendered ) {
+		
+		gl_FragColor = vec4( undoColorTransform( accumulatedSamplesTexel.rgb ), 0.05);
+
+		return;
+
+	}
+
+	float tileFactor = max(0.5, 1.0 - (tileCount - 1.0) * 0.025);
+
+	float m = ( 1.0 - min( movement * 2.0, 1.0 ) * ( 1.0 - temporalResolveMix ) ) * tileFactor - ( samples - 1.0 ) * 0.0025 - 0.025;
 
 	m = clamp( m, 0.0, 1.0 );
 
@@ -219,8 +240,12 @@ void main() {
 	// so make the final color blend more towards the new pixel
 	if( alpha < 1.0 ) {
 
-		float correctionMix = min( movement, 0.5 ) * newSamplesCorrection;
-		outputColor = mix( outputColor, samplesTexel.rgb, correctionMix );
+		if( distToLastFrame < 0.0 ) {
+			outputColor = accumulatedSamplesTexel.rgb;
+		}else{
+			float correctionMix = min( movement, 0.5 ) * newSamplesCorrection;
+			outputColor = mix( outputColor, samplesTexel.rgb, correctionMix );
+		}
 
 	}
 
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
index 2d8cb0888..899feb43d 100644
--- a/src/temporal-resolve/passes/TemporalResolvePass.js
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -1,9 +1,9 @@
 import {
 	Color,
-	FramebufferTexture,
-	HalfFloatType,
-	LinearFilter,
+	FloatType,
+	FramebufferTexture, LinearFilter,
 	MeshDepthMaterial,
+	NearestFilter,
 	RGBADepthPacking,
 	RGBAFormat,
 	Vector2,
@@ -30,7 +30,7 @@ export class TemporalResolvePass {
 		this.renderTarget = new WebGLRenderTarget( 1, 1, {
 			minFilter: LinearFilter,
 			magFilter: LinearFilter,
-			type: HalfFloatType,
+			type: FloatType,
 			depthBuffer: false,
 		} );
 
@@ -40,8 +40,8 @@ export class TemporalResolvePass {
 		} );
 
 		this.depthRenderTarget = new WebGLRenderTarget( 1, 1, {
-			minFilter: LinearFilter,
-			magFilter: LinearFilter,
+			minFilter: NearestFilter,
+			magFilter: NearestFilter,
 		} );
 
 		this.velocityPass = new VelocityPass( scene, camera );
@@ -91,11 +91,11 @@ export class TemporalResolvePass {
 		);
 		this.accumulatedSamplesTexture.minFilter = LinearFilter;
 		this.accumulatedSamplesTexture.magFilter = LinearFilter;
-		this.accumulatedSamplesTexture.type = HalfFloatType;
+		this.accumulatedSamplesTexture.type = FloatType;
 
 		this.lastDepthTexture = new FramebufferTexture( width, height, RGBAFormat );
-		this.lastDepthTexture.minFilter = LinearFilter;
-		this.lastDepthTexture.magFilter = LinearFilter;
+		this.lastDepthTexture.minFilter = NearestFilter;
+		this.lastDepthTexture.magFilter = NearestFilter;
 
 		this.fullscreenMaterial.accumulatedSamplesTexture = this.accumulatedSamplesTexture;
 		this.fullscreenMaterial.lastDepthTexture = this.lastDepthTexture;
@@ -127,6 +127,7 @@ export class TemporalResolvePass {
 		renderer.render( this.scene, this.camera );
 
 		// update uniforms of this pass
+		this.fullscreenMaterial.tileCount = this.ptRenderer.tiles.x * this.ptRenderer.tiles.y;
 		this.fullscreenMaterial.curInverseProjectionMatrix.copy(
 			this.camera.projectionMatrixInverse
 		);
From ee5b621307266c4d0e75098d710a02d074da937f Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 11 Aug 2022 13:19:52 +0200
Subject: [PATCH 22/25] Formatting
---
 .../materials/shaders/temporalResolveFragment.js          | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 546a34d1e..6b02e0bed 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -112,8 +112,8 @@ void main() {
 	// background doesn't need reprojection
 	if( velocity.a == 1.0 ) {
 
-		samplesTexel.rgb = undoColorTransform(samplesTexel.rgb);
-		gl_FragColor = vec4(samplesTexel.rgb, isTileRendered ? 1.0 : 0.0);
+		samplesTexel.rgb = undoColorTransform( samplesTexel.rgb );
+		gl_FragColor = vec4( samplesTexel.rgb, isTileRendered ? 1.0 : 0.0 );
 		
 		return;
 
@@ -205,7 +205,7 @@ void main() {
 			if ( newSamplesSmoothing != 0.0 && alpha < 1.0 && totalWeight > FLOAT_EPSILON ) {
 
 				boxBlurredColor /= totalWeight;
-				samplesTexel.rgb = mix(samplesTexel.rgb, boxBlurredColor, newSamplesSmoothing);
+				samplesTexel.rgb = mix( samplesTexel.rgb, boxBlurredColor, newSamplesSmoothing );
 
 			}
 
@@ -228,7 +228,7 @@ void main() {
 
 	}
 
-	float tileFactor = max(0.5, 1.0 - (tileCount - 1.0) * 0.025);
+	float tileFactor = max( 0.5, 1.0 - ( tileCount - 1.0 ) * 0.025 );
 
 	float m = ( 1.0 - min( movement * 2.0, 1.0 ) * ( 1.0 - temporalResolveMix ) ) * tileFactor - ( samples - 1.0 ) * 0.0025 - 0.025;
 
From 60f8e5502121fc820d1cde351746a0fdd3bda609 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 11 Aug 2022 13:33:57 +0200
Subject: [PATCH 23/25] Improve TR
---
 .../shaders/temporalResolveFragment.js        | 20 ++++++++-----------
 1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
index 6b02e0bed..25888240c 100644
--- a/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
+++ b/src/temporal-resolve/materials/shaders/temporalResolveFragment.js
@@ -27,7 +27,7 @@ varying vec2 vUv;
 #include 
 
 #define FLOAT_EPSILON 0.00001
-#define BLUR_EXPONENT 0.25
+#define BLUR_EXPONENT 0.5
 #define MAX_NEIGHBOR_DEPTH_DIFFERENCE 0.001
 
 // credits for transforming screen position to world position: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532/2
@@ -52,11 +52,11 @@ vec4 getDilatedTexture( sampler2D tex, vec2 uv, vec2 texSize ) {
     float closestDepth = 0.0;
     vec2 closestUVOffset;
 
-    for ( int j = - 1; j <= 1; ++ j ) {
+    for ( int x = - 1; x <= 1; ++ x ) {
 
-        for ( int i = - 1; i <= 1; ++ i ) {
+        for ( int y = - 1; y <= 1; ++ y ) {
 
-            vec2 uvOffset = vec2( i, j ) / texSize;
+            vec2 uvOffset = vec2( x, y ) / texSize;
             float neighborDepth = textureLod( tex, vUv + uvOffset, 0.0 ).b;
             if ( neighborDepth > closestDepth ) {
 
@@ -141,7 +141,7 @@ void main() {
 		accumulatedSamplesTexel = textureLod( accumulatedSamplesTexture, reprojectedUv, 0.0 );
 		accumulatedSamplesTexel.rgb = transformColor( accumulatedSamplesTexel.rgb );
 
-        alpha = distToLastFrame < 0.05 ? ( accumulatedSamplesTexel.a + 0.05 ) : 0.0;
+        alpha = distToLastFrame < 0.25 ? ( accumulatedSamplesTexel.a + 0.05 ) : 0.0;
 
 		// alpha = 0.0 is reserved for the background (to find out if the tile hasn't been rendered yet)
 		alpha = clamp( alpha, FLOAT_EPSILON, 1.0 );
@@ -239,13 +239,9 @@ void main() {
 	// alpha will be below 1 if the pixel is "new" (e.g. it became disoccluded recently)
 	// so make the final color blend more towards the new pixel
 	if( alpha < 1.0 ) {
-
-		if( distToLastFrame < 0.0 ) {
-			outputColor = accumulatedSamplesTexel.rgb;
-		}else{
-			float correctionMix = min( movement, 0.5 ) * newSamplesCorrection;
-			outputColor = mix( outputColor, samplesTexel.rgb, correctionMix );
-		}
+		
+		float correctionMix = min( movement, 0.5 ) * newSamplesCorrection;
+		outputColor = mix( outputColor, samplesTexel.rgb, correctionMix );
 
 	}
 
From e0a9a0a3f65e925c9f5437bd1ef8bab7639ae3b9 Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 11 Aug 2022 13:42:11 +0200
Subject: [PATCH 24/25] Fix resize issue
---
 src/temporal-resolve/TemporalResolve.js            |  1 +
 src/temporal-resolve/passes/TemporalResolvePass.js | 10 ----------
 2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/temporal-resolve/TemporalResolve.js b/src/temporal-resolve/TemporalResolve.js
index b3ba5ca12..eb575b120 100644
--- a/src/temporal-resolve/TemporalResolve.js
+++ b/src/temporal-resolve/TemporalResolve.js
@@ -81,6 +81,7 @@ export class TemporalResolve {
 		this.lastSize.width = width;
 		this.lastSize.height = height;
 
+		this.renderTarget.setSize( width, height );
 		this.temporalResolvePass.setSize( width, height );
 
 	}
diff --git a/src/temporal-resolve/passes/TemporalResolvePass.js b/src/temporal-resolve/passes/TemporalResolvePass.js
index 899feb43d..a5b67e589 100644
--- a/src/temporal-resolve/passes/TemporalResolvePass.js
+++ b/src/temporal-resolve/passes/TemporalResolvePass.js
@@ -34,11 +34,6 @@ export class TemporalResolvePass {
 			depthBuffer: false,
 		} );
 
-		this.sceneRenderTarget = new WebGLRenderTarget( 1, 1, {
-			minFilter: LinearFilter,
-			magFilter: LinearFilter,
-		} );
-
 		this.depthRenderTarget = new WebGLRenderTarget( 1, 1, {
 			minFilter: NearestFilter,
 			magFilter: NearestFilter,
@@ -70,7 +65,6 @@ export class TemporalResolvePass {
 	setSize( width, height ) {
 
 		this.renderTarget.setSize( width, height );
-		this.sceneRenderTarget.setSize( width, height );
 		this.depthRenderTarget.setSize( width, height );
 		this.velocityPass.setSize( width, height );
 
@@ -122,10 +116,6 @@ export class TemporalResolvePass {
 		// render velocity
 		this.velocityPass.render( renderer );
 
-		renderer.setRenderTarget( this.sceneRenderTarget );
-		renderer.clear();
-		renderer.render( this.scene, this.camera );
-
 		// update uniforms of this pass
 		this.fullscreenMaterial.tileCount = this.ptRenderer.tiles.x * this.ptRenderer.tiles.y;
 		this.fullscreenMaterial.curInverseProjectionMatrix.copy(
From fc044fd7eccb9d4c8795a0991ba34d21500309de Mon Sep 17 00:00:00 2001
From: 0beqz 
Date: Thu, 11 Aug 2022 18:05:48 +0200
Subject: [PATCH 25/25] Fix skinned mesh & velocity pass issue
---
 src/temporal-resolve/passes/VelocityPass.js | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/src/temporal-resolve/passes/VelocityPass.js b/src/temporal-resolve/passes/VelocityPass.js
index 467a74034..c798a1946 100644
--- a/src/temporal-resolve/passes/VelocityPass.js
+++ b/src/temporal-resolve/passes/VelocityPass.js
@@ -54,6 +54,8 @@ export class VelocityPass {
 						fragmentShader: VelocityShader.fragmentShader
 					} );
 
+					c.material = velocityMaterial;
+
 					if ( c.skeleton && c.skeleton.boneTexture ) this.saveBoneTexture( c );
 
 					this.cachedMaterials.set( c, [ originalMaterial, velocityMaterial ] );