Skip to content

Commit

Permalink
WebGPURenderer: Support for access previous frame textures using `pas…
Browse files Browse the repository at this point in the history
…s()` (#29069)

* PassNode: Support for previous frame textures

* add support to change render target textures

* add `webgpu_postprocessing_difference` example

* revision

* Update webgpu_postprocessing_difference.html

* revision

* revision

* revision

* update screenshot
  • Loading branch information
sunag committed Aug 6, 2024
1 parent ff43819 commit fffc941
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 52 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@
"webgpu_postprocessing_bloom",
"webgpu_postprocessing_bloom_emissive",
"webgpu_postprocessing_bloom_selective",
"webgpu_postprocessing_difference",
"webgpu_postprocessing_dof",
"webgpu_postprocessing_pixel",
"webgpu_postprocessing_fxaa",
Expand Down
Binary file modified examples/screenshots/webgpu_postprocessing_bloom_emissive.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/screenshots/webgpu_postprocessing_bloom_selective.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
130 changes: 130 additions & 0 deletions examples/webgpu_postprocessing_difference.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgpu - frame difference</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a>
<br/>saturated color of objects according to the difference from one frame to another
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/tsl": "../build/three.webgpu.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { pass, luminance } from 'three/tsl';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { Timer } from 'three/addons/misc/Timer.js';

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

const params = {
speed: 0
};

let camera, renderer, postProcessing;
let timer, mesh, controls;

init();

function init() {

renderer = new THREE.WebGPURenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.toneMapping = THREE.NeutralToneMapping;
document.body.appendChild( renderer.domElement );

//

camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 1, 2, 3 );

const scene = new THREE.Scene();
scene.fog = new THREE.Fog( 0x0487e2, 7, 25 );
scene.background = new THREE.Color( 0x0487e2 );

timer = new Timer();

const texture = new THREE.TextureLoader().load( 'textures/crate.gif' );
texture.colorSpace = THREE.SRGBColorSpace;

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { map: texture } );

mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

// post processing

postProcessing = new THREE.PostProcessing( renderer );

const scenePass = pass( scene, camera );

const currentTexture = scenePass.getTextureNode();
const previousTexture = scenePass.getPreviousTextureNode();

const frameDiff = previousTexture.sub( currentTexture ).abs();

const saturationAmount = luminance( frameDiff ).mul( 1000 ).clamp( 0, 3 );

postProcessing.outputNode = currentTexture.saturation( saturationAmount );

//

controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 2;
controls.maxDistance = 10;
controls.enableDamping = true;
controls.dampingFactor = 0.01;

window.addEventListener( 'resize', onWindowResize );

//

const gui = new GUI();
gui.add( params, 'speed', 0, 2 );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

timer.update();

controls.update();

mesh.rotation.y += timer.getDelta() * 5 * params.speed;

postProcessing.render();

}

</script>

</body>
</html>
74 changes: 71 additions & 3 deletions src/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,32 @@ class PassTextureNode extends TextureNode {

class PassMultipleTextureNode extends PassTextureNode {

constructor( passNode, textureName ) {
constructor( passNode, textureName, previousTexture = false ) {

super( passNode, null );

this.textureName = textureName;
this.previousTexture = previousTexture;

}

updateTexture() {

this.value = this.previousTexture ? this.passNode.getPreviousTexture( this.textureName ) : this.passNode.getTexture( this.textureName );

}

setup( builder ) {

this.value = this.passNode.getTexture( this.textureName );
this.updateTexture();

return super.setup( builder );

}

clone() {

return new this.constructor( this.passNode, this.textureName );
return new this.constructor( this.passNode, this.textureName, this.previousTexture );

}

Expand Down Expand Up @@ -104,6 +111,9 @@ class PassNode extends TempNode {
this._linearDepthNodes = {};
this._viewZNodes = {};

this._previousTextures = {};
this._previousTextureNodes = {};

this._cameraNear = uniform( 0 );
this._cameraFar = uniform( 0 );

Expand Down Expand Up @@ -155,6 +165,44 @@ class PassNode extends TempNode {

}

getPreviousTexture( name ) {

let texture = this._previousTextures[ name ];

if ( texture === undefined ) {

texture = this.getTexture( name ).clone();
texture.isRenderTargetTexture = true;

this._previousTextures[ name ] = texture;

}

return texture;

}

toggleTexture( name ) {

const prevTexture = this._previousTextures[ name ];

if ( prevTexture !== undefined ) {

const texture = this._textures[ name ];

const index = this.renderTarget.textures.indexOf( texture );
this.renderTarget.textures[ index ] = prevTexture;

this._textures[ name ] = prevTexture;
this._previousTextures[ name ] = texture;

this._textureNodes[ name ].updateTexture();
this._previousTextureNodes[ name ].updateTexture();

}

}

getTextureNode( name = 'output' ) {

let textureNode = this._textureNodes[ name ];
Expand All @@ -169,6 +217,20 @@ class PassNode extends TempNode {

}

getPreviousTextureNode( name = 'output' ) {

let textureNode = this._previousTextureNodes[ name ];

if ( textureNode === undefined ) {

this._previousTextureNodes[ name ] = textureNode = nodeObject( new PassMultipleTextureNode( this, name, true ) );

}

return textureNode;

}

getViewZNode( name = 'depth' ) {

let viewZNode = this._viewZNodes[ name ];
Expand Down Expand Up @@ -240,6 +302,12 @@ class PassNode extends TempNode {
this._cameraNear.value = camera.near;
this._cameraFar.value = camera.far;

for ( const name in this._previousTextures ) {

this.toggleTexture( name );

}

renderer.setRenderTarget( this.renderTarget );
renderer.setMRT( this._mrt );

Expand Down
24 changes: 24 additions & 0 deletions src/renderers/common/RenderContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ class RenderContext {

}

getCacheKey() {

return getCacheKey( this );

}

}

export function getCacheKey( renderContext ) {

const { textures, activeCubeFace } = renderContext;

let key = '';

for ( const texture of textures ) {

key += texture.id + ',';

}

key += activeCubeFace;

return key;

}

export default RenderContext;
Loading

0 comments on commit fffc941

Please sign in to comment.