Browse Source

Merge branch 'gpgpu' of https://github.com/zz85/three.js into dev

Mr.doob 11 years ago
parent
commit
6421e6d4c8
2 changed files with 1022 additions and 0 deletions
  1. 131 0
      examples/js/SimulatorRenderer.js
  2. 891 0
      examples/webgl_gpgpu_birds.html

+ 131 - 0
examples/js/SimulatorRenderer.js

@@ -0,0 +1,131 @@
+/**
+ * @author zz85 https://github.com/zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Bird Simulation Render
+ *
+ * 	A simple scene rendering a quad of the following shaders
+ *	1. Pass-thru Shader,
+ *	2. Bird Position Update Shader,
+ *	3. Bird Velocity Update Shader
+ *
+ */
+
+function SimulatorRenderer(WIDTH, renderer) {
+
+	WIDTH = WIDTH || 4;
+	var camera = new THREE.Camera();
+	camera.position.z = 1;
+
+	// Init RTT stuff
+	gl = renderer.getContext();
+
+	if( !gl.getExtension( "OES_texture_float" )) {
+		alert( "No OES_texture_float support for float textures!" );
+		return;
+	}
+
+	if( gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) == 0) {
+		alert( "No support for vertex shader textures!" );
+		return;
+	}
+
+	var scene = new THREE.Scene();
+
+	var uniforms = {
+		time: { type: "f", value: 1.0 },
+		resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) },
+		texture: { type: "t", value: null }
+	};
+
+	var passThruShader = new THREE.ShaderMaterial( {
+		uniforms: uniforms,
+		vertexShader: document.getElementById( 'vertexShader' ).textContent,
+		fragmentShader: document.getElementById( 'fragmentShader' ).textContent
+	} );
+
+	var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), passThruShader );
+
+	var positionShader = new THREE.ShaderMaterial( {
+
+		uniforms: {
+			time: { type: "f", value: 1.0 },
+			delta: { type: "f", value: 0.0 },
+			resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) },
+			texturePosition: { type: "t", value: null },
+			textureVelocity: { type: "t", value: null },
+		},
+		vertexShader: document.getElementById( 'vertexShader' ).textContent,
+		fragmentShader: document.getElementById( 'fragmentShaderPosition' ).textContent
+
+	} );
+
+	this.positionShader = positionShader;
+
+	var velocityShader = new THREE.ShaderMaterial( {
+
+		uniforms: {
+			time: { type: "f", value: 1.0 },
+			delta: { type: "f", value: 0.0 },
+			resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) },
+			texturePosition: { type: "t", value: null },
+			textureVelocity: { type: "t", value: null },
+			testing: { type: "f", value: 1.0 },
+			seperationDistance: { type: "f", value: 1.0 },
+			alignmentDistance: { type: "f", value: 1.0 },
+			cohesionDistance: { type: "f", value: 1.0 },
+			freedomFactor: { type: "f", value: 1.0 },
+			predator: { type: "v3", value: new THREE.Vector3() }
+		},
+		defines: {
+			WIDTH: WIDTH.toFixed(2)
+		},
+		vertexShader: document.getElementById( 'vertexShader' ).textContent,
+		fragmentShader: document.getElementById( 'fragmentShaderVelocity' ).textContent
+
+	} );
+
+	this.velocityUniforms = velocityShader.uniforms;
+
+	scene.add( mesh );
+
+	this.getRenderTarget = function() {
+		var renderTarget = new THREE.WebGLRenderTarget(WIDTH, WIDTH, {
+			wrapS: THREE.RepeatWrapping,
+			wrapT: THREE.RepeatWrapping,
+			minFilter: THREE.NearestFilter,
+			magFilter: THREE.NearestFilter,
+			format: THREE.RGBAFormat,
+			type: THREE.FloatType,
+			stencilBuffer: false
+		});
+
+		return renderTarget;
+	}
+
+	// Takes a texture, and render out as another texture
+	this.renderTexture = function( input, output ) {
+		mesh.material = passThruShader;
+		uniforms.texture.value = input;
+		renderer.render( scene, camera, output );
+	}
+
+
+	this.renderPosition = function(position, velocity, output, delta) {
+		mesh.material = positionShader;
+		positionShader.uniforms.texturePosition.value = position;
+		positionShader.uniforms.textureVelocity.value = velocity;
+		positionShader.uniforms.time.value = performance.now();
+		positionShader.uniforms.delta.value = delta;
+		renderer.render( scene, camera, output );
+	}
+
+	this.renderVelocity = function(position, velocity, output, delta) {
+		mesh.material = velocityShader;
+		velocityShader.uniforms.texturePosition.value = position;
+		velocityShader.uniforms.textureVelocity.value = velocity;
+		velocityShader.uniforms.time.value = performance.now();
+		velocityShader.uniforms.delta.value = delta;
+		renderer.render( scene, camera, output );
+	}
+
+}

