canvas_geometry_birds.html 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js canvas - geometry - birds</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. color: #808080;
  10. font-family:Monospace;
  11. font-size:13px;
  12. text-align:center;
  13. background-color: #ffffff;
  14. margin: 0px;
  15. overflow: hidden;
  16. }
  17. #info {
  18. position: absolute;
  19. top: 0px; width: 100%;
  20. padding: 5px;
  21. }
  22. </style>
  23. </head>
  24. <body>
  25. <div id="container"></div>
  26. <div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - birds demo</div>
  27. <script src="../build/three.js"></script>
  28. <script src="js/renderers/Projector.js"></script>
  29. <script src="js/renderers/CanvasRenderer.js"></script>
  30. <script src="js/libs/stats.min.js"></script>
  31. <script>
  32. var Bird = function () {
  33. var scope = this;
  34. THREE.Geometry.call( this );
  35. v( 5, 0, 0 );
  36. v( - 5, - 2, 1 );
  37. v( - 5, 0, 0 );
  38. v( - 5, - 2, - 1 );
  39. v( 0, 2, - 6 );
  40. v( 0, 2, 6 );
  41. v( 2, 0, 0 );
  42. v( - 3, 0, 0 );
  43. f3( 0, 2, 1 );
  44. f3( 4, 7, 6 );
  45. f3( 5, 6, 7 );
  46. this.computeFaceNormals();
  47. function v( x, y, z ) {
  48. scope.vertices.push( new THREE.Vector3( x, y, z ) );
  49. }
  50. function f3( a, b, c ) {
  51. scope.faces.push( new THREE.Face3( a, b, c ) );
  52. }
  53. };
  54. Bird.prototype = Object.create( THREE.Geometry.prototype );
  55. Bird.prototype.constructor = Bird;
  56. // Based on https://www.openprocessing.org/sketch/6910
  57. var Boid = function () {
  58. var vector = new THREE.Vector3(),
  59. _acceleration, _width = 500, _height = 500, _depth = 200, _goal, _neighborhoodRadius = 100,
  60. _maxSpeed = 4, _maxSteerForce = 0.1, _avoidWalls = false;
  61. this.position = new THREE.Vector3();
  62. this.velocity = new THREE.Vector3();
  63. _acceleration = new THREE.Vector3();
  64. this.setGoal = function ( target ) {
  65. _goal = target;
  66. };
  67. this.setAvoidWalls = function ( value ) {
  68. _avoidWalls = value;
  69. };
  70. this.setWorldSize = function ( width, height, depth ) {
  71. _width = width;
  72. _height = height;
  73. _depth = depth;
  74. };
  75. this.run = function ( boids ) {
  76. if ( _avoidWalls ) {
  77. vector.set( - _width, this.position.y, this.position.z );
  78. vector = this.avoid( vector );
  79. vector.multiplyScalar( 5 );
  80. _acceleration.add( vector );
  81. vector.set( _width, this.position.y, this.position.z );
  82. vector = this.avoid( vector );
  83. vector.multiplyScalar( 5 );
  84. _acceleration.add( vector );
  85. vector.set( this.position.x, - _height, this.position.z );
  86. vector = this.avoid( vector );
  87. vector.multiplyScalar( 5 );
  88. _acceleration.add( vector );
  89. vector.set( this.position.x, _height, this.position.z );
  90. vector = this.avoid( vector );
  91. vector.multiplyScalar( 5 );
  92. _acceleration.add( vector );
  93. vector.set( this.position.x, this.position.y, - _depth );
  94. vector = this.avoid( vector );
  95. vector.multiplyScalar( 5 );
  96. _acceleration.add( vector );
  97. vector.set( this.position.x, this.position.y, _depth );
  98. vector = this.avoid( vector );
  99. vector.multiplyScalar( 5 );
  100. _acceleration.add( vector );
  101. }/* else {
  102. this.checkBounds();
  103. }
  104. */
  105. if ( Math.random() > 0.5 ) {
  106. this.flock( boids );
  107. }
  108. this.move();
  109. };
  110. this.flock = function ( boids ) {
  111. if ( _goal ) {
  112. _acceleration.add( this.reach( _goal, 0.005 ) );
  113. }
  114. _acceleration.add( this.alignment( boids ) );
  115. _acceleration.add( this.cohesion( boids ) );
  116. _acceleration.add( this.separation( boids ) );
  117. };
  118. this.move = function () {
  119. this.velocity.add( _acceleration );
  120. var l = this.velocity.length();
  121. if ( l > _maxSpeed ) {
  122. this.velocity.divideScalar( l / _maxSpeed );
  123. }
  124. this.position.add( this.velocity );
  125. _acceleration.set( 0, 0, 0 );
  126. };
  127. this.checkBounds = function () {
  128. if ( this.position.x > _width ) this.position.x = - _width;
  129. if ( this.position.x < - _width ) this.position.x = _width;
  130. if ( this.position.y > _height ) this.position.y = - _height;
  131. if ( this.position.y < - _height ) this.position.y = _height;
  132. if ( this.position.z > _depth ) this.position.z = - _depth;
  133. if ( this.position.z < - _depth ) this.position.z = _depth;
  134. };
  135. //
  136. this.avoid = function ( target ) {
  137. var steer = new THREE.Vector3();
  138. steer.copy( this.position );
  139. steer.sub( target );
  140. steer.multiplyScalar( 1 / this.position.distanceToSquared( target ) );
  141. return steer;
  142. };
  143. this.repulse = function ( target ) {
  144. var distance = this.position.distanceTo( target );
  145. if ( distance < 150 ) {
  146. var steer = new THREE.Vector3();
  147. steer.subVectors( this.position, target );
  148. steer.multiplyScalar( 0.5 / distance );
  149. _acceleration.add( steer );
  150. }
  151. };
  152. this.reach = function ( target, amount ) {
  153. var steer = new THREE.Vector3();
  154. steer.subVectors( target, this.position );
  155. steer.multiplyScalar( amount );
  156. return steer;
  157. };
  158. this.alignment = function ( boids ) {
  159. var count = 0;
  160. var velSum = new THREE.Vector3();
  161. for ( var i = 0, il = boids.length; i < il; i ++ ) {
  162. if ( Math.random() > 0.6 ) continue;
  163. var boid = boids[ i ];
  164. var distance = boid.position.distanceTo( this.position );
  165. if ( distance > 0 && distance <= _neighborhoodRadius ) {
  166. velSum.add( boid.velocity );
  167. count ++;
  168. }
  169. }
  170. if ( count > 0 ) {
  171. velSum.divideScalar( count );
  172. var l = velSum.length();
  173. if ( l > _maxSteerForce ) {
  174. velSum.divideScalar( l / _maxSteerForce );
  175. }
  176. }
  177. return velSum;
  178. };
  179. this.cohesion = function ( boids ) {
  180. var count = 0;
  181. var posSum = new THREE.Vector3();
  182. var steer = new THREE.Vector3();
  183. for ( var i = 0, il = boids.length; i < il; i ++ ) {
  184. if ( Math.random() > 0.6 ) continue;
  185. var boid = boids[ i ];
  186. var distance = boid.position.distanceTo( this.position );
  187. if ( distance > 0 && distance <= _neighborhoodRadius ) {
  188. posSum.add( boid.position );
  189. count ++;
  190. }
  191. }
  192. if ( count > 0 ) {
  193. posSum.divideScalar( count );
  194. }
  195. steer.subVectors( posSum, this.position );
  196. var l = steer.length();
  197. if ( l > _maxSteerForce ) {
  198. steer.divideScalar( l / _maxSteerForce );
  199. }
  200. return steer;
  201. };
  202. this.separation = function ( boids ) {
  203. var posSum = new THREE.Vector3();
  204. var repulse = new THREE.Vector3();
  205. for ( var i = 0, il = boids.length; i < il; i ++ ) {
  206. if ( Math.random() > 0.6 ) continue;
  207. var boid = boids[ i ];
  208. var distance = boid.position.distanceTo( this.position );
  209. if ( distance > 0 && distance <= _neighborhoodRadius ) {
  210. repulse.subVectors( this.position, boid.position );
  211. repulse.normalize();
  212. repulse.divideScalar( distance );
  213. posSum.add( repulse );
  214. }
  215. }
  216. return posSum;
  217. };
  218. };
  219. var SCREEN_WIDTH = window.innerWidth,
  220. SCREEN_HEIGHT = window.innerHeight,
  221. SCREEN_WIDTH_HALF = SCREEN_WIDTH / 2,
  222. SCREEN_HEIGHT_HALF = SCREEN_HEIGHT / 2;
  223. var camera, scene, renderer, birds, bird;
  224. var boid, boids;
  225. var stats;
  226. init();
  227. animate();
  228. function init() {
  229. camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
  230. camera.position.z = 450;
  231. scene = new THREE.Scene();
  232. scene.background = new THREE.Color( 0xffffff );
  233. birds = [];
  234. boids = [];
  235. for ( var i = 0; i < 200; i ++ ) {
  236. boid = boids[ i ] = new Boid();
  237. boid.position.x = Math.random() * 400 - 200;
  238. boid.position.y = Math.random() * 400 - 200;
  239. boid.position.z = Math.random() * 400 - 200;
  240. boid.velocity.x = Math.random() * 2 - 1;
  241. boid.velocity.y = Math.random() * 2 - 1;
  242. boid.velocity.z = Math.random() * 2 - 1;
  243. boid.setAvoidWalls( true );
  244. boid.setWorldSize( 500, 500, 400 );
  245. bird = birds[ i ] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, side: THREE.DoubleSide } ) );
  246. bird.phase = Math.floor( Math.random() * 62.83 );
  247. scene.add( bird );
  248. }
  249. renderer = new THREE.CanvasRenderer();
  250. renderer.setPixelRatio( window.devicePixelRatio );
  251. renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
  252. document.addEventListener( 'mousemove', onDocumentMouseMove, false );
  253. document.body.appendChild( renderer.domElement );
  254. stats = new Stats();
  255. document.getElementById( 'container' ).appendChild( stats.dom );
  256. //
  257. window.addEventListener( 'resize', onWindowResize, false );
  258. }
  259. function onWindowResize() {
  260. camera.aspect = window.innerWidth / window.innerHeight;
  261. camera.updateProjectionMatrix();
  262. renderer.setSize( window.innerWidth, window.innerHeight );
  263. }
  264. function onDocumentMouseMove( event ) {
  265. var vector = new THREE.Vector3( event.clientX - SCREEN_WIDTH_HALF, - event.clientY + SCREEN_HEIGHT_HALF, 0 );
  266. for ( var i = 0, il = boids.length; i < il; i ++ ) {
  267. boid = boids[ i ];
  268. vector.z = boid.position.z;
  269. boid.repulse( vector );
  270. }
  271. }
  272. //
  273. function animate() {
  274. requestAnimationFrame( animate );
  275. stats.begin();
  276. render();
  277. stats.end();
  278. }
  279. function render() {
  280. for ( var i = 0, il = birds.length; i < il; i ++ ) {
  281. boid = boids[ i ];
  282. boid.run( boids );
  283. bird = birds[ i ];
  284. bird.position.copy( boids[ i ].position );
  285. var color = bird.material.color;
  286. color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000;
  287. bird.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x );
  288. bird.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() );
  289. bird.phase = ( bird.phase + ( Math.max( 0, bird.rotation.z ) + 0.1 ) ) % 62.83;
  290. bird.geometry.vertices[ 5 ].y = bird.geometry.vertices[ 4 ].y = Math.sin( bird.phase ) * 5;
  291. }
  292. renderer.render( scene, camera );
  293. }
  294. </script>
  295. </body>
  296. </html>