Skip to content

Commit 58f60e7

Browse files
authored
Merge pull request #13917 from HypnosNova/afterimage-effect
add afterimage effect
2 parents 251ebc8 + 972faec commit 58f60e7

File tree

4 files changed

+265
-0
lines changed

4 files changed

+265
-0
lines changed

examples/files.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ var files = {
222222
"webgl_points_sprites",
223223
"webgl_postprocessing",
224224
"webgl_postprocessing_advanced",
225+
"webgl_postprocessing_afterimage",
225226
"webgl_postprocessing_backgrounds",
226227
"webgl_postprocessing_crossfade",
227228
"webgl_postprocessing_dof",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @author HypnosNova / https://www.threejs.org.cn/gallery/
3+
*/
4+
5+
THREE.AfterimagePass = function ( damp ) {
6+
7+
THREE.Pass.call( this );
8+
9+
if ( THREE.AfterimageShader === undefined )
10+
console.error( "THREE.AfterimagePass relies on THREE.AfterimageShader" );
11+
12+
this.shader = THREE.AfterimageShader;
13+
14+
this.uniforms = THREE.UniformsUtils.clone( this.shader.uniforms );
15+
16+
this.uniforms[ "damp" ].value = damp !== undefined ? damp : 0.96;
17+
18+
this.textureComp = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {
19+
20+
minFilter: THREE.LinearFilter,
21+
magFilter: THREE.NearestFilter,
22+
format: THREE.RGBAFormat
23+
24+
} );
25+
26+
this.textureOld = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {
27+
28+
minFilter: THREE.LinearFilter,
29+
magFilter: THREE.NearestFilter,
30+
format: THREE.RGBAFormat
31+
32+
} );
33+
34+
this.shaderMaterial = new THREE.ShaderMaterial( {
35+
36+
uniforms: this.uniforms,
37+
vertexShader: this.shader.vertexShader,
38+
fragmentShader: this.shader.fragmentShader
39+
40+
} );
41+
42+
this.sceneComp = new THREE.Scene();
43+
this.scene = new THREE.Scene();
44+
45+
this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
46+
this.camera.position.z = 1;
47+
48+
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
49+
50+
this.quadComp = new THREE.Mesh( geometry, this.shaderMaterial );
51+
this.sceneComp.add( this.quadComp );
52+
53+
var material = new THREE.MeshBasicMaterial( {
54+
map: this.textureComp.texture
55+
} );
56+
57+
var quadScreen = new THREE.Mesh( geometry, material );
58+
this.scene.add( quadScreen );
59+
60+
};
61+
62+
THREE.AfterimagePass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
63+
64+
constructor: THREE.AfterimagePass,
65+
66+
render: function ( renderer, writeBuffer, readBuffer ) {
67+
68+
this.uniforms[ "tOld" ].value = this.textureOld.texture;
69+
this.uniforms[ "tNew" ].value = readBuffer.texture;
70+
71+
this.quadComp.material = this.shaderMaterial;
72+
73+
renderer.render( this.sceneComp, this.camera, this.textureComp );
74+
renderer.render( this.scene, this.camera, this.textureOld );
75+
76+
if ( this.renderToScreen ) {
77+
78+
renderer.render( this.scene, this.camera );
79+
80+
} else {
81+
82+
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
83+
84+
}
85+
86+
}
87+
88+
} );
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @author HypnosNova / https://www.threejs.org.cn/gallery/
3+
*
4+
* Afterimage shader
5+
* I created this effect inspired by a demo on codepen:
6+
* https://codepen.io/brunoimbrizi/pen/MoRJaN?page=1&
7+
*/
8+
9+
THREE.AfterimageShader = {
10+
11+
uniforms: {
12+
13+
"damp": { value: 0.96 },
14+
"tOld": { value: null },
15+
"tNew": { value: null }
16+
17+
},
18+
19+
vertexShader: [
20+
21+
"varying vec2 vUv;",
22+
23+
"void main() {",
24+
25+
"vUv = uv;",
26+
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
27+
28+
"}"
29+
30+
].join( "\n" ),
31+
32+
fragmentShader: [
33+
34+
"uniform float damp;",
35+
36+
"uniform sampler2D tOld;",
37+
"uniform sampler2D tNew;",
38+
39+
"varying vec2 vUv;",
40+
41+
"vec4 when_gt( vec4 x, float y ) {",
42+
43+
"return max( sign( x - y ), 0.0 );",
44+
45+
"}",
46+
47+
"void main() {",
48+
49+
"vec4 texelOld = texture2D( tOld, vUv );",
50+
"vec4 texelNew = texture2D( tNew, vUv );",
51+
52+
"texelOld *= damp * when_gt( texelOld, 0.1 );",
53+
54+
"gl_FragColor = max(texelNew, texelOld);",
55+
56+
"}"
57+
58+
].join( "\n" )
59+
60+
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgl - postprocessing - afterimage</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<style>
8+
body {
9+
margin: 0px;
10+
background-color: #000000;
11+
overflow: hidden;
12+
}
13+
</style>
14+
</head>
15+
<body>
16+
17+
<script src="../build/three.js"></script>
18+
19+
<script src="js/shaders/CopyShader.js"></script>
20+
<script src="js/shaders/AfterimageShader.js"></script>
21+
22+
<script src="js/postprocessing/EffectComposer.js"></script>
23+
<script src="js/postprocessing/RenderPass.js"></script>
24+
<script src="js/postprocessing/MaskPass.js"></script>
25+
<script src="js/postprocessing/ShaderPass.js"></script>
26+
<script src="js/postprocessing/AfterimagePass.js"></script>
27+
28+
<script src="js/libs/dat.gui.min.js" type="text/javascript" charset="utf-8"></script>
29+
30+
<script>
31+
32+
var camera, scene, renderer, composer;
33+
var mesh, light;
34+
35+
var afterimagePass, enable = true;
36+
37+
init();
38+
createGUI();
39+
animate();
40+
41+
function init() {
42+
43+
renderer = new THREE.WebGLRenderer();
44+
renderer.setPixelRatio( window.devicePixelRatio );
45+
renderer.setSize( window.innerWidth, window.innerHeight );
46+
document.body.appendChild( renderer.domElement );
47+
48+
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
49+
camera.position.z = 400;
50+
51+
scene = new THREE.Scene();
52+
scene.fog = new THREE.Fog( 0x000000, 1, 1000 );
53+
54+
object = new THREE.Object3D();
55+
scene.add( object );
56+
57+
var geometry = new THREE.BoxBufferGeometry( 150, 150, 150, 2, 2, 2);
58+
var material = new THREE.MeshNormalMaterial();
59+
mesh = new THREE.Mesh( geometry, material );
60+
scene.add( mesh );
61+
62+
// postprocessing
63+
64+
composer = new THREE.EffectComposer( renderer );
65+
composer.addPass( new THREE.RenderPass( scene, camera ) );
66+
67+
afterimagePass = new THREE.AfterimagePass();
68+
afterimagePass.renderToScreen = true;
69+
composer.addPass( afterimagePass );
70+
71+
window.addEventListener( 'resize', onWindowResize, false );
72+
73+
}
74+
75+
function createGUI(){
76+
77+
var gui = new dat.GUI( { name: 'Damp setting' } );
78+
gui.add( afterimagePass.uniforms[ "damp" ], 'value', 0, 1 ).step( 0.001 );
79+
gui.add( this, 'enable' );
80+
81+
}
82+
83+
function onWindowResize() {
84+
85+
camera.aspect = window.innerWidth / window.innerHeight;
86+
camera.updateProjectionMatrix();
87+
88+
renderer.setSize( window.innerWidth, window.innerHeight );
89+
composer.setSize( window.innerWidth, window.innerHeight );
90+
91+
}
92+
93+
function animate() {
94+
95+
requestAnimationFrame( animate );
96+
97+
var time = Date.now();
98+
99+
mesh.rotation.x += 0.005;
100+
mesh.rotation.y += 0.01;
101+
102+
if( enable ){
103+
104+
composer.render();
105+
106+
} else {
107+
108+
renderer.render( scene, camera );
109+
110+
}
111+
112+
}
113+
114+
</script>
115+
</body>
116+
</html>

0 commit comments

Comments
 (0)