Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
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( { forceWebGL: true });
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 );

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>
79 changes: 76 additions & 3 deletions src/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,40 @@ 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;

this.updateType = NodeUpdateType.RENDER;

}

updateTexture() {

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

}

update() {

this.updateTexture();

}

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 +119,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 +173,41 @@ 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;

}

}

getTextureNode( name = 'output' ) {

let textureNode = this._textureNodes[ name ];
Expand All @@ -169,6 +222,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 +307,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
18 changes: 18 additions & 0 deletions src/renderers/common/RenderContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ class RenderContext {

}

getCacheKey() {

const { textures, activeCubeFace } = this;

let key = '';

for ( const texture of textures ) {

key += texture.id + ',';

}

key += activeCubeFace;

return key;

}

}

export default RenderContext;
19 changes: 10 additions & 9 deletions src/renderers/webgl-fallback/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -1253,21 +1253,21 @@ class WebGLBackend extends Backend {
let msaaFb = renderTargetContextData.msaaFrameBuffer;
let depthRenderbuffer = renderTargetContextData.depthRenderbuffer;

const cacheKey = renderContext.getCacheKey();

let fb;

if ( isCube ) {

if ( renderTargetContextData.cubeFramebuffers === undefined ) {

renderTargetContextData.cubeFramebuffers = [];

}
renderTargetContextData.cubeFramebuffers || ( renderTargetContextData.cubeFramebuffers = {} );

fb = renderTargetContextData.cubeFramebuffers[ cubeFace ];

} else {

fb = renderTargetContextData.framebuffer;
renderTargetContextData.framebuffers || ( renderTargetContextData.framebuffers = {} );

fb = renderTargetContextData.framebuffers[ cacheKey ];

}

Expand All @@ -1281,13 +1281,16 @@ class WebGLBackend extends Backend {

if ( isCube ) {

renderTargetContextData.cubeFramebuffers[ cubeFace ] = fb;
renderTargetContextData.cubeFramebuffers[ cacheKey ] = fb;

const { textureGPU } = this.get( textures[ 0 ] );

gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + cubeFace, textureGPU, 0 );

} else {

renderTargetContextData.framebuffers[ cacheKey ] = fb;

for ( let i = 0; i < textures.length; i ++ ) {

const texture = textures[ i ];
Expand All @@ -1300,8 +1303,6 @@ class WebGLBackend extends Backend {

}

renderTargetContextData.framebuffer = fb;

state.drawBuffers( renderContext, fb );

}
Expand Down
24 changes: 10 additions & 14 deletions src/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,22 +193,16 @@ class WebGPUBackend extends Backend {

let descriptors = renderTargetData.descriptors;

if ( descriptors === undefined ) {

descriptors = [];

renderTargetData.descriptors = descriptors;

}

if ( renderTargetData.width !== renderTarget.width ||
if ( descriptors === undefined ||
renderTargetData.width !== renderTarget.width ||
renderTargetData.height !== renderTarget.height ||
renderTargetData.activeMipmapLevel !== renderTarget.activeMipmapLevel ||
renderTargetData.samples !== renderTarget.samples ||
descriptors.length !== renderTarget.textures.length
renderTargetData.samples !== renderTarget.samples
) {

descriptors.length = 0;
descriptors = {};

renderTargetData.descriptors = descriptors;

// dispose

Expand All @@ -224,7 +218,9 @@ class WebGPUBackend extends Backend {

}

let descriptor = descriptors[ renderContext.activeCubeFace ];
const cacheKey = renderContext.getCacheKey();

let descriptor = descriptors[ cacheKey ];

if ( descriptor === undefined ) {

Expand Down Expand Up @@ -276,7 +272,7 @@ class WebGPUBackend extends Backend {
depthStencilAttachment
};

descriptors[ renderContext.activeCubeFace ] = descriptor;
descriptors[ cacheKey ] = descriptor;

renderTargetData.width = renderTarget.width;
renderTargetData.height = renderTarget.height;
Expand Down
Loading