webgl_instancing_interactive.html 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - instancing - interactive</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. <link type="text/css" rel="stylesheet" href="main.css">
  8. <style>
  9. #info {
  10. background-color: rgba(0,0,0,0.75);
  11. }
  12. #notSupported {
  13. width: 50%;
  14. margin: auto;
  15. border: 2px red solid;
  16. margin-top: 20px;
  17. padding: 10px;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div id="info">
  23. <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - gpu picking of geometry instances
  24. <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
  25. <br/><br/>
  26. <div>
  27. <div>
  28. number of instances:
  29. <select id="instanceCount">
  30. <option>100</option>
  31. <option>500</option>
  32. <option selected>1000</option>
  33. <option>2000</option>
  34. <option>3000</option>
  35. <option>5000</option>
  36. <option>10000</option>
  37. <option>20000</option>
  38. <option>30000</option>
  39. <option>50000</option>
  40. <option>100000</option>
  41. </select>
  42. </div>
  43. <div>
  44. method:
  45. <select id="method">
  46. <option>instanced</option>
  47. <option>merged</option>
  48. <option selected>singleMaterial</option>
  49. <option>multiMaterial</option>
  50. </select>
  51. </div>
  52. <div>
  53. render continuously:
  54. <input id="animate" type="checkbox" />
  55. </div>
  56. <div>
  57. override material (only affects singleMaterial):
  58. <input id="override" type="checkbox" checked/>
  59. </div>
  60. <div>
  61. construct anew(to get additional timings):
  62. <button id="construct" type="button">do it</button>
  63. </div>
  64. </div>
  65. <br/>
  66. <div>
  67. <span>Materials: #<span id="materialCount"></span></span>
  68. &nbsp;&nbsp;&nbsp;
  69. <span>Objects: #<span id="objectCount"></span></span>
  70. &nbsp;&nbsp;&nbsp;
  71. <span>Drawcalls: #<span id="drawcalls"></span></span>
  72. &nbsp;&nbsp;&nbsp;
  73. <span>Construction time: <span id="initTime"></span>&nbsp;ms</span>
  74. &nbsp;&nbsp;&nbsp;
  75. </div>
  76. </div>
  77. <div id="container"></div>
  78. <script id="vertMerged" type="x-shader/x-vertex">
  79. #define SHADER_NAME vertMerged
  80. precision highp float;
  81. uniform mat4 modelViewMatrix;
  82. uniform mat4 projectionMatrix;
  83. attribute vec3 position;
  84. #ifdef PICKING
  85. attribute vec3 pickingColor;
  86. #else
  87. attribute vec3 color;
  88. varying vec3 vPosition;
  89. #endif
  90. varying vec3 vColor;
  91. void main() {
  92. vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
  93. #ifdef PICKING
  94. vColor = pickingColor;
  95. #else
  96. vColor = color;
  97. vPosition = positionEye;
  98. #endif
  99. gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
  100. }
  101. </script>
  102. <script id="fragMerged" type="x-shader/x-fragment">
  103. #define SHADER_NAME fragMerged
  104. #extension GL_OES_standard_derivatives : enable
  105. precision highp float;
  106. varying vec3 vColor;
  107. #ifndef PICKING
  108. varying vec3 vPosition;
  109. #endif
  110. void main() {
  111. #ifdef PICKING
  112. gl_FragColor = vec4( vColor, 1.0 );
  113. #else
  114. vec3 fdx = dFdx( vPosition );
  115. vec3 fdy = dFdy( vPosition );
  116. vec3 normal = normalize( cross( fdx, fdy ) );
  117. float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
  118. gl_FragColor = vec4( diffuse * vColor, 1.0 );
  119. #endif
  120. }
  121. </script>
  122. <script id="vertInstanced" type="x-shader/x-vertex">
  123. #define SHADER_NAME vertInstanced
  124. precision highp float;
  125. uniform mat4 modelViewMatrix;
  126. uniform mat4 projectionMatrix;
  127. attribute vec3 position;
  128. attribute vec3 mcol0;
  129. attribute vec3 mcol1;
  130. attribute vec3 mcol2;
  131. attribute vec3 mcol3;
  132. #ifdef PICKING
  133. attribute vec3 pickingColor;
  134. #else
  135. attribute vec3 color;
  136. varying vec3 vPosition;
  137. #endif
  138. varying vec3 vColor;
  139. void main() {
  140. mat4 matrix = mat4(
  141. vec4( mcol0, 0 ),
  142. vec4( mcol1, 0 ),
  143. vec4( mcol2, 0 ),
  144. vec4( mcol3, 1 )
  145. );
  146. vec3 positionEye = ( modelViewMatrix * matrix * vec4( position, 1.0 ) ).xyz;
  147. #ifdef PICKING
  148. vColor = pickingColor;
  149. #else
  150. vColor = color;
  151. vPosition = positionEye;
  152. #endif
  153. gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
  154. }
  155. </script>
  156. <script id="fragInstanced" type="x-shader/x-fragment">
  157. #define SHADER_NAME fragInstanced
  158. #extension GL_OES_standard_derivatives : enable
  159. precision highp float;
  160. varying vec3 vColor;
  161. #ifndef PICKING
  162. varying vec3 vPosition;
  163. #endif
  164. void main() {
  165. #ifdef PICKING
  166. gl_FragColor = vec4( vColor, 1.0 );
  167. #else
  168. vec3 fdx = dFdx( vPosition );
  169. vec3 fdy = dFdy( vPosition );
  170. vec3 normal = normalize( cross( fdx, fdy ) );
  171. float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
  172. gl_FragColor = vec4( diffuse * vColor, 1.0 );
  173. #endif
  174. }
  175. </script>
  176. <script id="vertMaterial" type="x-shader/x-vertex">
  177. #define SHADER_NAME vertMaterial
  178. precision highp float;
  179. uniform mat4 modelViewMatrix;
  180. uniform mat4 projectionMatrix;
  181. attribute vec3 position;
  182. #ifndef PICKING
  183. varying vec3 vPosition;
  184. #endif
  185. void main() {
  186. vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
  187. #ifndef PICKING
  188. vPosition = positionEye;
  189. #endif
  190. gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
  191. }
  192. </script>
  193. <script id="fragMaterial" type="x-shader/x-fragment">
  194. #define SHADER_NAME fragMaterial
  195. #extension GL_OES_standard_derivatives : enable
  196. precision highp float;
  197. #ifdef PICKING
  198. uniform vec3 pickingColor;
  199. #else
  200. uniform vec3 color;
  201. varying vec3 vPosition;
  202. #endif
  203. void main() {
  204. #ifdef PICKING
  205. gl_FragColor = vec4( pickingColor, 1.0 );
  206. #else
  207. vec3 fdx = dFdx( vPosition );
  208. vec3 fdy = dFdy( vPosition );
  209. vec3 normal = normalize( cross( fdx, fdy ) );
  210. float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
  211. gl_FragColor = vec4( diffuse * color, 1.0 );
  212. #endif
  213. }
  214. </script>
  215. <script type="module">
  216. import * as THREE from '../build/three.module.js';
  217. import Stats from './jsm/libs/stats.module.js';
  218. import { TrackballControls } from './jsm/controls/TrackballControls.js';
  219. var container, stats;
  220. var camera, controls, scene, renderer;
  221. var pickingData, pickingRenderTarget, pickingScene;
  222. var useOverrideMaterial = true;
  223. var singleMaterial, singlePickingMaterial;
  224. var highlightBox;
  225. var materialList = [];
  226. var geometryList = [];
  227. var objectCount = 0;
  228. var geometrySize = new THREE.Vector3();
  229. var mouse = new THREE.Vector2();
  230. var scale = 1.03;
  231. var loader = new THREE.BufferGeometryLoader();
  232. //create buffer for reading a single pixel
  233. var pixelBuffer = new Uint8Array( 4 );
  234. // gui
  235. var instanceCount, method, doAnimate;
  236. //
  237. gui();
  238. init();
  239. initMesh();
  240. if ( doAnimate ) animate();
  241. //
  242. function gui() {
  243. var instanceCountElm = document.getElementById( 'instanceCount' );
  244. instanceCount = parseInt( instanceCountElm.value );
  245. instanceCountElm.addEventListener( "change", function () {
  246. instanceCount = parseInt( instanceCountElm.value );
  247. initMesh();
  248. } );
  249. //
  250. var methodElm = document.getElementById( 'method' );
  251. method = methodElm.value;
  252. methodElm.addEventListener( "change", function () {
  253. method = methodElm.value;
  254. initMesh();
  255. } );
  256. //
  257. var animateElm = document.getElementById( 'animate' );
  258. doAnimate = animateElm.checked;
  259. animateElm.addEventListener( "click", function () {
  260. doAnimate = animateElm.checked;
  261. animate();
  262. } );
  263. //
  264. var overrideElm = document.getElementById( 'override' );
  265. useOverrideMaterial = overrideElm.checked;
  266. overrideElm.addEventListener( "click", function () {
  267. useOverrideMaterial = overrideElm.checked;
  268. initMesh();
  269. } );
  270. //
  271. var constructElm = document.getElementById( 'construct' );
  272. constructElm.addEventListener( "click", function () {
  273. initMesh();
  274. } );
  275. }
  276. function clean() {
  277. THREE.Cache.clear();
  278. materialList.forEach( function ( m ) {
  279. m.dispose();
  280. } );
  281. geometryList.forEach( function ( g ) {
  282. g.dispose();
  283. } );
  284. scene = new THREE.Scene();
  285. scene.background = new THREE.Color( 0xffffff );
  286. scene.add( camera );
  287. scene.add( highlightBox );
  288. pickingScene = new THREE.Scene();
  289. pickingData = {};
  290. materialList = [];
  291. geometryList = [];
  292. objectCount = 0;
  293. singleMaterial = undefined;
  294. singlePickingMaterial = undefined;
  295. }
  296. var randomizeMatrix = function () {
  297. var position = new THREE.Vector3();
  298. var rotation = new THREE.Euler();
  299. var quaternion = new THREE.Quaternion();
  300. var scale = new THREE.Vector3();
  301. return function ( matrix ) {
  302. position.x = Math.random() * 40 - 20;
  303. position.y = Math.random() * 40 - 20;
  304. position.z = Math.random() * 40 - 20;
  305. rotation.x = Math.random() * 2 * Math.PI;
  306. rotation.y = Math.random() * 2 * Math.PI;
  307. rotation.z = Math.random() * 2 * Math.PI;
  308. quaternion.setFromEuler( rotation );
  309. scale.x = scale.y = scale.z = Math.random() * 1;
  310. matrix.compose( position, quaternion, scale );
  311. };
  312. }();
  313. function initMesh() {
  314. clean();
  315. // make instances
  316. loader.load( 'models/json/suzanne_buffergeometry.json', function ( geo ) {
  317. geo = geo.toNonIndexed();
  318. geo.computeBoundingBox();
  319. geo.boundingBox.getSize( geometrySize );
  320. geometryList.push( geo );
  321. var start = window.performance.now();
  322. switch ( method ) {
  323. case "merged":
  324. makeMerged( geo );
  325. break;
  326. case "instanced":
  327. makeInstanced( geo );
  328. break;
  329. case "singleMaterial":
  330. makeSingleMaterial( geo );
  331. break;
  332. case "multiMaterial":
  333. makeMultiMaterial( geo );
  334. break;
  335. }
  336. render();
  337. var end = window.performance.now();
  338. document.getElementById( 'materialCount' ).innerText = materialList.length;
  339. document.getElementById( 'objectCount' ).innerText = objectCount;
  340. document.getElementById( 'drawcalls' ).innerText = renderer.info.render.calls;
  341. document.getElementById( 'initTime' ).innerText = ( end - start ).toFixed( 2 );
  342. } );
  343. }
  344. function makeMultiMaterial( geo ) {
  345. // material
  346. var vert = document.getElementById( 'vertMaterial' ).textContent;
  347. var frag = document.getElementById( 'fragMaterial' ).textContent;
  348. var material = new THREE.RawShaderMaterial( {
  349. vertexShader: vert,
  350. fragmentShader: frag,
  351. uniforms: {
  352. color: {
  353. value: new THREE.Color()
  354. }
  355. }
  356. } );
  357. var pickingMaterial = new THREE.RawShaderMaterial( {
  358. vertexShader: "#define PICKING\n" + vert,
  359. fragmentShader: "#define PICKING\n" + frag,
  360. uniforms: {
  361. pickingColor: {
  362. value: new THREE.Color()
  363. }
  364. }
  365. } );
  366. // geometry / mesh
  367. var matrix = new THREE.Matrix4();
  368. for ( var i = 0; i < instanceCount; i ++ ) {
  369. var object = new THREE.Mesh( geo, material );
  370. objectCount ++;
  371. randomizeMatrix( matrix );
  372. object.applyMatrix( matrix );
  373. var pickingObject = object.clone();
  374. objectCount ++;
  375. object.material = material.clone();
  376. object.material.uniforms[ "color" ].value.setHex( Math.random() * 0xffffff );
  377. materialList.push( object.material );
  378. pickingObject.material = pickingMaterial.clone();
  379. pickingObject.material.uniforms[ "pickingColor" ].value.setHex( i + 1 );
  380. materialList.push( pickingObject.material );
  381. pickingData[ i + 1 ] = object;
  382. scene.add( object );
  383. pickingScene.add( pickingObject );
  384. }
  385. material.dispose();
  386. pickingMaterial.dispose();
  387. }
  388. function makeSingleMaterial( geo ) {
  389. // material
  390. var vert = document.getElementById( 'vertMaterial' ).textContent;
  391. var frag = document.getElementById( 'fragMaterial' ).textContent;
  392. var material = new THREE.RawShaderMaterial( {
  393. vertexShader: vert,
  394. fragmentShader: frag,
  395. uniforms: {
  396. "color": {
  397. value: new THREE.Color()
  398. }
  399. }
  400. } );
  401. materialList.push( material );
  402. var pickingMaterial = new THREE.RawShaderMaterial( {
  403. vertexShader: "#define PICKING\n" + vert,
  404. fragmentShader: "#define PICKING\n" + frag,
  405. uniforms: {
  406. "pickingColor": {
  407. value: new THREE.Color()
  408. }
  409. }
  410. } );
  411. materialList.push( pickingMaterial );
  412. if ( useOverrideMaterial ) {
  413. // make globally available
  414. singleMaterial = material;
  415. singlePickingMaterial = pickingMaterial;
  416. }
  417. // geometry / mesh
  418. var matrix = new THREE.Matrix4();
  419. function onBeforeRender( renderer, scene, camera, geometry, material ) {
  420. var updateList = [];
  421. var u = material.uniforms;
  422. var d = this.userData;
  423. if ( u.pickingColor ) {
  424. u.pickingColor.value.setHex( d.pickingColor );
  425. updateList.push( "pickingColor" );
  426. }
  427. if ( u.color ) {
  428. u.color.value.setHex( d.color );
  429. updateList.push( "color" );
  430. }
  431. if ( updateList.length ) {
  432. var materialProperties = renderer.properties.get( material );
  433. if ( materialProperties.program ) {
  434. var gl = renderer.getContext();
  435. var p = materialProperties.program;
  436. gl.useProgram( p.program );
  437. var pu = p.getUniforms();
  438. updateList.forEach( function ( name ) {
  439. pu.setValue( gl, name, u[ name ].value );
  440. } );
  441. }
  442. }
  443. }
  444. for ( var i = 0; i < instanceCount; i ++ ) {
  445. var object = new THREE.Mesh( geo, material );
  446. objectCount ++;
  447. randomizeMatrix( matrix );
  448. object.applyMatrix( matrix );
  449. var pickingObject;
  450. if ( ! useOverrideMaterial ) {
  451. pickingObject = object.clone();
  452. objectCount ++;
  453. }
  454. object.material = material;
  455. object.userData[ "color" ] = Math.random() * 0xffffff;
  456. if ( useOverrideMaterial ) {
  457. object.userData[ "pickingColor" ] = i + 1;
  458. object.onBeforeRender = onBeforeRender;
  459. } else {
  460. pickingObject.material = pickingMaterial;
  461. pickingObject.userData[ "pickingColor" ] = i + 1;
  462. pickingObject.onBeforeRender = onBeforeRender;
  463. }
  464. pickingData[ i + 1 ] = object;
  465. scene.add( object );
  466. if ( ! useOverrideMaterial ) pickingScene.add( pickingObject );
  467. }
  468. }
  469. function makeMerged( geo ) {
  470. // material
  471. var vert = document.getElementById( 'vertMerged' ).textContent;
  472. var frag = document.getElementById( 'fragMerged' ).textContent;
  473. var material = new THREE.RawShaderMaterial( {
  474. vertexShader: vert,
  475. fragmentShader: frag
  476. } );
  477. materialList.push( material );
  478. var pickingMaterial = new THREE.RawShaderMaterial( {
  479. vertexShader: "#define PICKING\n" + vert,
  480. fragmentShader: "#define PICKING\n" + frag
  481. } );
  482. materialList.push( pickingMaterial );
  483. // geometry
  484. var mgeo = new THREE.BufferGeometry();
  485. geometryList.push( mgeo );
  486. var pos = geo.attributes.position;
  487. var posLen = geo.attributes.position.count * 3;
  488. var vertices = new THREE.BufferAttribute(
  489. new Float32Array( instanceCount * posLen ), 3
  490. );
  491. var vertex = new THREE.Vector3();
  492. var matrix = new THREE.Matrix4();
  493. for ( var i = 0, ul = instanceCount; i < ul; i ++ ) {
  494. var offset = i * posLen;
  495. randomizeMatrix( matrix );
  496. var object = new THREE.Object3D();
  497. objectCount ++;
  498. object.applyMatrix( matrix );
  499. pickingData[ i + 1 ] = object;
  500. vertices.set( pos.array, offset );
  501. for ( var k = 0, l = offset; k < posLen; k += 3, l += 3 ) {
  502. vertex.fromArray( vertices.array, l );
  503. vertex.applyMatrix4( matrix );
  504. vertex.toArray( vertices.array, l );
  505. }
  506. }
  507. mgeo.setAttribute( 'position', vertices );
  508. var colCount = posLen / 3;
  509. var colors = new THREE.BufferAttribute(
  510. new Float32Array( instanceCount * colCount * 3 ), 3
  511. );
  512. var randCol = function () {
  513. return Math.random();
  514. };
  515. for ( var i = 0, ul = instanceCount; i < ul; i ++ ) {
  516. var r = randCol(), g = randCol(), b = randCol();
  517. for ( var j = i * colCount, jl = ( i + 1 ) * colCount; j < jl; j ++ ) {
  518. colors.setXYZ( j, r, g, b );
  519. }
  520. }
  521. mgeo.setAttribute( 'color', colors );
  522. var col = new THREE.Color();
  523. var pickingColors = new THREE.BufferAttribute(
  524. new Float32Array( instanceCount * colCount * 3 ), 3
  525. );
  526. for ( var i = 0, ul = instanceCount; i < ul; i ++ ) {
  527. col.setHex( i + 1 );
  528. for ( var j = i * colCount, jl = ( i + 1 ) * colCount; j < jl; j ++ ) {
  529. pickingColors.setXYZ( j, col.r, col.g, col.b );
  530. }
  531. }
  532. mgeo.setAttribute( 'pickingColor', pickingColors );
  533. // mesh
  534. var mesh = new THREE.Mesh( mgeo, material );
  535. scene.add( mesh );
  536. var pickingMesh = new THREE.Mesh( mgeo, pickingMaterial );
  537. pickingScene.add( pickingMesh );
  538. }
  539. function makeInstanced( geo ) {
  540. // material
  541. var vert = document.getElementById( 'vertInstanced' ).textContent;
  542. var frag = document.getElementById( 'fragInstanced' ).textContent;
  543. var material = new THREE.RawShaderMaterial( {
  544. vertexShader: vert,
  545. fragmentShader: frag,
  546. } );
  547. materialList.push( material );
  548. var pickingMaterial = new THREE.RawShaderMaterial( {
  549. vertexShader: "#define PICKING\n" + vert,
  550. fragmentShader: "#define PICKING\n" + frag
  551. } );
  552. materialList.push( pickingMaterial );
  553. // geometry
  554. var igeo = new THREE.InstancedBufferGeometry();
  555. geometryList.push( igeo );
  556. var vertices = geo.attributes.position.clone();
  557. igeo.setAttribute( 'position', vertices );
  558. // var matrices = new THREE.InstancedBufferAttribute(
  559. // new Float32Array( instanceCount * 16 ), 16
  560. // );
  561. var mcol0 = new THREE.InstancedBufferAttribute(
  562. new Float32Array( instanceCount * 3 ), 3
  563. );
  564. var mcol1 = new THREE.InstancedBufferAttribute(
  565. new Float32Array( instanceCount * 3 ), 3
  566. );
  567. var mcol2 = new THREE.InstancedBufferAttribute(
  568. new Float32Array( instanceCount * 3 ), 3
  569. );
  570. var mcol3 = new THREE.InstancedBufferAttribute(
  571. new Float32Array( instanceCount * 3 ), 3
  572. );
  573. var matrix = new THREE.Matrix4();
  574. var me = matrix.elements;
  575. for ( var i = 0, ul = mcol0.count; i < ul; i ++ ) {
  576. randomizeMatrix( matrix );
  577. var object = new THREE.Object3D();
  578. objectCount ++;
  579. object.applyMatrix( matrix );
  580. pickingData[ i + 1 ] = object;
  581. // matrices.set( matrix.elements, i * 16 );
  582. mcol0.setXYZ( i, me[ 0 ], me[ 1 ], me[ 2 ] );
  583. mcol1.setXYZ( i, me[ 4 ], me[ 5 ], me[ 6 ] );
  584. mcol2.setXYZ( i, me[ 8 ], me[ 9 ], me[ 10 ] );
  585. mcol3.setXYZ( i, me[ 12 ], me[ 13 ], me[ 14 ] );
  586. }
  587. // igeo.setAttribute( 'matrix', matrices );
  588. igeo.setAttribute( 'mcol0', mcol0 );
  589. igeo.setAttribute( 'mcol1', mcol1 );
  590. igeo.setAttribute( 'mcol2', mcol2 );
  591. igeo.setAttribute( 'mcol3', mcol3 );
  592. var randCol = function () {
  593. return Math.random();
  594. };
  595. var colors = new THREE.InstancedBufferAttribute(
  596. new Float32Array( instanceCount * 3 ), 3
  597. );
  598. for ( var i = 0, ul = colors.count; i < ul; i ++ ) {
  599. colors.setXYZ( i, randCol(), randCol(), randCol() );
  600. }
  601. igeo.setAttribute( 'color', colors );
  602. var col = new THREE.Color();
  603. var pickingColors = new THREE.InstancedBufferAttribute(
  604. new Float32Array( instanceCount * 3 ), 3
  605. );
  606. for ( var i = 0, ul = pickingColors.count; i < ul; i ++ ) {
  607. col.setHex( i + 1 );
  608. pickingColors.setXYZ( i, col.r, col.g, col.b );
  609. }
  610. igeo.setAttribute( 'pickingColor', pickingColors );
  611. // mesh
  612. var mesh = new THREE.Mesh( igeo, material );
  613. scene.add( mesh );
  614. var pickingMesh = new THREE.Mesh( igeo, pickingMaterial );
  615. pickingScene.add( pickingMesh );
  616. }
  617. function init() {
  618. // camera
  619. camera = new THREE.PerspectiveCamera(
  620. 70, window.innerWidth / window.innerHeight, 1, 100
  621. );
  622. camera.position.z = 40;
  623. // picking render target
  624. pickingRenderTarget = new THREE.WebGLRenderTarget(
  625. window.innerWidth, window.innerHeight
  626. );
  627. pickingRenderTarget.texture.generateMipmaps = false;
  628. pickingRenderTarget.texture.minFilter = THREE.NearestFilter;
  629. // highlight box
  630. highlightBox = new THREE.Mesh(
  631. new THREE.BoxBufferGeometry( 1, 1, 1 ),
  632. new THREE.MeshLambertMaterial( {
  633. emissive: 0xffff00,
  634. transparent: true,
  635. opacity: 0.5
  636. } )
  637. );
  638. // renderer
  639. container = document.getElementById( "container" );
  640. renderer = new THREE.WebGLRenderer( {
  641. antialias: true,
  642. alpha: true
  643. } );
  644. if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
  645. document.getElementById( "notSupported" ).style.display = "";
  646. return;
  647. }
  648. renderer.setPixelRatio( window.devicePixelRatio );
  649. renderer.setSize( window.innerWidth, window.innerHeight );
  650. //renderer.sortObjects = false;
  651. container.appendChild( renderer.domElement );
  652. if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
  653. throw 'ANGLE_instanced_arrays not supported';
  654. }
  655. // controls
  656. controls = new TrackballControls( camera, renderer.domElement );
  657. controls.staticMoving = true;
  658. // stats
  659. stats = new Stats();
  660. container.appendChild( stats.dom );
  661. // listeners
  662. renderer.domElement.addEventListener( 'mousemove', onMouseMove );
  663. window.addEventListener( 'resize', onWindowResize, false );
  664. }
  665. //
  666. function onMouseMove( e ) {
  667. mouse.x = e.clientX;
  668. mouse.y = e.clientY;
  669. controls.update();
  670. requestAnimationFrame( render );
  671. }
  672. function onWindowResize() {
  673. camera.aspect = window.innerWidth / window.innerHeight;
  674. camera.updateProjectionMatrix();
  675. renderer.setSize( window.innerWidth, window.innerHeight );
  676. pickingRenderTarget.setSize( window.innerWidth, window.innerHeight );
  677. }
  678. function animate() {
  679. if ( doAnimate ) {
  680. requestAnimationFrame( animate );
  681. }
  682. controls.update();
  683. stats.update();
  684. render();
  685. }
  686. function pick() {
  687. // render the picking scene off-screen
  688. highlightBox.visible = false;
  689. renderer.setRenderTarget( pickingRenderTarget );
  690. if ( singlePickingMaterial ) {
  691. scene.overrideMaterial = singlePickingMaterial;
  692. renderer.render( scene, camera );
  693. scene.overrideMaterial = null;
  694. } else {
  695. renderer.render( pickingScene, camera );
  696. }
  697. // read the pixel under the mouse from the texture
  698. renderer.readRenderTargetPixels(
  699. pickingRenderTarget,
  700. mouse.x,
  701. pickingRenderTarget.height - mouse.y,
  702. 1,
  703. 1,
  704. pixelBuffer
  705. );
  706. // interpret the pixel as an ID
  707. var id =
  708. ( pixelBuffer[ 0 ] << 16 ) |
  709. ( pixelBuffer[ 1 ] << 8 ) |
  710. ( pixelBuffer[ 2 ] );
  711. var object = pickingData[ id ];
  712. if ( object ) {
  713. // move the highlightBox so that it surrounds the picked object
  714. if ( object.position && object.rotation && object.scale ) {
  715. highlightBox.position.copy( object.position );
  716. highlightBox.rotation.copy( object.rotation );
  717. highlightBox.scale.copy( object.scale )
  718. .multiply( geometrySize )
  719. .multiplyScalar( scale );
  720. highlightBox.visible = true;
  721. }
  722. } else {
  723. highlightBox.visible = false;
  724. }
  725. }
  726. function render() {
  727. pick();
  728. renderer.setRenderTarget( null );
  729. renderer.render( scene, camera );
  730. }
  731. </script>
  732. </body>
  733. </html>