فهرست منبع

Examples: Add transition effect. (#28174)

* Examples: Add transition effect.

* remove useless code

remove useless code

* remove animate()

* revert: renderer.setSize
林炳权 1 سال پیش
والد
کامیت
b7b70a8eb0
2فایلهای تغییر یافته به همراه328 افزوده شده و 262 حذف شده
  1. 193 0
      examples/jsm/postprocessing/RenderTransitionPass.js
  2. 135 262
      examples/webgl_postprocessing_crossfade.html

+ 193 - 0
examples/jsm/postprocessing/RenderTransitionPass.js

@@ -0,0 +1,193 @@
+import {
+	HalfFloatType,
+	ShaderMaterial,
+	WebGLRenderTarget
+} from 'three';
+import { FullScreenQuad, Pass } from './Pass.js';
+
+class RenderTransitionPass extends Pass {
+
+	constructor( sceneA, cameraA, sceneB, cameraB ) {
+
+		super();
+
+		this.material = this.createMaterial();
+		this.fsQuad = new FullScreenQuad( this.material );
+
+		this.sceneA = sceneA;
+		this.cameraA = cameraA;
+		this.sceneB = sceneB;
+		this.cameraB = cameraB;
+
+		this.renderTargetA = new WebGLRenderTarget();
+		this.renderTargetA.texture.type = HalfFloatType;
+		this.renderTargetB = new WebGLRenderTarget();
+		this.renderTargetB.texture.type = HalfFloatType;
+
+	}
+
+	setTransition( value ) {
+
+		this.material.uniforms.mixRatio.value = value;
+
+	}
+
+	useTexture( value ) {
+
+		this.material.uniforms.useTexture.value = value ? 1 : 0;
+
+	}
+
+	setTexture( value ) {
+
+		this.material.uniforms.tMixTexture.value = value;
+
+	}
+
+	setTextureThreshold( value ) {
+
+		this.material.uniforms.threshold.value = value;
+
+	}
+
+	setSize( width, height ) {
+
+		this.renderTargetA.setSize( width, height );
+		this.renderTargetB.setSize( width, height );
+
+	}
+
+	render( renderer, writeBuffer ) {
+
+		const uniforms = this.fsQuad.material.uniforms;
+		const transition = uniforms.mixRatio.value;
+
+		// Prevent render both scenes when it's not necessary
+
+		if ( transition === 0 ) {
+
+			renderer.setRenderTarget( writeBuffer );
+			if ( this.clear ) renderer.clear();
+			renderer.render( this.sceneB, this.cameraB );
+
+		} else if ( transition === 1 ) {
+
+			renderer.setRenderTarget( writeBuffer );
+			if ( this.clear ) renderer.clear();
+			renderer.render( this.sceneA, this.cameraA );
+
+		} else {
+
+			// When 0 < transition < 1 render transition between two scenes
+
+			renderer.setRenderTarget( this.renderTargetA );
+			renderer.render( this.sceneA, this.cameraA );
+			renderer.setRenderTarget( this.renderTargetB );
+			renderer.render( this.sceneB, this.cameraB );
+
+			uniforms.tDiffuse1.value = this.renderTargetA.texture;
+			uniforms.tDiffuse2.value = this.renderTargetB.texture;
+
+			if ( this.renderToScreen ) {
+
+				renderer.setRenderTarget( null );
+				renderer.clear();
+
+			} else {
+
+				renderer.setRenderTarget( writeBuffer );
+				if ( this.clear ) renderer.clear();
+
+			}
+
+			this.fsQuad.render( renderer );
+
+		}
+
+	}
+
+	dispose() {
+
+		this.renderTargetA.dispose();
+		this.renderTargetB.dispose();
+		this.material.dispose();
+		this.fsQuad.dispose();
+
+	}
+
+	createMaterial() {
+
+		return new ShaderMaterial( {
+			uniforms: {
+				tDiffuse1: {
+					value: null
+				},
+				tDiffuse2: {
+					value: null
+				},
+				mixRatio: {
+					value: 0.0
+				},
+				threshold: {
+					value: 0.1
+				},
+				useTexture: {
+					value: 1
+				},
+				tMixTexture: {
+					value: null
+				}
+			},
+			vertexShader: /* glsl */`
+				varying vec2 vUv;
+
+				void main() {
+
+					vUv = vec2( uv.x, uv.y );
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+
+				}
+			`,
+			fragmentShader: /* glsl */`
+				uniform float mixRatio;
+
+				uniform sampler2D tDiffuse1;
+				uniform sampler2D tDiffuse2;
+				uniform sampler2D tMixTexture;
+
+				uniform int useTexture;
+				uniform float threshold;
+
+				varying vec2 vUv;
+
+				void main() {
+
+					vec4 texel1 = texture2D( tDiffuse1, vUv );
+					vec4 texel2 = texture2D( tDiffuse2, vUv );
+
+					if (useTexture == 1) {
+
+						vec4 transitionTexel = texture2D( tMixTexture, vUv );
+						float r = mixRatio * ( 1.0 + threshold * 2.0 ) - threshold;
+						float mixf = clamp( ( transitionTexel.r - r ) * ( 1.0 / threshold ), 0.0, 1.0 );
+
+						gl_FragColor = mix( texel1, texel2, mixf );
+
+					} else {
+
+						gl_FragColor = mix( texel2, texel1, mixRatio );
+
+					}
+
+					#include <tonemapping_fragment>
+					#include <colorspace_fragment>
+
+				}
+			`
+		} );
+
+	}
+
+}
+
+export { RenderTransitionPass };

+ 135 - 262
examples/webgl_postprocessing_crossfade.html

@@ -13,8 +13,6 @@
 			by <a href="https://twitter.com/fernandojsg">fernandojsg</a> - <a href="https://github.com/kile/three.js-demos">github</a>
 		</div>
 
-		<div id="container"></div>
-
 		<script type="importmap">
 			{
 				"imports": {
@@ -31,155 +29,181 @@
 			import Stats from 'three/addons/libs/stats.module.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import TWEEN from 'three/addons/libs/tween.module.js';
-			import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
-
-			let container, stats;
-			let renderer;
-			let transition;
-
-			const transitionParams = {
-				'useTexture': true,
-				'transition': 0,
-				'texture': 5,
-				'cycle': true,
-				'animate': true,
-				'threshold': 0.3
-			};
+			import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
+			import { RenderTransitionPass } from 'three/addons/postprocessing/RenderTransitionPass.js';
+			import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
+
+			let stats;
+			let renderer, composer, renderTransitionPass;
 
+			const textures = [];
 			const clock = new THREE.Clock();
 
+			const params = {
+				sceneAnimate: true,
+				transitionAnimate: true,
+				transition: 0,
+				useTexture: true,
+				texture: 5,
+				cycle: true,
+				threshold: 0.1,
+			};
+
+			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 );
+
 			init();
-			animate();
 
 			function init() {
 
 				initGUI();
-
-				container = document.getElementById( 'container' );
+				initTextures();
 
 				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
-				container.appendChild( renderer.domElement );
+				document.body.appendChild( renderer.domElement );
+
+				composer = new EffectComposer( renderer );
 
 				stats = new Stats();
-				container.appendChild( stats.dom );
+				document.body.appendChild( stats.dom );
 
-				const geometryA = new THREE.BoxGeometry( 2, 2, 2 );
-				const geometryB = new THREE.IcosahedronGeometry( 1, 1 );
-				const sceneA = new FXScene( geometryA, new THREE.Vector3( 0, - 0.4, 0 ), 0xffffff );
-				const sceneB = new FXScene( geometryB, new THREE.Vector3( 0, 0.2, 0.1 ), 0x000000 );
+				renderTransitionPass = new RenderTransitionPass( fxSceneA.scene, fxSceneA.camera, fxSceneB.scene, fxSceneB.camera );
+				renderTransitionPass.setTexture( textures[ 0 ] );
+				composer.addPass( renderTransitionPass );
 
-				transition = new Transition( sceneA, sceneB );
+				const outputPass = new OutputPass();
+				composer.addPass( outputPass );
 
-				window.addEventListener( 'resize', onWindowResize );
+				renderer.setAnimationLoop( animate );
 
-				function onWindowResize() {
+			}
 
-					sceneA.resize();
-					sceneB.resize();
-					renderer.setSize( window.innerWidth, window.innerHeight );
+			window.addEventListener( 'resize', onWindowResize );
 
-				}
+			function onWindowResize() {
+
+				fxSceneA.resize();
+				fxSceneB.resize();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				composer.setSize( window.innerWidth, window.innerHeight );
 
 			}
 
+			new TWEEN.Tween( params )
+				.to( { transition: 1 }, 1500 )
+				.onUpdate( function () {
+
+					renderTransitionPass.setTransition( params.transition );
+
+					// Change the current alpha texture after each transition
+					if ( params.cycle ) {
+
+						if ( params.transition == 0 || params.transition == 1 ) {
+
+							params.texture = ( params.texture + 1 ) % textures.length;
+							renderTransitionPass.setTexture( textures[ params.texture ] );
+
+						}
+
+					}
+
+				} )
+				.repeat( Infinity )
+				.delay( 2000 )
+				.yoyo( true )
+				.start();
+
 			function animate() {
 
-				requestAnimationFrame( animate );
+				const delta = clock.getDelta();
+				fxSceneA.render( delta );
+				fxSceneB.render( delta );
 
 				render();
 				stats.update();
 
-			}
-
-			function initGUI() {
+				// Transition animation
+				if ( params.transitionAnimate ) TWEEN.update();
 
-				const gui = new GUI();
+			}
 
-				gui.add( transitionParams, 'animate' );
-				gui.add( transitionParams, 'transition', 0, 1, 0.01 ).listen();
+			function initTextures() {
 
-				gui.add( transitionParams, 'useTexture' ).onChange( function ( value ) {
+				const loader = new THREE.TextureLoader();
 
-					transition.useTexture( value );
+				for ( let i = 0; i < 6; i ++ ) {
 
-				} );
+					textures[ i ] = loader.load( 'textures/transition/transition' + ( i + 1 ) + '.png' );
 
-				gui.add( transitionParams, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ).onChange( function ( value ) {
+				}
 
-					transition.setTexture( value );
+			}
 
-				} ).listen();
+			function initGUI() {
 
-				gui.add( transitionParams, 'cycle' );
+				const gui = new GUI();
 
-				gui.add( transitionParams, 'threshold', 0, 1, 0.01 ).onChange( function ( value ) {
+				gui.add( params, 'sceneAnimate' ).name( 'Animate scene' );
+				gui.add( params, 'transitionAnimate' ).name( 'Animate transition' );
+				gui.add( params, 'transition', 0, 1, 0.01 ).onChange( function ( value ) {
 
-					transition.setTextureThreshold( value );
+					renderTransitionPass.setTransition( value );
 
-				} );
+				} ).listen();
 
-			}
+				gui.add( params, 'useTexture' ).onChange( function ( value ) {
 
+					renderTransitionPass.useTexture( value );
 
-			function render() {
+				} );
 
-				transition.render( clock.getDelta() );
+				gui.add( params, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ).onChange( function ( value ) {
 
-			}
+					renderTransitionPass.setTexture( textures[ value ] );
 
-			function generateInstancedMesh( geometry, material, count ) {
+				} ).listen();
 
-				const mesh = new THREE.InstancedMesh( geometry, material, count );
+				gui.add( params, 'cycle' );
 
-				const dummy = new THREE.Object3D();
-				const color = new THREE.Color();
+				gui.add( params, 'threshold', 0, 1, 0.01 ).onChange( function ( value ) {
 
-				for ( let i = 0; i < count; i ++ ) {
+					renderTransitionPass.setTextureThreshold( value );
 
-					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' ) {
+			function render() {
 
-						dummy.scale.y = Math.random() * 2 + 1;
-						dummy.scale.z = Math.random() * 2 + 1;
+				// Prevent render both scenes when it's not necessary
+				if ( params.transition === 0 ) {
 
-					} else {
+					renderer.render( fxSceneB.scene, fxSceneB.camera );
 
-						dummy.scale.y = dummy.scale.x;
-						dummy.scale.z = dummy.scale.x;
+				} else if ( params.transition === 1 ) {
 
-					}
+					renderer.render( fxSceneA.scene, fxSceneA.camera );
 
-					dummy.updateMatrix();
+				} else {
 
-					mesh.setMatrixAt( i, dummy.matrix );
-					mesh.setColorAt( i, color.setScalar( 0.1 + 0.9 * Math.random() ) );
+					// When 0 < transition < 1 render transition between two scenes
+					composer.render();
 
 				}
 
-				return mesh;
-
 			}
 
-			function FXScene( geometry, rotationSpeed, clearColor ) {
-
-				this.clearColor = clearColor;
+			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 );
@@ -193,26 +217,17 @@
 				const mesh = generateInstancedMesh( geometry, material, 500 );
 				scene.add( mesh );
 
-				this.fbo = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { type: THREE.HalfFloatType } );
-
-				this.render = function ( delta, rtt ) {
-
-					mesh.rotation.x += delta * this.rotationSpeed.x;
-					mesh.rotation.y += delta * this.rotationSpeed.y;
-					mesh.rotation.z += delta * this.rotationSpeed.z;
+				this.scene = scene;
+				this.camera = camera;
+				this.mesh = mesh;
 
-					renderer.setClearColor( this.clearColor );
-
-					if ( rtt ) {
+				this.render = function ( delta ) {
 
-						renderer.setRenderTarget( this.fbo );
-						renderer.clear();
-						renderer.render( scene, camera );
+					if ( params.sceneAnimate ) {
 
-					} else {
-
-						renderer.setRenderTarget( null );
-						renderer.render( scene, camera );
+						mesh.rotation.x += this.rotationSpeed.x * delta;
+						mesh.rotation.y += this.rotationSpeed.y * delta;
+						mesh.rotation.z += this.rotationSpeed.z * delta;
 
 					}
 
@@ -222,194 +237,52 @@
 
 					camera.aspect = window.innerWidth / window.innerHeight;
 					camera.updateProjectionMatrix();
-					this.fbo.setSize( window.innerWidth, window.innerHeight );
 
 				};
 
 			}
 
-			function Transition( sceneA, sceneB ) {
-
-				const textures = [];
-
-				const loader = new THREE.TextureLoader();
-
-				for ( let i = 0; i < 6; i ++ ) {
-
-					textures[ i ] = loader.load( 'textures/transition/transition' + ( i + 1 ) + '.png' );
-
-				}
-
-				const material = new THREE.ShaderMaterial( {
-
-					uniforms: {
-
-						tDiffuse1: {
-							value: null
-						},
-						tDiffuse2: {
-							value: null
-						},
-						mixRatio: {
-							value: 0.0
-						},
-						threshold: {
-							value: 0.1
-						},
-						useTexture: {
-							value: 1
-						},
-						tMixTexture: {
-							value: textures[ 0 ]
-						}
-					},
-					vertexShader: [
-
-						'varying vec2 vUv;',
-
-						'void main() {',
-
-						'vUv = vec2( uv.x, uv.y );',
-						'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-
-						'}'
-
-					].join( '\n' ),
-					fragmentShader: [
-
-						'uniform float mixRatio;',
-
-						'uniform sampler2D tDiffuse1;',
-						'uniform sampler2D tDiffuse2;',
-						'uniform sampler2D tMixTexture;',
-
-						'uniform int useTexture;',
-						'uniform float threshold;',
-
-						'varying vec2 vUv;',
-
-						'void main() {',
-
-						'	vec4 texel1 = texture2D( tDiffuse1, vUv );',
-						'	vec4 texel2 = texture2D( tDiffuse2, vUv );',
-
-						'	if (useTexture==1) {',
-
-						'		vec4 transitionTexel = texture2D( tMixTexture, vUv );',
-						'		float r = mixRatio * (1.0 + threshold * 2.0) - threshold;',
-						'		float mixf=clamp((transitionTexel.r - r)*(1.0/threshold), 0.0, 1.0);',
-
-						'		gl_FragColor = mix( texel1, texel2, mixf );',
-
-						'	} else {',
-
-						'		gl_FragColor = mix( texel2, texel1, mixRatio );',
-
-						'	}',
-
-						'	#include <tonemapping_fragment>',
-						'	#include <colorspace_fragment>',
-
-						'}'
-
-					].join( '\n' )
-
-				} );
-
-				const fsQuad = new FullScreenQuad( material );
-
-				material.uniforms.tDiffuse1.value = sceneA.fbo.texture;
-				material.uniforms.tDiffuse2.value = sceneB.fbo.texture;
-
-				new TWEEN.Tween( transitionParams )
-					.to( { transition: 1 }, 1500 )
-					.repeat( Infinity )
-					.delay( 2000 )
-					.yoyo( true )
-					.start();
-
-				this.needsTextureChange = false;
-
-				this.setTextureThreshold = function ( value ) {
-
-					material.uniforms.threshold.value = value;
-
-				};
-
-				this.useTexture = function ( value ) {
-
-					material.uniforms.useTexture.value = value ? 1 : 0;
-
-				};
-
-				this.setTexture = function ( i ) {
-
-					material.uniforms.tMixTexture.value = textures[ i ];
-
-				};
-
-				this.render = function ( delta ) {
-
-					// Transition animation
-					if ( transitionParams.animate ) {
-
-						TWEEN.update();
-
-						// Change the current alpha texture after each transition
-						if ( transitionParams.cycle ) {
+			function generateInstancedMesh( geometry, material, count ) {
 
-							if ( transitionParams.transition == 0 || transitionParams.transition == 1 ) {
+				const mesh = new THREE.InstancedMesh( geometry, material, count );
 
-								if ( this.needsTextureChange ) {
+				const dummy = new THREE.Object3D();
+				const color = new THREE.Color();
 
-									transitionParams.texture = ( transitionParams.texture + 1 ) % textures.length;
-									material.uniforms.tMixTexture.value = textures[ transitionParams.texture ];
-									this.needsTextureChange = false;
+				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;
 
-							} else {
+					dummy.rotation.x = Math.random() * 2 * Math.PI;
+					dummy.rotation.y = Math.random() * 2 * Math.PI;
+					dummy.rotation.z = Math.random() * 2 * Math.PI;
 
-								this.needsTextureChange = true;
+					dummy.scale.x = Math.random() * 2 + 1;
 
-							}
+					if ( geometry.type === 'BoxGeometry' ) {
 
-						} else {
+						dummy.scale.y = Math.random() * 2 + 1;
+						dummy.scale.z = Math.random() * 2 + 1;
 
-							this.needsTextureChange = true;
+					} else {
 
-						}
+						dummy.scale.y = dummy.scale.x;
+						dummy.scale.z = dummy.scale.x;
 
 					}
 
-					material.uniforms.mixRatio.value = transitionParams.transition;
-
-					// Prevent render both scenes when it's not necessary
-					if ( transitionParams.transition == 0 ) {
-
-						sceneB.render( delta, false );
-
-					} else if ( transitionParams.transition == 1 ) {
-
-						sceneA.render( delta, false );
-
-					} else {
-
-						// When 0<transition<1 render transition between two scenes
-
-						sceneA.render( delta, true );
-						sceneB.render( delta, true );
+					dummy.updateMatrix();
 
-						renderer.setRenderTarget( null );
-						renderer.clear();
-						fsQuad.render( renderer );
+					mesh.setMatrixAt( i, dummy.matrix );
+					mesh.setColorAt( i, color.setScalar( 0.1 + 0.9 * Math.random() ) );
 
-					}
+				}
 
-				};
+				return mesh;
 
 			}
-
 		</script>
 	</body>
 </html>