Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Temporal Resolving #241

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,64 @@ _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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we use js instead of javascript for the syntax keyword

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.

### .clampRadius

```javascript
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.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "neighborhood clamping" mean here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You clamp the color of the reprojected pixel by the min and max color of the neighboring pixels from the raytracer's output at the current pixel. This is the most popular method to reduce ghosting for TRAA but since we have a noisy output in the first frames when raytracing, neighborhood clamping will lead to flickering there. So by increasing the number of pixel we use for neighborhood clamping, we reduce flickering but will have more smear.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation! Maybe we can include more information on the docs about the role color plays in this process

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, yeah I'll add a more in-depth explanation for it


### .newSamplesSmoothing

```javascript
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.

### .newSamplesCorrection

```javascript
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
constructor( ptRenderer : PathTracingRenderer, scene : Object3D, camera : Camera )
```

### .update

```javascript
update() : void
```

Updates the temporal resolve pass for the current frame.

## PhysicalSpotLight

_extends THREE.SpotLight_
Expand Down
50 changes: 47 additions & 3 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -71,6 +72,13 @@ const params = {
tilesY: 2,
samplesPerFrame: 1,

temporalResolve: true,
temporalResolveMix: 0.9,
clampRadius: 1,
newSamplesSmoothing: 0.675,
newSamplesCorrection: 1,
weightTransform: 0,

model: initialModel,

envMap: envMaps[ 'Royal Esplanade' ],
Expand Down Expand Up @@ -104,7 +112,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;
Expand Down Expand Up @@ -143,6 +151,13 @@ async function init() {
ptRenderer.material.bgGradientTop.set( params.bgGradientTop );
ptRenderer.material.bgGradientBottom.set( params.bgGradientBottom );

temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
temporalResolve.temporalResolveMix = 0.9;
temporalResolve.clampRadius = 1;
temporalResolve.newSamplesSmoothing = 0.5;
temporalResolve.newSamplesCorrection = 0.75;
temporalResolve.weightTransform = 0;
Comment on lines +155 to +159
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be best to initialize these parameters from the params object - otherwise if we change the demo defaults we have to remember to do it in two places.


fsQuad = new FullScreenQuad( new MeshBasicMaterial( {
map: ptRenderer.target.texture,
blending: CustomBlending
Expand Down Expand Up @@ -198,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 );

Expand Down Expand Up @@ -230,6 +245,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;

Expand All @@ -245,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;

Expand Down Expand Up @@ -311,6 +337,24 @@ 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, 'clampRadius', 1, 8, 1 )
.onChange( ( value ) => ( temporalResolve.clampRadius = 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 ) );
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( () => {

Expand Down
52 changes: 46 additions & 6 deletions example/materialBall.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -75,7 +76,6 @@ const params = {
matte: false,
castShadow: true,
},

multipleImportanceSampling: true,
stableNoise: false,
environmentIntensity: 1,
Expand All @@ -86,6 +86,12 @@ const params = {
samplesPerFrame: 1,
acesToneMapping: true,
resolutionScale: 1 / window.devicePixelRatio,
temporalResolve: true,
temporalResolveMix: 0.925,
clampRadius: 2,
newSamplesSmoothing: 0.675,
newSamplesCorrection: 1,
weightTransform: 0,
transparentTraversals: 20,
filterGlossyFactor: 0.5,
tiles: 1,
Expand Down Expand Up @@ -128,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 );
Comment on lines -135 to +141
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we revert this camera change unless there's a reason to move it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll change it back


const orthoHeight = orthoWidth / aspect;
orthoCamera = new THREE.OrthographicCamera( orthoWidth / - 2, orthoWidth / 2, orthoHeight / 2, orthoHeight / - 2, 0, 100 );
Expand Down Expand Up @@ -158,6 +164,13 @@ async function init() {

scene = new THREE.Scene();

temporalResolve = new TemporalResolve( ptRenderer, scene, activeCamera );
temporalResolve.temporalResolveMix = 0.925;
temporalResolve.clampRadius = 2;
temporalResolve.newSamplesSmoothing = 0.675;
temporalResolve.newSamplesCorrection = 1;
temporalResolve.weightTransform = 0;

samplesEl = document.getElementById( 'samples' );

envMapGenerator = new BlurredEnvMapGenerator( renderer );
Expand Down Expand Up @@ -315,6 +328,24 @@ 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, 'clampRadius', 1, 8, 1 )
.onChange( ( value ) => ( temporalResolve.clampRadius = 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 ) );
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( () => {

Expand Down Expand Up @@ -618,14 +649,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;

Expand All @@ -635,4 +676,3 @@ function animate() {




3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.js';