-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* TransitionNode sketch * cleanup * fix non-functional controller * texture loading fixed * lint fix * fix screenshot * remove texture import * Update TransitionNode.js * Update webgpu_postprocessing_transition.html --------- Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
- Loading branch information
1 parent
d82e3e6
commit ffef510
Showing
5 changed files
with
343 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>three.js webgpu - scenes transition</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> webgpu scene transitions.<br/> | ||
Original implementation by <a href="https://twitter.com/fernandojsg">fernandojsg</a> - <a href="https://github.com/kile/three.js-demos">github</a> | ||
</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 { GUI } from 'three/addons/libs/lil-gui.module.min.js'; | ||
import TWEEN from 'three/addons/libs/tween.module.js'; | ||
import { uniform, transition, pass } from 'three/tsl'; | ||
|
||
let renderer, postProcessing, transitionController, transitionPass; | ||
|
||
const textures = []; | ||
const clock = new THREE.Clock(); | ||
|
||
const effectController = { | ||
animateScene: true, | ||
animateTransition: true, | ||
transition: 0, | ||
_transition: uniform( 0 ), | ||
useTexture: true, | ||
_useTexture: uniform( 1 ), | ||
texture: 5, | ||
cycle: true, | ||
threshold: uniform( 0.1 ), | ||
}; | ||
|
||
function generateInstancedMesh( geometry, material, count ) { | ||
|
||
const mesh = new THREE.InstancedMesh( geometry, material, count ); | ||
|
||
const dummy = new THREE.Object3D(); | ||
const color = new THREE.Color(); | ||
|
||
for ( let i = 0; i < count; i ++ ) { | ||
|
||
dummy.position.x = Math.random() * 100 - 50; | ||
dummy.position.y = Math.random() * 60 - 30; | ||
dummy.position.z = Math.random() * 80 - 40; | ||
|
||
dummy.rotation.x = Math.random() * 2 * Math.PI; | ||
dummy.rotation.y = Math.random() * 2 * Math.PI; | ||
dummy.rotation.z = Math.random() * 2 * Math.PI; | ||
|
||
dummy.scale.x = Math.random() * 2 + 1; | ||
|
||
if ( geometry.type === 'BoxGeometry' ) { | ||
|
||
dummy.scale.y = Math.random() * 2 + 1; | ||
dummy.scale.z = Math.random() * 2 + 1; | ||
|
||
} else { | ||
|
||
dummy.scale.y = dummy.scale.x; | ||
dummy.scale.z = dummy.scale.x; | ||
|
||
} | ||
|
||
dummy.updateMatrix(); | ||
|
||
mesh.setMatrixAt( i, dummy.matrix ); | ||
mesh.setColorAt( i, color.setScalar( 0.1 + 0.9 * Math.random() ) ); | ||
|
||
} | ||
|
||
return mesh; | ||
|
||
} | ||
|
||
function FXScene( geometry, rotationSpeed, backgroundColor ) { | ||
|
||
const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 ); | ||
camera.position.z = 20; | ||
|
||
// Setup scene | ||
const scene = new THREE.Scene(); | ||
scene.background = new THREE.Color( backgroundColor ); | ||
scene.add( new THREE.AmbientLight( 0xaaaaaa, 3 ) ); | ||
|
||
const light = new THREE.DirectionalLight( 0xffffff, 3 ); | ||
light.position.set( 0, 1, 4 ); | ||
scene.add( light ); | ||
|
||
this.rotationSpeed = rotationSpeed; | ||
|
||
const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000; | ||
const material = new THREE.MeshPhongNodeMaterial( { color: color, flatShading: true } ); | ||
const mesh = generateInstancedMesh( geometry, material, 500 ); | ||
scene.add( mesh ); | ||
|
||
this.scene = scene; | ||
this.camera = camera; | ||
this.mesh = mesh; | ||
|
||
this.update = function ( delta ) { | ||
|
||
if ( effectController.animateScene ) { | ||
|
||
mesh.rotation.x += this.rotationSpeed.x * delta; | ||
mesh.rotation.y += this.rotationSpeed.y * delta; | ||
mesh.rotation.z += this.rotationSpeed.z * delta; | ||
|
||
} | ||
|
||
}; | ||
|
||
this.resize = function () { | ||
|
||
camera.aspect = window.innerWidth / window.innerHeight; | ||
camera.updateProjectionMatrix(); | ||
|
||
}; | ||
|
||
} | ||
|
||
const fxSceneA = new FXScene( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.Vector3( 0, - 0.4, 0 ), 0xffffff ); | ||
const fxSceneB = new FXScene( new THREE.IcosahedronGeometry( 1, 1 ), new THREE.Vector3( 0, 0.2, 0.1 ), 0x000000 ); | ||
|
||
function init() { | ||
|
||
// Initialize textures | ||
|
||
const loader = new THREE.TextureLoader(); | ||
|
||
for ( let i = 0; i < 6; i ++ ) { | ||
|
||
textures[ i ] = loader.load( 'textures/transition/transition' + ( i + 1 ) + '.png' ); | ||
|
||
} | ||
|
||
renderer = new THREE.WebGPURenderer( { antialias: true } ); | ||
renderer.setPixelRatio( window.devicePixelRatio ); | ||
renderer.setSize( window.innerWidth, window.innerHeight ); | ||
renderer.setAnimationLoop( animate ); | ||
document.body.appendChild( renderer.domElement ); | ||
|
||
postProcessing = new THREE.PostProcessing( renderer ); | ||
|
||
const scenePassA = pass( fxSceneA.scene, fxSceneA.camera ); | ||
const scenePassB = pass( fxSceneB.scene, fxSceneB.camera ); | ||
|
||
transitionPass = transition( scenePassA, scenePassB, new THREE.TextureNode( textures[ effectController.texture ] ), effectController._transition, effectController.threshold, effectController._useTexture ); | ||
|
||
postProcessing.outputNode = transitionPass; | ||
|
||
const gui = new GUI(); | ||
|
||
gui.add( effectController, 'animateScene' ).name( 'Animate Scene' ); | ||
gui.add( effectController, 'animateTransition' ).name( 'Animate Transition' ); | ||
transitionController = gui.add( effectController, 'transition', 0, 1, 0.01 ).name( 'transition' ).onChange( () => { | ||
|
||
effectController._transition.value = effectController.transition; | ||
|
||
} ); | ||
gui.add( effectController, 'useTexture' ).onChange( () => { | ||
|
||
const value = effectController.useTexture ? 1 : 0; | ||
effectController._useTexture.value = value; | ||
|
||
} ); | ||
gui.add( effectController, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ); | ||
gui.add( effectController, 'cycle' ); | ||
gui.add( effectController.threshold, 'value', 0, 1, 0.01 ).name( 'threshold' ); | ||
|
||
} | ||
|
||
window.addEventListener( 'resize', onWindowResize ); | ||
|
||
function onWindowResize() { | ||
|
||
fxSceneA.resize(); | ||
fxSceneB.resize(); | ||
renderer.setSize( window.innerWidth, window.innerHeight ); | ||
|
||
} | ||
|
||
new TWEEN.Tween( effectController ) | ||
.to( { transition: 1 }, 1500 ) | ||
.onUpdate( function ( ) { | ||
|
||
transitionController.setValue( effectController.transition ); | ||
|
||
// Change the current alpha texture after each transition | ||
if ( effectController.cycle ) { | ||
|
||
if ( effectController.transition == 0 || effectController.transition == 1 ) { | ||
|
||
effectController.texture = ( effectController.texture + 1 ) % textures.length; | ||
|
||
} | ||
|
||
} | ||
|
||
} ) | ||
.repeat( Infinity ) | ||
.delay( 2000 ) | ||
.yoyo( true ) | ||
.start(); | ||
|
||
function animate() { | ||
|
||
if ( effectController.animateTransition ) TWEEN.update(); | ||
|
||
if ( textures[ effectController.texture ] ) { | ||
|
||
const mixTexture = textures[ effectController.texture ]; | ||
transitionPass.mixTextureNode.value = mixTexture; | ||
|
||
} | ||
|
||
const delta = clock.getDelta(); | ||
fxSceneA.update( delta ); | ||
fxSceneB.update( delta ); | ||
|
||
render(); | ||
|
||
} | ||
|
||
function render() { | ||
|
||
// Prevent render both scenes when it's not necessary | ||
if ( effectController.transition === 0 ) { | ||
|
||
renderer.render( fxSceneB.scene, fxSceneB.camera ); | ||
|
||
} else if ( effectController.transition === 1 ) { | ||
|
||
renderer.render( fxSceneA.scene, fxSceneA.camera ); | ||
|
||
} else { | ||
|
||
postProcessing.render(); | ||
|
||
} | ||
|
||
} | ||
|
||
init(); | ||
|
||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import TempNode from '../core/TempNode.js'; | ||
import { uv } from '../accessors/UVNode.js'; | ||
import { addNodeElement, tslFn, nodeObject, float, int, vec4, If } from '../shadernode/ShaderNode.js'; | ||
import { clamp, mix } from '../math/MathNode.js'; | ||
import { sub } from '../math/OperatorNode.js'; | ||
|
||
class TransitionNode extends TempNode { | ||
|
||
constructor( textureNodeA, textureNodeB, mixTextureNode, mixRatioNode, thresholdNode, useTextureNode ) { | ||
|
||
super(); | ||
|
||
// Input textures | ||
|
||
this.textureNodeA = textureNodeA; | ||
this.textureNodeB = textureNodeB; | ||
this.mixTextureNode = mixTextureNode; | ||
|
||
// Uniforms | ||
|
||
this.mixRatioNode = mixRatioNode; | ||
this.thresholdNode = thresholdNode; | ||
this.useTextureNode = useTextureNode; | ||
|
||
} | ||
|
||
setup() { | ||
|
||
const { textureNodeA, textureNodeB, mixTextureNode, mixRatioNode, thresholdNode, useTextureNode } = this; | ||
|
||
const sampleTexture = ( textureNode ) => { | ||
|
||
const uvNodeTexture = textureNode.uvNode || uv(); | ||
return textureNode.uv( uvNodeTexture ); | ||
|
||
}; | ||
|
||
const transition = tslFn( () => { | ||
|
||
const texelOne = sampleTexture( textureNodeA ); | ||
const texelTwo = sampleTexture( textureNodeB ); | ||
|
||
const color = vec4().toVar(); | ||
|
||
If( useTextureNode.equal( int( 1 ) ), () => { | ||
|
||
const transitionTexel = sampleTexture( mixTextureNode ); | ||
const r = mixRatioNode.mul( thresholdNode.mul( 2.0 ).add( 1.0 ) ).sub( thresholdNode ); | ||
const mixf = clamp( sub( transitionTexel.r, r ).mul( float( 1.0 ).div( thresholdNode ) ), 0.0, 1.0 ); | ||
|
||
color.assign( mix( texelOne, texelTwo, mixf ) ); | ||
|
||
} ).else( () => { | ||
|
||
color.assign( mix( texelTwo, texelOne, mixRatioNode ) ); | ||
|
||
} ); | ||
|
||
return color; | ||
|
||
} ); | ||
|
||
const outputNode = transition(); | ||
|
||
return outputNode; | ||
|
||
} | ||
|
||
} | ||
|
||
export const transition = ( nodeA, nodeB, mixTexture, mixRatio = 0.0, threshold = 0.1, useTexture = 0 ) => nodeObject( new TransitionNode( nodeObject( nodeA ).toTexture(), nodeObject( nodeB ).toTexture(), nodeObject( mixTexture ).toTexture(), nodeObject( mixRatio ), nodeObject( threshold ), nodeObject( useTexture ) ) ); | ||
|
||
addNodeElement( 'transition', transition ); | ||
|
||
export default TransitionNode; | ||
|