optimize-lots-of-objects.html 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. <!DOCTYPE html><html lang="fr"><head>
  2. <meta charset="utf-8">
  3. <title>Optimiser Beaucoup d'Objets</title>
  4. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  5. <meta name="twitter:card" content="summary_large_image">
  6. <meta name="twitter:site" content="@threejs">
  7. <meta name="twitter:title" content="Three.js – Optimize Lots of Objects">
  8. <meta property="og:image" content="https://threejs.org/files/share.png">
  9. <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
  10. <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
  11. <link rel="stylesheet" href="../resources/lesson.css">
  12. <link rel="stylesheet" href="../resources/lang.css">
  13. <script type="importmap">
  14. {
  15. "imports": {
  16. "three": "../../build/three.module.js"
  17. }
  18. }
  19. </script>
  20. </head>
  21. <body>
  22. <div class="container">
  23. <div class="lesson-title">
  24. <h1>Optimiser Beaucoup d'Objets</h1>
  25. </div>
  26. <div class="lesson">
  27. <div class="lesson-main">
  28. <p>Cet article fait partie d'une série d'articles sur three.js. Le premier article
  29. est <a href="fundamentals.html">les bases de three.js</a>. Si vous ne l'avez pas encore lu
  30. et que vous débutez avec three.js, vous pourriez vouloir commencer par là. </p>
  31. <p>Il existe de nombreuses façons d'optimiser les choses pour three.js. Une méthode est souvent appelée
  32. <em>fusion de géométrie</em>. Chaque <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> que vous créez et que three.js représente est 1 ou
  33. plusieurs requêtes du système pour dessiner quelque chose. Dessiner 2 choses a plus
  34. de surcoût que d'en dessiner 1, même si les résultats sont les mêmes, donc une façon d'optimiser
  35. est de fusionner les maillages (meshes).</p>
  36. <p>Voyons un exemple où cela est une bonne solution pour un problème.
  37. Recréons le <a href="https://globe.chromeexperiments.com/">Globe WebGL</a>.</p>
  38. <p>La première chose à faire est d'obtenir des données. Le Globe WebGL a dit que les données
  39. qu'ils utilisent proviennent de <a href="http://sedac.ciesin.columbia.edu/gpw/">SEDAC</a>. En consultant
  40. le site, j'ai vu qu'il y avait des <a href="https://beta.sedac.ciesin.columbia.edu/data/set/gpw-v4-basic-demographic-characteristics-rev10">données démographiques au format grille</a>.
  41. J'ai téléchargé les données avec une résolution de 60 minutes. Ensuite, j'ai examiné les données</p>
  42. <p>Cela ressemble à ceci</p>
  43. <pre class="prettyprint showlinemods notranslate lang-txt" translate="no"> ncols 360
  44. nrows 145
  45. xllcorner -180
  46. yllcorner -60
  47. cellsize 0.99999999999994
  48. NODATA_value -9999
  49. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  50. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  51. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  52. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  53. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  54. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  55. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  56. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  57. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  58. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  59. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  60. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  61. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  62. 9.241768 8.790958 2.095345 -9999 0.05114867 -9999 -9999 -9999 -9999 -999...
  63. 1.287993 0.4395509 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999...
  64. -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
  65. </pre>
  66. <p>Il y a quelques lignes qui sont comme des paires clé/valeur suivies de lignes avec une valeur
  67. par point de grille, une ligne pour chaque rangée de points de données.</p>
  68. <p>Pour nous assurer que nous comprenons les données, essayons de les tracer en 2D.</p>
  69. <p>D'abord un peu de code pour charger le fichier texte</p>
  70. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">async function loadFile(url) {
  71. const res = await fetch(url);
  72. return res.text();
  73. }
  74. </pre>
  75. <p>Le code ci-dessus renvoie une <code class="notranslate" translate="no">Promise</code> avec le contenu du fichier à l'<code class="notranslate" translate="no">url</code> ;</p>
  76. <p>Ensuite, nous avons besoin de code pour analyser le fichier</p>
  77. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function parseData(text) {
  78. const data = [];
  79. const settings = {data};
  80. let max;
  81. let min;
  82. // split into lines
  83. text.split('\n').forEach((line) =&gt; {
  84. // split the line by whitespace
  85. const parts = line.trim().split(/\s+/);
  86. if (parts.length === 2) {
  87. // only 2 parts, must be a key/value pair
  88. settings[parts[0]] = parseFloat(parts[1]);
  89. } else if (parts.length &gt; 2) {
  90. // more than 2 parts, must be data
  91. const values = parts.map((v) =&gt; {
  92. const value = parseFloat(v);
  93. if (value === settings.NODATA_value) {
  94. return undefined;
  95. }
  96. max = Math.max(max === undefined ? value : max, value);
  97. min = Math.min(min === undefined ? value : min, value);
  98. return value;
  99. });
  100. data.push(values);
  101. }
  102. });
  103. return Object.assign(settings, {min, max});
  104. }
  105. </pre>
  106. <p>Le code ci-dessus renvoie un objet avec toutes les paires clé/valeur du fichier ainsi
  107. qu'une propriété <code class="notranslate" translate="no">data</code> contenant toutes les données dans un grand tableau et les valeurs <code class="notranslate" translate="no">min</code> et
  108. <code class="notranslate" translate="no">max</code> trouvées dans les données.</p>
  109. <p>Ensuite, nous avons besoin de code pour dessiner ces données</p>
  110. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function drawData(file) {
  111. const {min, max, data} = file;
  112. const range = max - min;
  113. const ctx = document.querySelector('canvas').getContext('2d');
  114. // make the canvas the same size as the data
  115. ctx.canvas.width = ncols;
  116. ctx.canvas.height = nrows;
  117. // but display it double size so it's not too small
  118. ctx.canvas.style.width = px(ncols * 2);
  119. ctx.canvas.style.height = px(nrows * 2);
  120. // fill the canvas to dark gray
  121. ctx.fillStyle = '#444';
  122. ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  123. // draw each data point
  124. data.forEach((row, latNdx) =&gt; {
  125. row.forEach((value, lonNdx) =&gt; {
  126. if (value === undefined) {
  127. return;
  128. }
  129. const amount = (value - min) / range;
  130. const hue = 1;
  131. const saturation = 1;
  132. const lightness = amount;
  133. ctx.fillStyle = hsl(hue, saturation, lightness);
  134. ctx.fillRect(lonNdx, latNdx, 1, 1);
  135. });
  136. });
  137. }
  138. function px(v) {
  139. return `${v | 0}px`;
  140. }
  141. function hsl(h, s, l) {
  142. return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
  143. }
  144. </pre>
  145. <p>Et enfin, en liant le tout</p>
  146. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
  147. .then(parseData)
  148. .then(drawData);
  149. </pre>
  150. <p>Nous donne ce résultat</p>
  151. <p></p><div translate="no" class="threejs_example_container notranslate">
  152. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/gpw-data-viewer.html"></iframe></div>
  153. <a class="threejs_center" href="/manual/examples/gpw-data-viewer.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  154. </div>
  155. <p></p>
  156. <p>Donc, cela semble fonctionner. </p>
  157. <p>Essayons-le en 3D. En partant du code de <a href="rendering-on-demand.html">rendu à la
  158. demande</a>, nous allons créer une boîte par donnée dans
  159. le fichier.</p>
  160. <p>Commençons par créer une simple sphère avec une texture du monde. Voici la texture</p>
  161. <div class="threejs_center"><img src="../examples/resources/images/world.jpg" style="width: 600px"></div>
  162. <p>Et le code pour le mettre en place.</p>
  163. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  164. const loader = new THREE.TextureLoader();
  165. const texture = loader.load('resources/images/world.jpg', render);
  166. const geometry = new THREE.SphereGeometry(1, 64, 32);
  167. const material = new THREE.MeshBasicMaterial({map: texture});
  168. scene.add(new THREE.Mesh(geometry, material));
  169. }
  170. </pre>
  171. <p>Notez l'appel à <code class="notranslate" translate="no">render</code> lorsque la texture a fini de charger. Nous en avons besoin
  172. car nous faisons du <a href="rendering-on-demand.html">rendu à la demande</a> au lieu de le faire en
  173. continu, nous devons donc rendre la scène une fois que la texture est chargée.</p>
  174. <p>Ensuite, nous devons modifier le code qui dessinait un point par point de donnée ci-dessus pour
  175. créer une boîte par point de donnée à la place.</p>
  176. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
  177. const {min, max, data} = file;
  178. const range = max - min;
  179. // make one box geometry
  180. const boxWidth = 1;
  181. const boxHeight = 1;
  182. const boxDepth = 1;
  183. const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  184. // make it so it scales away from the positive Z axis
  185. geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
  186. // these helpers will make it easy to position the boxes
  187. // We can rotate the lon helper on its Y axis to the longitude
  188. const lonHelper = new THREE.Object3D();
  189. scene.add(lonHelper);
  190. // We rotate the latHelper on its X axis to the latitude
  191. const latHelper = new THREE.Object3D();
  192. lonHelper.add(latHelper);
  193. // The position helper moves the object to the edge of the sphere
  194. const positionHelper = new THREE.Object3D();
  195. positionHelper.position.z = 1;
  196. latHelper.add(positionHelper);
  197. const lonFudge = Math.PI * .5;
  198. const latFudge = Math.PI * -0.135;
  199. data.forEach((row, latNdx) =&gt; {
  200. row.forEach((value, lonNdx) =&gt; {
  201. if (value === undefined) {
  202. return;
  203. }
  204. const amount = (value - min) / range;
  205. const material = new THREE.MeshBasicMaterial();
  206. const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
  207. const saturation = 1;
  208. const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
  209. material.color.setHSL(hue, saturation, lightness);
  210. const mesh = new THREE.Mesh(geometry, material);
  211. scene.add(mesh);
  212. // adjust the helpers to point to the latitude and longitude
  213. lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
  214. latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
  215. // use the world matrix of the position helper to
  216. // position this mesh.
  217. positionHelper.updateWorldMatrix(true, false);
  218. mesh.applyMatrix4(positionHelper.matrixWorld);
  219. mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
  220. });
  221. });
  222. }
  223. </pre>
  224. <p>Le code est principalement direct par rapport à notre code de dessin de test. </p>
  225. <p>Nous créons une boîte et ajustons son centre de manière à ce qu'elle s'éloigne de l'axe Z positif. Si nous
  226. ne faisions pas cela, elle s'agrandirait à partir du centre, mais nous voulons qu'elles s'éloignent de l'origine.</p>
  227. <div class="spread">
  228. <div>
  229. <div data-diagram="scaleCenter" style="height: 250px"></div>
  230. <div class="code">par défaut</div>
  231. </div>
  232. <div>
  233. <div data-diagram="scalePositiveZ" style="height: 250px"></div>
  234. <div class="code">ajusté</div>
  235. </div>
  236. </div>
  237. <p>Bien sûr, nous pourrions aussi résoudre cela en faisant de la boîte un enfant d'autres objets <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a>
  238. comme nous l'avons vu dans les <a href="scenegraph.html">graphes de scène</a>, mais plus nous ajoutons de
  239. nœuds à un graphe de scène, plus cela devient lent.</p>
  240. <p>Nous avons également mis en place cette petite hiérarchie de nœuds : <code class="notranslate" translate="no">lonHelper</code>, <code class="notranslate" translate="no">latHelper</code>, et
  241. <code class="notranslate" translate="no">positionHelper</code>. Nous utilisons ces objets pour calculer une position autour de la sphère
  242. où placer la boîte. </p>
  243. <div class="spread">
  244. <div data-diagram="lonLatPos" style="width: 600px; height: 400px;"></div>
  245. </div>
  246. <p>Ci-dessus, la <span style="color: green;">barre verte</span> représente <code class="notranslate" translate="no">lonHelper</code> et
  247. est utilisée pour pivoter vers la longitude sur l'équateur. La <span style="color: blue;">
  248. barre bleue</span> représente <code class="notranslate" translate="no">latHelper</code> qui est utilisée pour pivoter vers une
  249. latitude au-dessus ou en dessous de l'équateur. La <span style="color: red;">sphère
  250. rouge</span> représente le décalage que fournit <code class="notranslate" translate="no">positionHelper</code>.</p>
  251. <p>Nous pourrions faire tous les calculs manuellement pour déterminer les positions sur le globe, mais
  252. faire les choses de cette manière laisse la majeure partie des calculs à la librairie elle-même, de sorte que nous
  253. n'avons pas à nous en occuper.</p>
  254. <p>Pour chaque point de donnée, nous créons un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> et un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> et
  255. puis nous demandons la matrice monde du <code class="notranslate" translate="no">positionHelper</code> et l'appliquons au nouveau <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>.
  256. Enfin, nous mettons à l'échelle le maillage à sa nouvelle position.</p>
  257. <p>Comme ci-dessus, nous aurions pu également créer un <code class="notranslate" translate="no">latHelper</code>, un <code class="notranslate" translate="no">lonHelper</code> et un
  258. <code class="notranslate" translate="no">positionHelper</code> pour chaque nouvelle boîte, mais cela aurait été encore plus lent.</p>
  259. <p>Il y a jusqu'à 360x145 boîtes que nous allons créer. Cela représente jusqu'à 52000 boîtes.
  260. Comme certains points de données sont marqués comme "NO_DATA", le nombre réel de boîtes
  261. que nous allons créer est d'environ 19000. Si nous ajoutions 3 objets d'aide supplémentaires par boîte,
  262. cela représenterait près de 80000 nœuds dans le graphe de scène pour lesquels THREE.js devrait
  263. calculer les positions. En utilisant plutôt un seul ensemble d'aides pour positionner
  264. simplement les maillages, nous économisons environ 60000 opérations.</p>
  265. <p>Une note sur <code class="notranslate" translate="no">lonFudge</code> et <code class="notranslate" translate="no">latFudge</code>. <code class="notranslate" translate="no">lonFudge</code> est π/2, ce qui correspond à un quart de tour.
  266. Cela a du sens. Cela signifie simplement que la texture ou les coordonnées de texture commencent à un
  267. décalage différent autour du globe. <code class="notranslate" translate="no">latFudge</code>, d'un autre côté, je n'ai aucune idée
  268. pourquoi il doit être π * -0.135, c'est juste un montant qui a permis aux boîtes de s'aligner
  269. avec la texture.</p>
  270. <p>La dernière chose à faire est d'appeler notre chargeur</p>
  271. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
  272. .then(parseData)
  273. - .then(drawData)
  274. + .then(addBoxes)
  275. + .then(render);
  276. </pre><p>Une fois que les données ont fini de charger et d'être analysées, nous devons rendre la scène au moins
  277. une fois puisque nous faisons du <a href="rendering-on-demand.html">rendu à la demande</a>.</p>
  278. <p></p><div translate="no" class="threejs_example_container notranslate">
  279. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-slow.html"></iframe></div>
  280. <a class="threejs_center" href="/manual/examples/lots-of-objects-slow.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  281. </div>
  282. <p></p>
  283. <p>Si vous essayez de faire pivoter l'exemple ci-dessus en faisant glisser la souris sur l'échantillon, vous
  284. remarquerez probablement que c'est lent.</p>
  285. <p>Nous pouvons vérifier la fréquence d'images en <a href="debugging-javascript.html">ouvrant les
  286. outils de développement</a> et en activant l'indicateur de fréquence d'images
  287. du navigateur.</p>
  288. <div class="threejs_center"><img src="../resources/images/bring-up-fps-meter.gif"></div>
  289. <p>Sur ma machine, je vois une fréquence d'images inférieure à 20 ips.</p>
  290. <div class="threejs_center"><img src="../resources/images/fps-meter.gif"></div>
  291. <p>Cela ne me semble pas très fluide et je suspecte que beaucoup de gens ont des machines
  292. plus lentes, ce qui rendrait la situation encore pire. Nous ferions mieux d'étudier l'optimisation.</p>
  293. <p>Pour ce problème particulier, nous pouvons fusionner toutes les boîtes en une seule géométrie.
  294. Nous dessinons actuellement environ 19000 boîtes. En les fusionnant en une seule
  295. géométrie, nous supprimerions 18999 opérations.</p>
  296. <p>Voici le nouveau code pour fusionner les boîtes en une seule géométrie.</p>
  297. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
  298. const {min, max, data} = file;
  299. const range = max - min;
  300. - // make one box geometry
  301. - const boxWidth = 1;
  302. - const boxHeight = 1;
  303. - const boxDepth = 1;
  304. - const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  305. - // make it so it scales away from the positive Z axis
  306. - geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
  307. // these helpers will make it easy to position the boxes
  308. // We can rotate the lon helper on its Y axis to the longitude
  309. const lonHelper = new THREE.Object3D();
  310. scene.add(lonHelper);
  311. // We rotate the latHelper on its X axis to the latitude
  312. const latHelper = new THREE.Object3D();
  313. lonHelper.add(latHelper);
  314. // The position helper moves the object to the edge of the sphere
  315. const positionHelper = new THREE.Object3D();
  316. positionHelper.position.z = 1;
  317. latHelper.add(positionHelper);
  318. + // Utilisé pour déplacer le centre de la boîte afin qu'elle s'agrandisse à partir de l'axe Z positif
  319. + const originHelper = new THREE.Object3D();
  320. + originHelper.position.z = 0.5;
  321. + positionHelper.add(originHelper);
  322. const lonFudge = Math.PI * .5;
  323. const latFudge = Math.PI * -0.135;
  324. + const geometries = [];
  325. data.forEach((row, latNdx) =&gt; {
  326. row.forEach((value, lonNdx) =&gt; {
  327. if (value === undefined) {
  328. return;
  329. }
  330. const amount = (value - min) / range;
  331. - const material = new THREE.MeshBasicMaterial();
  332. - const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
  333. - const saturation = 1;
  334. - const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
  335. - material.color.setHSL(hue, saturation, lightness);
  336. - const mesh = new THREE.Mesh(geometry, material);
  337. - scene.add(mesh);
  338. + const boxWidth = 1;
  339. + const boxHeight = 1;
  340. + const boxDepth = 1;
  341. + const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  342. // adjust the helpers to point to the latitude and longitude
  343. lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
  344. latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
  345. - // use the world matrix of the position helper to
  346. - // position this mesh.
  347. - positionHelper.updateWorldMatrix(true, false);
  348. - mesh.applyMatrix4(positionHelper.matrixWorld);
  349. -
  350. - mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
  351. + // use the world matrix of the origin helper to
  352. + // position this geometry
  353. + positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
  354. + originHelper.updateWorldMatrix(true, false);
  355. + geometry.applyMatrix4(originHelper.matrixWorld);
  356. +
  357. + geometries.push(geometry);
  358. });
  359. });
  360. + const mergedGeometry = BufferGeometryUtils.mergeGeometries(
  361. + geometries, false);
  362. + const material = new THREE.MeshBasicMaterial({color:'red'});
  363. + const mesh = new THREE.Mesh(mergedGeometry, material);
  364. + scene.add(mesh);
  365. }
  366. </pre>
  367. <p>Ci-dessus, nous avons supprimé le code qui modifiait le point central de la géométrie de la boîte
  368. et le faisons à la place en ajoutant un <code class="notranslate" translate="no">originHelper</code>. Auparavant, nous utilisions la même
  369. géométrie 19000 fois. Cette fois, nous créons une nouvelle géométrie pour chaque boîte et
  370. comme nous allons utiliser <code class="notranslate" translate="no">applyMatrix</code> pour déplacer les sommets de chaque géométrie de boîte,
  371. autant le faire une fois au lieu de deux.</p>
  372. <p>À la fin, nous passons un tableau de toutes les géométries à
  373. <code class="notranslate" translate="no">BufferGeometryUtils.mergeGeometries</code>, ce qui les combinera toutes
  374. en un seul maillage.</p>
  375. <p>Nous devons également inclure le <code class="notranslate" translate="no">BufferGeometryUtils</code></p>
  376. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
  377. </pre>
  378. <p>Et maintenant, du moins sur ma machine, j'obtiens 60 images par seconde</p>
  379. <p></p><div translate="no" class="threejs_example_container notranslate">
  380. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-merged.html"></iframe></div>
  381. <a class="threejs_center" href="/manual/examples/lots-of-objects-merged.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  382. </div>
  383. <p></p>
  384. <p>Cela a donc fonctionné, mais comme il s'agit d'un seul maillage, nous n'obtenons qu'un seul matériau, ce qui
  385. signifie que nous n'avons qu'une seule couleur, alors qu'avant, nous avions une couleur différente sur chaque boîte. Nous pouvons
  386. y remédier en utilisant les couleurs de sommet.</p>
  387. <p>Les couleurs de sommet ajoutent une couleur par sommet. En réglant toutes les couleurs de chaque sommet
  388. de chaque boîte sur des couleurs spécifiques, chaque boîte aura une couleur différente.</p>
  389. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const color = new THREE.Color();
  390. const lonFudge = Math.PI * .5;
  391. const latFudge = Math.PI * -0.135;
  392. const geometries = [];
  393. data.forEach((row, latNdx) =&gt; {
  394. row.forEach((value, lonNdx) =&gt; {
  395. if (value === undefined) {
  396. return;
  397. }
  398. const amount = (value - min) / range;
  399. const boxWidth = 1;
  400. const boxHeight = 1;
  401. const boxDepth = 1;
  402. const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  403. // adjust the helpers to point to the latitude and longitude
  404. lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
  405. latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
  406. // use the world matrix of the origin helper to
  407. // position this geometry
  408. positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
  409. originHelper.updateWorldMatrix(true, false);
  410. geometry.applyMatrix4(originHelper.matrixWorld);
  411. + // calculer une couleur
  412. + const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
  413. + const saturation = 1;
  414. + const lightness = THREE.MathUtils.lerp(0.4, 1.0, amount);
  415. + color.setHSL(hue, saturation, lightness);
  416. + // obtenir les couleurs sous forme de tableau de valeurs de 0 à 255
  417. + const rgb = color.toArray().map(v =&gt; v * 255);
  418. +
  419. + // créer un tableau pour stocker les couleurs pour chaque sommet
  420. + const numVerts = geometry.getAttribute('position').count;
  421. + const itemSize = 3; // r, g, b
  422. + const colors = new Uint8Array(itemSize * numVerts);
  423. +
  424. + // copier la couleur dans le tableau de couleurs pour chaque sommet
  425. + colors.forEach((v, ndx) =&gt; {
  426. + colors[ndx] = rgb[ndx % 3];
  427. + });
  428. +
  429. + const normalized = true;
  430. + const colorAttrib = new THREE.BufferAttribute(colors, itemSize, normalized);
  431. + geometry.setAttribute('color', colorAttrib);
  432. geometries.push(geometry);
  433. });
  434. });
  435. </pre>
  436. <p>Le code ci-dessus recherche le nombre ou les sommets nécessaires en obtenant l'attribut <code class="notranslate" translate="no">position</code>
  437. de la géométrie. Nous créons ensuite un <code class="notranslate" translate="no">Uint8Array</code> pour y mettre les couleurs.
  438. Il ajoute ensuite cela comme un attribut en appelant <code class="notranslate" translate="no">geometry.setAttribute</code>.</p>
  439. <p>Enfin, nous devons dire à three.js d'utiliser les couleurs de sommet. </p>
  440. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mergedGeometry = BufferGeometryUtils.mergeGeometries(
  441. geometries, false);
  442. -const material = new THREE.MeshBasicMaterial({color:'red'});
  443. +const material = new THREE.MeshBasicMaterial({
  444. + vertexColors: true,
  445. +});
  446. const mesh = new THREE.Mesh(mergedGeometry, material);
  447. scene.add(mesh);
  448. </pre>
  449. <p>Et avec cela, nous retrouvons nos couleurs</p>
  450. <p></p><div translate="no" class="threejs_example_container notranslate">
  451. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-merged-vertexcolors.html"></iframe></div>
  452. <a class="threejs_center" href="/manual/examples/lots-of-objects-merged-vertexcolors.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  453. </div>
  454. <p></p>
  455. <p>La fusion de géométrie est une technique d'optimisation courante. Par exemple, au lieu de
  456. 100 arbres, vous pourriez fusionner les arbres en 1 seule géométrie, un tas de roches individuelles
  457. en une seule géométrie de roches, une clôture de piquets individuels en un seul maillage de clôture.
  458. Un autre exemple dans Minecraft, il ne dessine probablement pas chaque cube individuellement,
  459. mais crée plutôt des groupes de cubes fusionnés et supprime également sélectivement les faces qui ne
  460. sont jamais visibles.</p>
  461. <p>Le problème avec le fait de tout transformer en un seul maillage est qu'il n'est plus facile
  462. de déplacer une partie qui était auparavant séparée. Cependant, selon notre cas d'utilisation,
  463. il existe des solutions créatives. Nous en explorerons une dans
  464. <a href="optimize-lots-of-objects-animated.html">un autre article</a>.</p>
  465. <p><canvas id="c"></canvas></p>
  466. <script type="module" src="../resources/threejs-lots-of-objects.js"></script>
  467. </div>
  468. </div>
  469. </div>
  470. <script src="../resources/prettify.js"></script>
  471. <script src="../resources/lesson.js"></script>
  472. </body></html>
粤ICP备19079148号