Skip to content

Commit

Permalink
Merge pull request #13917 from HypnosNova/afterimage-effect
Browse files Browse the repository at this point in the history
add afterimage effect
  • Loading branch information
mrdoob committed Jul 31, 2018
2 parents 251ebc8 + 972faec commit 58f60e7
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/files.js
Expand Up @@ -222,6 +222,7 @@ var files = {
"webgl_points_sprites",
"webgl_postprocessing",
"webgl_postprocessing_advanced",
"webgl_postprocessing_afterimage",
"webgl_postprocessing_backgrounds",
"webgl_postprocessing_crossfade",
"webgl_postprocessing_dof",
Expand Down
88 changes: 88 additions & 0 deletions examples/js/postprocessing/AfterimagePass.js
@@ -0,0 +1,88 @@
/**
* @author HypnosNova / https://www.threejs.org.cn/gallery/
*/

THREE.AfterimagePass = function ( damp ) {

THREE.Pass.call( this );

if ( THREE.AfterimageShader === undefined )
console.error( "THREE.AfterimagePass relies on THREE.AfterimageShader" );

this.shader = THREE.AfterimageShader;

this.uniforms = THREE.UniformsUtils.clone( this.shader.uniforms );

this.uniforms[ "damp" ].value = damp !== undefined ? damp : 0.96;

this.textureComp = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {

minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBAFormat

} );

this.textureOld = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {

minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBAFormat

} );

this.shaderMaterial = new THREE.ShaderMaterial( {

uniforms: this.uniforms,
vertexShader: this.shader.vertexShader,
fragmentShader: this.shader.fragmentShader

} );

this.sceneComp = new THREE.Scene();
this.scene = new THREE.Scene();

this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
this.camera.position.z = 1;

var geometry = new THREE.PlaneBufferGeometry( 2, 2 );

this.quadComp = new THREE.Mesh( geometry, this.shaderMaterial );
this.sceneComp.add( this.quadComp );

var material = new THREE.MeshBasicMaterial( {
map: this.textureComp.texture
} );

var quadScreen = new THREE.Mesh( geometry, material );
this.scene.add( quadScreen );

};

THREE.AfterimagePass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {

constructor: THREE.AfterimagePass,

render: function ( renderer, writeBuffer, readBuffer ) {

this.uniforms[ "tOld" ].value = this.textureOld.texture;
this.uniforms[ "tNew" ].value = readBuffer.texture;

this.quadComp.material = this.shaderMaterial;

renderer.render( this.sceneComp, this.camera, this.textureComp );
renderer.render( this.scene, this.camera, this.textureOld );

if ( this.renderToScreen ) {

renderer.render( this.scene, this.camera );

} else {

renderer.render( this.scene, this.camera, writeBuffer, this.clear );

}

}

} );
60 changes: 60 additions & 0 deletions examples/js/shaders/AfterimageShader.js
@@ -0,0 +1,60 @@
/**
* @author HypnosNova / https://www.threejs.org.cn/gallery/
*
* Afterimage shader
* I created this effect inspired by a demo on codepen:
* https://codepen.io/brunoimbrizi/pen/MoRJaN?page=1&
*/

THREE.AfterimageShader = {

uniforms: {

"damp": { value: 0.96 },
"tOld": { value: null },
"tNew": { value: null }

},

vertexShader: [

"varying vec2 vUv;",

"void main() {",

"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

"}"

].join( "\n" ),

fragmentShader: [

"uniform float damp;",

"uniform sampler2D tOld;",
"uniform sampler2D tNew;",

"varying vec2 vUv;",

"vec4 when_gt( vec4 x, float y ) {",

"return max( sign( x - y ), 0.0 );",

"}",

"void main() {",

"vec4 texelOld = texture2D( tOld, vUv );",
"vec4 texelNew = texture2D( tNew, vUv );",

"texelOld *= damp * when_gt( texelOld, 0.1 );",

"gl_FragColor = max(texelNew, texelOld);",

"}"

].join( "\n" )

};
116 changes: 116 additions & 0 deletions examples/webgl_postprocessing_afterimage.html
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - postprocessing - afterimage</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>

<script src="../build/three.js"></script>

<script src="js/shaders/CopyShader.js"></script>
<script src="js/shaders/AfterimageShader.js"></script>

<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/AfterimagePass.js"></script>

<script src="js/libs/dat.gui.min.js" type="text/javascript" charset="utf-8"></script>

<script>

var camera, scene, renderer, composer;
var mesh, light;

var afterimagePass, enable = true;

init();
createGUI();
animate();

function init() {

renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;

scene = new THREE.Scene();
scene.fog = new THREE.Fog( 0x000000, 1, 1000 );

object = new THREE.Object3D();
scene.add( object );

var geometry = new THREE.BoxBufferGeometry( 150, 150, 150, 2, 2, 2);
var material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

// postprocessing

composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );

afterimagePass = new THREE.AfterimagePass();
afterimagePass.renderToScreen = true;
composer.addPass( afterimagePass );

window.addEventListener( 'resize', onWindowResize, false );

}

function createGUI(){

var gui = new dat.GUI( { name: 'Damp setting' } );
gui.add( afterimagePass.uniforms[ "damp" ], 'value', 0, 1 ).step( 0.001 );
gui.add( this, 'enable' );

}

function onWindowResize() {

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

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

}

function animate() {

requestAnimationFrame( animate );

var time = Date.now();

mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;

if( enable ){

composer.render();

} else {

renderer.render( scene, camera );

}

}

</script>
</body>
</html>

0 comments on commit 58f60e7

Please sign in to comment.