Skip to content

WebGPURenderer: Support for access previous frame textures using pass() #29069

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

Merged
merged 9 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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