+ 891 - 0
examples/webgl_gpgpu_birds.html

@@ -0,0 +1,891 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - gpgpu - flocking</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 {
+				background-color: #ffffff;
+				margin: 0px;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				text-align:center;
+				cursor: pointer;
+			}
+
+			a {
+				color:#0078ff;
+			}
+
+			#info {
+				color: #000;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - <span id="birds"></span> webgl gpgpu birds<br/>
+			Select <span id="options"></span> birds<br/>
+			Move mouse to disturb birds.
+			
+		</div>
+
+		<script src="../build/three.min.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/SimulatorRenderer.js"></script>
+
+		<!--
+		TODO: If you're reading this, you may wish to improve this example by
+			- Replacing the custom BirdGeometry with a BufferGeometry?
+			- Create a better shading for the birds?
+			- Refactoring the SimulationRenderer to a more generic TextureRenderer / making the GPGPU workflow easier?
+
+		-->
+
+
+
+		<!-- pass through vertex shader -->
+		<script id="vertexShader" type="x-shader/x-vertex">
+
+			void main()	{
+
+				gl_Position = vec4( position, 1.0 );
+
+			}
+
+		</script>
+
+		<!-- pass through fragment shader -->
+		<script id="fragmentShader" type="x-shader/x-fragment">
+
+			uniform vec2 resolution;
+			uniform float time;
+			uniform sampler2D texture;
+
+			void main()	{
+
+				vec2 uv = gl_FragCoord.xy / resolution.xy;
+
+				vec3 color = texture2D( texture, uv ).xyz;
+
+				gl_FragColor=vec4(color, 1.0);
+
+			}
+
+		</script>
+		<!-- end pass through shaders -->
+
+		<!-- shader for bird's position -->
+		<script id="fragmentShaderPosition" type="x-shader/x-fragment">
+
+			uniform vec2 resolution;
+			uniform float time;
+			uniform float delta;
+			uniform sampler2D textureVelocity;
+			uniform sampler2D texturePosition;
+
+			void main()	{
+
+				vec2 uv = gl_FragCoord.xy / resolution.xy;
+				vec4 tmpPos = texture2D( texturePosition, uv );
+				vec3 position = tmpPos.xyz;
+				vec3 velocity = texture2D( textureVelocity, uv ).xyz;
+
+				float phase = tmpPos.w;
+
+				phase = mod( ( phase + delta +
+					length( velocity.xz ) * delta * 3. +
+					max( velocity.y, 0.0 ) * delta * 6. ), 62.83 );
+
+				gl_FragColor = vec4( position + velocity * delta * 15. , phase );
+
+			}
+
+		</script>
+
+		<!-- shader for bird's velocity -->
+		<script id="fragmentShaderVelocity" type="x-shader/x-fragment">
+
+			uniform vec2 resolution;
+			uniform float time;
+			uniform float testing;
+			uniform float delta; // about 0.016
+			uniform float seperationDistance; // 20
+			uniform float alignmentDistance; // 40
+			uniform float cohesionDistance; // 
+			uniform float freedomFactor;
+			uniform vec3 predator;
+
+
+			uniform sampler2D textureVelocity;
+			uniform sampler2D texturePosition;
+
+			const float width = WIDTH;
+			const float height = WIDTH;
+
+			const float PI = 3.141592653589793;
+			const float PI_2 = PI * 2.0;
+			// const float VISION = PI * 0.55;
+
+			float zoneRadius = 40.0;
+			float zoneRadiusSquared = zoneRadius * zoneRadius;
+
+			float separationThresh = 0.45;
+			float alignmentThresh = 0.65;
+
+			const float UPPER_BOUNDS = 400.0;
+			const float LOWER_BOUNDS = -UPPER_BOUNDS;
+
+			const float SPEED_LIMIT = 9.0;
+
+			float rand(vec2 co){
+			    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
+			}
+
+			void main()	{
+
+				zoneRadius = seperationDistance + alignmentDistance + cohesionDistance;
+				separationThresh = seperationDistance / zoneRadius;
+				alignmentThresh = ( seperationDistance + alignmentDistance ) / zoneRadius;
+				zoneRadiusSquared = zoneRadius * zoneRadius;
+
+
+				vec2 uv = gl_FragCoord.xy / resolution.xy;
+				vec3 birdPosition, birdVelocity;
+
+				vec3 selfPosition = texture2D( texturePosition, uv ).xyz;
+				vec3 selfVelocity = texture2D( textureVelocity, uv ).xyz;
+
+				float dist;
+				vec3 dir; // direction
+				float distSquared;
+
+				float seperationSquared = seperationDistance * seperationDistance;
+				float cohesionSquared = cohesionDistance * cohesionDistance;
+
+				float f;
+				float percent;
+
+				vec3 velocity = selfVelocity;
+
+				float limit = SPEED_LIMIT;
+
+				dir = predator * UPPER_BOUNDS - selfPosition;
+				dir.z = 0.;
+				// dir.z *= 0.6;
+				dist = length( dir );
+				distSquared = dist * dist;
+
+				float preyRadius = 150.0;
+				float preyRadiusSq = preyRadius * preyRadius;
+
+				
+				// move birds away from predator
+				if (dist < preyRadius) {
+
+					f = ( distSquared / preyRadiusSq - 1.0 ) * delta * 100.;
+					velocity += normalize( dir ) * f;
+					limit += 5.0;
+				}
+
+
+				// if (testing == 0.0) {}
+				// if ( rand( uv + time ) < freedomFactor ) {}
+
+
+				// Attract flocks to the center
+				vec3 central = vec3( 0., 0., 0. );
+				dir = selfPosition - central;
+				dist = length( dir );
+				dir.y *= 2.5;
+				velocity -= normalize( dir ) * delta * 5.;
+
+				for (float y=0.0;y<height;y++) {
+					for (float x=0.0;x<width;x++) {
+
+						if (
+							x == gl_FragCoord.x && y == gl_FragCoord.y) continue;
+
+						birdPosition = texture2D( texturePosition,
+							vec2( x / resolution.x,  y / resolution.y ) ).xyz;
+
+						dir = birdPosition - selfPosition;
+						dist = length(dir);
+						distSquared = dist * dist;
+
+						if ( dist > 0.0 && distSquared < zoneRadiusSquared ) {
+
+							percent = distSquared / zoneRadiusSquared;
+
+							if ( percent < separationThresh ) { // low
+
+								// Separation - Move apart for comfort
+								f = (separationThresh / percent - 1.0) * delta;
+								velocity -= normalize(dir) * f;
+
+							} else if ( percent < alignmentThresh ) { // high
+
+								// Alignment - fly the same direction
+								float threshDelta = alignmentThresh - separationThresh;
+								float adjustedPercent = ( percent - separationThresh ) / threshDelta;
+
+								birdVelocity = texture2D( textureVelocity, vec2(x/resolution.x, y/resolution.y) ).xyz;
+								
+								f = ( 0.5 - cos( adjustedPercent * PI_2 ) * 0.5 + 0.5 ) * delta;
+								velocity += normalize(birdVelocity) * f;
+
+							} else {
+
+								// Attraction / Cohesion - move closer
+								float threshDelta = 1.0 - alignmentThresh;
+								float adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
+
+								 f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) ) * delta;
+
+								 velocity += normalize(dir) * f;
+
+							}
+
+						}
+
+					}
+
+				}
+
+				
+
+				// this make tends to fly around than down or up
+				// if (velocity.y > 0.) velocity.y *= (1. - 0.2 * delta);
+
+				// Speed Limits
+				if ( length( velocity ) > limit ) {
+					velocity = normalize( velocity ) * limit;
+				}
+
+				gl_FragColor = vec4( velocity, 1.0 );
+
+			}
+
+		</script>
+
+		<script type="x-shader/x-vertex" id="birdVS">
+
+			attribute vec2 reference;
+			attribute float birdVertex;
+
+			attribute vec3 birdColor;
+			
+			uniform sampler2D texturePosition;
+			uniform sampler2D textureVelocity;
+
+			varying vec3 vNormal;
+			varying vec2 vUv;
+			varying vec4 vColor;
+			varying float z;
+
+			uniform float time;
+
+			void main() {
+
+				vNormal = normal;
+
+				vec4 tmpPos = texture2D( texturePosition, reference );
+				vec3 pos = tmpPos.xyz;
+				vec3 velocity = normalize(texture2D( textureVelocity, reference ).xyz);
+
+				vec3 newPosition = position;
+
+				if ( birdVertex == 4.0 || birdVertex == 7.0 ) {
+					// flap wings
+					newPosition.y = sin( tmpPos.w ) * 5.;
+				}
+
+				newPosition = mat3( modelMatrix ) * newPosition;
+
+
+				velocity.z *= -1.;
+				float xz = length( velocity.xz );
+				float xyz = 1.;
+				float x = sqrt( 1. - velocity.y * velocity.y );
+				
+				float cosry = velocity.x / xz;
+				float sinry = velocity.z / xz;
+
+				float cosrz = x / xyz;
+				float sinrz = velocity.y / xyz;
+
+				mat3 maty =  mat3(
+					cosry, 0, -sinry,
+					0    , 1, 0     ,
+					sinry, 0, cosry
+
+				);
+
+				mat3 matz =  mat3(
+					cosrz , sinrz, 0,
+					-sinrz, cosrz, 0,
+					0     , 0    , 1
+				);
+
+				newPosition =  maty * matz * newPosition;
+				newPosition += pos;
+
+				z = newPosition.z;
+				
+				vColor = vec4( birdColor, 1.0 );
+				gl_Position = projectionMatrix *  viewMatrix  * vec4( newPosition, 1.0 );
+			}
+
+		</script>
+
+		<!-- bird geometry shader -->
+		<script type="x-shader/x-fragment" id="birdFS">
+
+			varying vec3 vNormal;
+			varying vec2 vUv;
+			varying vec4 vColor;
+			varying float z;
+
+			uniform vec3 color;
+
+			void main() {
+				// Fake colors for now
+				float z2 = 0.2 + ( 1000. - z ) / 1000. * vColor.x;
+				gl_FragColor = vec4( z2, z2, z2, 1. );
+
+				// vec3 light = vec3( 0.0, 1.0, 1.0 );
+				// light = normalize( light );
+				// float dProd = dot( vNormal, light ) ; //* 0.5 + 0.5;
+				// vec4 tcolor = vColor;
+				// vec4 gray = vec4( vec3( tcolor.r * 0.3 + tcolor.g * 0.59 + tcolor.b * 0.11 ), 1.0 );
+
+				// gl_FragColor =  gray * dProd;
+				// gl_FragColor =  vec4( dProd  * tcolor );
+
+			}
+
+		</script>
+
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var hash = document.location.hash.substr( 1 );
+			if (hash) hash = parseInt(hash, 0);
+
+			/* TEXTURE WIDTH FOR SIMULATION */
+			var WIDTH = hash || 32; 
+
+			var BIRDS = 1024;
+
+			// Custom Geometry
+			THREE.BirdGeometry = function () {
+
+				THREE.Geometry.call( this );
+
+				BIRDS = WIDTH * WIDTH;
+
+				var verts = this.vertices;
+				var faces = this.faces;
+				var uvs = this.faceVertexUvs[ 0 ];
+
+				var fi = 0;
+
+				for (var f = 0; f<BIRDS; f++ ) {
+					verts.push(
+						new THREE.Vector3(0, -0, -20),
+						new THREE.Vector3(0, 10, -20),
+						new THREE.Vector3(0, 0, 30)
+					);
+
+					faces.push(new THREE.Face3(
+						fi++,
+						fi++,
+						fi++
+					));
+
+					uvs.push([
+						new THREE.Vector2(0, 0),
+						new THREE.Vector2(0, 1),
+						new THREE.Vector2(1, 1)
+					]);
+
+					var wingsSpan = 30;
+
+					verts.push(
+						new THREE.Vector3(0, 0, -20),
+						new THREE.Vector3(-wingsSpan, 0, 0),
+						new THREE.Vector3(0, 0, 20)
+					);
+
+					verts.push(
+						new THREE.Vector3(0, 0, 20),
+						new THREE.Vector3(wingsSpan, 0, 0),
+						new THREE.Vector3(0, 0, -20)
+					);
+
+					faces.push(new THREE.Face3(
+						fi++,
+						fi++,
+						fi++
+					));
+
+					faces.push(new THREE.Face3(
+						fi++,
+						fi++,
+						fi++
+					));
+
+					uvs.push([
+						new THREE.Vector2(0, 0),
+						new THREE.Vector2(0, 1),
+						new THREE.Vector2(1, 1)
+					]);
+
+					uvs.push([
+						new THREE.Vector2(0, 0),
+						new THREE.Vector2(0, 1),
+						new THREE.Vector2(1, 1)
+					]);
+
+				}
+
+				this.applyMatrix( new THREE.Matrix4().makeScale( 0.2, 0.2, 0.2 ) );
+
+				this.computeCentroids();
+				this.computeFaceNormals();
+				this.computeVertexNormals();
+
+			}
+
+
+			THREE.BirdGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+
+			var container, stats;
+			var camera, scene, renderer, geometry, i, h, color;
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+			
+			var HEIGHT = WIDTH;
+			var PARTICLES = WIDTH * WIDTH;
+			var BOUNDS = 800, BOUNDS_HALF = BOUNDS / 2;
+
+			document.getElementById('birds').innerText = PARTICLES;
+
+			function change(n) {
+				location.hash = n;
+				location.reload();
+				return false;
+			}
+
+
+			var options = '';
+			for (i=1; i<7; i++) {
+				var j = Math.pow(2, i);
+				options += '<a href="#" onclick="return change(' + j + ')">' + (j * j) + '</a> ';
+			}
+			document.getElementById('options').innerHTML = options;
+
+			var debug;
+			var data, texture;
+
+			var spline = new THREE.SplineCurve3();
+
+
+			var timer = 0;
+			var paused = false;
+
+			var last = performance.now();
+			var delta, now, t = 0;
+
+
+			var simulator;
+			var flipflop = true;
+			var rtPosition1, rtPosition2, rtVelocity1, rtVelocity2;
+
+
+			init();
+			animate();
+			onMouseDown();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 3000 );
+				camera.position.z = 350;
+
+				scene = new THREE.Scene();
+
+				scene.fog = new THREE.Fog( 0xffffff, 100, 1000 );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				renderer.setClearColor( scene.fog.color, 1 );
+				renderer.autoClear = true;
+
+				////////
+				simulator = new SimulatorRenderer(WIDTH, renderer);
+
+				var dtPosition = generateDataTexture();
+				var dtVelocity = generateVelocityTexture();
+
+				rtPosition1 = simulator.getRenderTarget();
+				rtPosition2 = rtPosition1.clone();
+				rtVelocity1 = rtPosition1.clone();
+				rtVelocity2 = rtPosition1.clone();
+
+				simulator.renderTexture(dtPosition, rtPosition1);
+				simulator.renderTexture(rtPosition1, rtPosition2);
+
+				simulator.renderTexture(dtVelocity, rtVelocity1);
+				simulator.renderTexture(rtVelocity1, rtVelocity2);
+
+				simulator.velocityUniforms.testing.value = 10;
+
+				/////////
+
+
+		
+				plane = new THREE.PlaneGeometry( BOUNDS, BOUNDS, 1, 1 );
+				// new THREE.CubeGeometry( BOUNDS, BOUNDS, BOUNDS),
+
+				cube = new THREE.Mesh(
+					plane,
+					new THREE.MeshBasicMaterial( {color: 0xdddddd, wireframe: true, depthWrite: false} )
+				);
+
+				cube.rotation.x = -Math.PI / 2;
+				cube.position.y = -400;
+				// scene.add(cube);
+
+
+
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				// document.addEventListener( 'mousedown', onMouseDown, false );
+				document.addEventListener( 'mouseup', onMouseUp, false );
+				document.addEventListener( 'touchstart', onDocumentTouchStart, false );
+				document.addEventListener( 'touchmove', onDocumentTouchMove, false );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+
+
+				var gui = new dat.GUI();
+
+
+				var effectController = {
+					seperation: 20.0,
+					alignment: 20.0,
+					cohesion: 20.0,
+					freedom: 0.75
+				};
+
+				var valuesChanger = function() {
+
+					simulator.velocityUniforms.seperationDistance.value = effectController.seperation;
+					simulator.velocityUniforms.alignmentDistance.value = effectController.alignment;
+					simulator.velocityUniforms.cohesionDistance.value = effectController.cohesion;
+					simulator.velocityUniforms.freedomFactor.value = effectController.freedom;
+
+				};
+
+				valuesChanger();
+
+
+				gui.add( effectController, "seperation", 0.0, 100.0, 1.0 ).onChange( valuesChanger );
+				gui.add( effectController, "alignment", 0.0, 100, 0.001 ).onChange( valuesChanger );
+				gui.add( effectController, "cohesion", 0.0, 100, 0.025 ).onChange( valuesChanger );
+				// gui.add( effectController, "freedom", 0.0, 1.0, 0.025 ).onChange( valuesChanger );
+				gui.close();
+
+				initBirds();
+
+
+				// var ambient = new THREE.AmbientLight( 0x444444 );
+				// scene.add( ambient );
+
+				// light = new THREE.DirectionalLight( 0xffffff );
+				// light.position.set( 1, 1, 1 );
+				// scene.add( light );
+
+
+				// light = new THREE.DirectionalLight( 0xffffff );
+				// light.position.set( -1, -1, -1 );
+				// scene.add( light );
+				
+
+
+			}
+
+
+			function initBirds() {
+				var geometry = new THREE.BirdGeometry( );
+
+
+				// For Vertex Shaders
+				birdAttributes = {
+					index: { type: 'i', value: [] },
+					birdColor: {	type: 'c', value: [] },
+					reference: { type: 'v2', value: [] },
+					birdVertex: { type: 'f', value: [] },
+				};
+
+				// For Vertex and Fragment
+				birdUniforms = {
+
+					color:     { type: "c", value: new THREE.Color( 0xff2200 ) },
+					texturePosition:     { type: "t", value: null },
+					textureVelocity:     { type: "t", value: null },
+					time: { type: "f", value: 1.0 },
+					delta: { type: "f", value: 0.0 },
+
+				};
+
+				// ShaderMaterial
+				var shaderMaterial = new THREE.ShaderMaterial( {
+
+					uniforms: 		birdUniforms,
+					attributes:     birdAttributes,
+					vertexShader:   document.getElementById( 'birdVS' ).textContent,
+					fragmentShader: document.getElementById( 'birdFS' ).textContent,
+					side: THREE.DoubleSide,
+					// wireframe: true
+
+				});
+
+				// geometry.dynamic = false;
+
+				var vertices = geometry.vertices;
+				var birdColors = birdAttributes.birdColor.value;
+				var references = birdAttributes.reference.value;
+				var birdVertex = birdAttributes.birdVertex.value;
+
+				for( var v = 0; v < vertices.length; v++ ) {
+
+					var i = ~~(v / 3);
+					var x = (i % WIDTH) / WIDTH;
+					var y = ~~(i / WIDTH) / WIDTH;
+
+					birdColors[ v ] = new THREE.Color(
+						Math.random() * 0xffffff
+						// ~~(v / 9) / BIRDS * 0xffffff
+					);
+					references[ v ] = new THREE.Vector2( x, y );
+					birdVertex[ v ] = v % 9;
+
+				}
+
+
+				// var 
+				birdMesh = new THREE.Mesh( geometry, shaderMaterial );
+				birdMesh.rotation.y = Math.PI / 2;
+				birdMesh.sortObjects = false;
+
+				birdMesh.matrixAutoUpdate = false;
+				birdMesh.updateMatrix();
+
+				scene.add(birdMesh);
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = event.clientX - windowHalfX;
+				mouseY = event.clientY - windowHalfY;
+
+			}
+
+			function onDocumentTouchStart( event ) {
+
+				if ( event.touches.length === 1 ) {
+
+					event.preventDefault();
+
+					mouseX = event.touches[ 0 ].pageX - windowHalfX;
+					mouseY = event.touches[ 0 ].pageY - windowHalfY;
+
+				}
+
+			}
+
+			function onDocumentTouchMove( event ) {
+
+				if ( event.touches.length === 1 ) {
+
+					event.preventDefault();
+
+					mouseX = event.touches[ 0 ].pageX - windowHalfX;
+					mouseY = event.touches[ 0 ].pageY - windowHalfY;
+
+				}
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+				now = performance.now()
+				delta = (now - last) / 1000;
+
+				if (delta > 1) delta = 1; // safety cap on large deltas
+				last = now;
+
+				birdUniforms.time.value = now;
+				birdUniforms.delta.value = delta;
+
+				// if ( paused ) {
+				// 	camera.position.x += ( mouseX * 2 - camera.position.x ) * 0.05;
+				// 	camera.position.y += ( - mouseY * 2 - camera.position.y ) * 0.05;
+
+				// 	camera.lookAt( scene.position );
+				// 	delta = 0.0001;
+				// }
+
+				if (!paused)
+				if (flipflop) {
+					simulator.renderVelocity( rtPosition1, rtVelocity1, rtVelocity2, delta );
+					simulator.renderPosition( rtPosition1, rtVelocity2, rtPosition2, delta );
+					birdUniforms.texturePosition.value = rtPosition2;
+					birdUniforms.textureVelocity.value = rtVelocity2;
+
+				} else {
+					simulator.renderVelocity( rtPosition2, rtVelocity2, rtVelocity1, delta );
+					simulator.renderPosition( rtPosition2, rtVelocity1, rtPosition1, delta );
+					birdUniforms.texturePosition.value = rtPosition1;
+					birdUniforms.textureVelocity.value = rtVelocity1;
+				}
+
+				flipflop = !flipflop;
+
+				simulator.velocityUniforms.predator.value.set( mouseX / windowHalfX, -mouseY / windowHalfY, 0 );
+
+
+				mouseX = 10000;
+				mouseY = 10000;
+
+				renderer.render( scene, camera );
+
+			}
+
+			function onMouseDown() {
+				// simulator.velocityUniforms.testing.value = 0;
+			}
+
+			function onMouseUp() {
+				// simulator.velocityUniforms.testing.value = 1;
+			}
+
+
+			function generateDataTexture() {
+
+				var x, y, z;
+
+				var w = WIDTH, h = WIDTH;
+
+				var a = new Float32Array(PARTICLES * 4);
+
+				for (var k = 0; k < PARTICLES; k++) {
+
+					x = Math.random() * BOUNDS - BOUNDS_HALF;
+					y = Math.random() * BOUNDS - BOUNDS_HALF;
+					z = Math.random() * BOUNDS - BOUNDS_HALF;
+
+					a[ k*4 + 0 ] = x;
+					a[ k*4 + 1 ] = y;
+					a[ k*4 + 2 ] = z;
+					a[ k*4 + 3 ] = 1;
+
+				}
+
+				var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBAFormat, THREE.FloatType );
+				texture.minFilter = THREE.NearestFilter;
+				texture.magFilter = THREE.NearestFilter;
+				texture.needsUpdate = true;
+				texture.flipY = false;
+
+				return texture;
+
+			}
+
+			function generateVelocityTexture() {
+
+				var x, y, z;
+
+				var w = WIDTH, h = WIDTH;
+
+				var a = new Float32Array(PARTICLES * 4);
+
+				for (var k = 0; k < PARTICLES; k++) {
+
+					x = Math.random() - 0.5;
+					y = Math.random() - 0.5;
+					z = Math.random() - 0.5;
+
+					a[ k*4 + 0 ] = x * 10;
+					a[ k*4 + 1 ] = y * 10;
+					a[ k*4 + 2 ] = z * 10;
+					a[ k*4 + 3 ] = 1;
+
+				}
+
+				var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBAFormat, THREE.FloatType );
+				texture.minFilter = THREE.NearestFilter;
+				texture.magFilter = THREE.NearestFilter;
+				texture.needsUpdate = true;
+				texture.flipY = false;
+
+				return texture;
+
+			}
+
+		</script>
+	</body>
+</html>