custom-buffergeometry.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <!DOCTYPE html><html lang="fr"><head>
  2. <meta charset="utf-8">
  3. <title>BufferGeometry Personnalisée</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 – BufferGeometry Personnalisée">
  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>BufferGeometry Personnalisée</h1>
  25. </div>
  26. <div class="lesson">
  27. <div class="lesson-main">
  28. <p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> est la manière dont three.js représente toute la géométrie. Une <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>
  29. est essentiellement une collection <em>nommée</em> de <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s.
  30. Chaque <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> représente un tableau d'un type de données : positions,
  31. normales, couleurs, uv, etc... Ensemble, les <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s nommées représentent
  32. des <em>tableaux parallèles</em> de toutes les données pour chaque sommet.</p>
  33. <div class="threejs_center"><img src="../resources/threejs-attributes.svg" style="width: 700px"></div>
  34. <p>Ci-dessus, vous pouvez voir que nous avons 4 attributs : <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">normal</code>, <code class="notranslate" translate="no">color</code>, <code class="notranslate" translate="no">uv</code>.
  35. Ils représentent des <em>tableaux parallèles</em>, ce qui signifie que le N-ième ensemble de données de chaque
  36. attribut appartient au même sommet. Le sommet à l'index = 4 est mis en évidence
  37. pour montrer que les données parallèles à travers tous les attributs définissent un seul sommet.</p>
  38. <p>Cela soulève un point, voici un diagramme d'un cube avec un coin mis en évidence.</p>
  39. <div class="threejs_center"><img src="../resources/cube-faces-vertex.svg" style="width: 500px"></div>
  40. <p>En y réfléchissant, ce coin unique nécessite une normale différente pour chaque face du
  41. cube. Une normale est une information sur la direction vers laquelle quelque chose fait face. Dans le diagramme,
  42. les normales sont représentées par les flèches autour du sommet d'angle, montrant que chaque
  43. face qui partage cette position de sommet a besoin d'une normale qui pointe dans une direction différente.</p>
  44. <p>Ce coin a également besoin d'UV différents pour chaque face. Les UV sont des coordonnées de texture
  45. qui spécifient quelle partie d'une texture dessinée sur un triangle correspond à cette
  46. position de sommet. Vous pouvez voir que la face verte a besoin que ce sommet ait une UV qui corresponde
  47. au coin supérieur droit de la texture F, la face bleue a besoin d'une UV qui corresponde au
  48. coin supérieur gauche de la texture F, et la face rouge a besoin d'une UV qui corresponde au coin
  49. inférieur gauche de la texture F.</p>
  50. <p>Un seul <em>sommet</em> est la combinaison de toutes ses parties. Si un sommet a besoin d'une
  51. partie différente, alors il doit s'agir d'un sommet différent.</p>
  52. <p>Comme exemple simple, créons un cube en utilisant <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. Un cube est intéressant
  53. car il semble partager des sommets aux coins mais ce n'est pas le cas en réalité. Pour notre exemple, nous allons lister tous les sommets avec toutes leurs données,
  54. puis convertir ces données en tableaux parallèles et enfin les utiliser pour créer des
  55. <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s et les ajouter à une <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
  56. <p>Nous commençons par une liste de toutes les données nécessaires pour le cube. Rappelez-vous encore une fois
  57. que si un sommet a des parties uniques, il doit s'agir d'un sommet distinct. En conséquence,
  58. pour faire un cube, il faut 36 sommets. 2 triangles par face, 3 sommets par triangle,
  59. 6 faces = 36 sommets.</p>
  60. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
  61. // avant
  62. { pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
  63. { pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
  64. { pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
  65. { pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
  66. { pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
  67. { pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
  68. // droite
  69. { pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
  70. { pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
  71. { pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
  72. { pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
  73. { pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
  74. { pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
  75. // arrière
  76. { pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
  77. { pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
  78. { pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
  79. { pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
  80. { pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
  81. { pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
  82. // gauche
  83. { pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 0], },
  84. { pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
  85. { pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
  86. { pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
  87. { pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
  88. { pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 1], },
  89. // haut
  90. { pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 0], },
  91. { pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
  92. { pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
  93. { pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
  94. { pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
  95. { pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 1], },
  96. // bas
  97. { pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 0], },
  98. { pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
  99. { pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
  100. { pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
  101. { pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
  102. { pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 1], },
  103. ];
  104. </pre>
  105. <p>Nous pouvons ensuite traduire tout cela en 3 tableaux parallèles</p>
  106. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const positions = [];
  107. const normals = [];
  108. const uvs = [];
  109. for (const vertex of vertices) {
  110. positions.push(...vertex.pos);
  111. normals.push(...vertex.norm);
  112. uvs.push(...vertex.uv);
  113. }
  114. </pre>
  115. <p>Enfin, nous pouvons créer une <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>, puis une <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> pour chaque tableau
  116. et l'ajouter à la <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
  117. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const geometry = new THREE.BufferGeometry();
  118. const positionNumComponents = 3;
  119. const normalNumComponents = 3;
  120. const uvNumComponents = 2;
  121. geometry.setAttribute(
  122. 'position',
  123. new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
  124. geometry.setAttribute(
  125. 'normal',
  126. new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
  127. geometry.setAttribute(
  128. 'uv',
  129. new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
  130. </pre>
  131. <p>Notez que les noms sont importants. Vous devez nommer vos attributs avec les noms
  132. que three.js attend (sauf si vous créez un shader personnalisé).
  133. Dans ce cas : <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">normal</code>, et <code class="notranslate" translate="no">uv</code>. Si vous voulez des couleurs de sommet,
  134. nommez votre attribut <code class="notranslate" translate="no">color</code>.</p>
  135. <p>Ci-dessus, nous avons créé 3 tableaux natifs JavaScript, <code class="notranslate" translate="no">positions</code>, <code class="notranslate" translate="no">normals</code> et <code class="notranslate" translate="no">uvs</code>.
  136. Nous les avons ensuite convertis en
  137. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArrays</a>
  138. de type <code class="notranslate" translate="no">Float32Array</code>. Une <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> nécessite un TypedArray, pas un tableau natif.
  139. Une <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> exige également que vous lui indiquiez combien de composants il y a
  140. par sommet. Pour les positions et les normales, nous avons 3 composants par sommet,
  141. x, y et z. Pour les UV, nous en avons 2, u et v.</p>
  142. <p></p><div translate="no" class="threejs_example_container notranslate">
  143. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-cube.html"></iframe></div>
  144. <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  145. </div>
  146. <p></p>
  147. <p>C'est beaucoup de données. Une petite chose que nous pouvons faire est d'utiliser des indices pour référencer
  148. les sommets. En revenant à nos données de cube, chaque face est composée de 2 triangles
  149. avec 3 sommets chacun, soit 6 sommets au total, mais 2 de ces sommets sont exactement les mêmes ;
  150. La même position, la même normale et la même uv.
  151. Nous pouvons donc supprimer les sommets correspondants et les
  152. référencer par index. Nous commençons par supprimer les sommets correspondants.</p>
  153. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
  154. // avant
  155. { pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 0], }, // 0
  156. { pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], }, // 1
  157. { pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], }, // 2
  158. -
  159. - { pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
  160. - { pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
  161. { pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 1], }, // 3
  162. // droite
  163. { pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 0], }, // 4
  164. { pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], }, // 5
  165. -
  166. - { pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
  167. - { pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
  168. { pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], }, // 6
  169. { pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 1], }, // 7
  170. // arrière
  171. { pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 0], }, // 8
  172. { pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], }, // 9
  173. -
  174. - { pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
  175. - { pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
  176. { pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], }, // 10
  177. { pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 1], }, // 11
  178. // gauche
  179. { pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 0], }, // 12
  180. { pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], }, // 13
  181. -
  182. - { pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
  183. - { pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
  184. { pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], }, // 14
  185. { pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 1], }, // 15
  186. // haut
  187. { pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 0], }, // 16
  188. { pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], }, // 17
  189. -
  190. - { pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
  191. - { pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
  192. { pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], }, // 18
  193. { pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 1], }, // 19
  194. // bas
  195. { pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 0], }, // 20
  196. { pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], }, // 21
  197. -
  198. - { pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
  199. - { pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
  200. { pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], }, // 22
  201. { pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 1], }, // 23
  202. ];
  203. </pre>
  204. <p>Nous avons donc maintenant 24 sommets uniques. Ensuite, nous spécifions 36 indices
  205. pour les 36 sommets que nous devons dessiner pour faire 12 triangles en appelant <a href="/docs/#api/en/core/BufferGeometry.setIndex"><code class="notranslate" translate="no">BufferGeometry.setIndex</code></a> avec un tableau d'indices.</p>
  206. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.setAttribute(
  207. 'position',
  208. new THREE.BufferAttribute(positions, positionNumComponents));
  209. geometry.setAttribute(
  210. 'normal',
  211. new THREE.BufferAttribute(normals, normalNumComponents));
  212. geometry.setAttribute(
  213. 'uv',
  214. new THREE.BufferAttribute(uvs, uvNumComponents));
  215. +geometry.setIndex([
  216. + 0, 1, 2, 2, 1, 3, // avant
  217. + 4, 5, 6, 6, 5, 7, // droite
  218. + 8, 9, 10, 10, 9, 11, // arrière
  219. + 12, 13, 14, 14, 13, 15, // gauche
  220. + 16, 17, 18, 18, 17, 19, // haut
  221. + 20, 21, 22, 22, 21, 23, // bas
  222. +]);
  223. </pre>
  224. <p></p><div translate="no" class="threejs_example_container notranslate">
  225. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-cube-indexed.html"></iframe></div>
  226. <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-indexed.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  227. </div>
  228. <p></p>
  229. <p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> a une méthode <a href="/docs/#api/en/core/BufferGeometry#computeVertexNormals"><code class="notranslate" translate="no">computeVertexNormals</code></a> pour calculer les normales si vous
  230. ne les fournissez pas. Malheureusement,
  231. étant donné que les positions ne peuvent pas être partagées si une autre partie d'un sommet est différente,
  232. les résultats de l'appel à <code class="notranslate" translate="no">computeVertexNormals</code> généreront des coutures si votre
  233. géométrie est censée se connecter à elle-même comme une sphère ou un cylindre.</p>
  234. <div class="spread">
  235. <div>
  236. <div data-diagram="bufferGeometryCylinder"></div>
  237. </div>
  238. </div>
  239. <p>Pour le cylindre ci-dessus, les normales ont été créées à l'aide de <code class="notranslate" translate="no">computeVertexNormals</code>.
  240. Si vous regardez attentivement, il y a une couture sur le cylindre. Cela est dû au fait qu'il
  241. n'y a aucun moyen de partager les sommets au début et à la fin du cylindre, car ils
  242. nécessitent des UV différents, de sorte que la fonction pour les calculer n'a aucune idée qu'il
  243. s'agit des mêmes sommets à lisser. Juste une petite chose dont il faut être conscient.
  244. La solution est de fournir vos propres normales.</p>
  245. <p>Nous pouvons également utiliser des <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArrays</a> dès le début au lieu de tableaux JavaScript natifs.
  246. L'inconvénient des TypedArrays est que vous devez spécifier leur taille à l'avance. Bien sûr,
  247. ce n'est pas une si grande contrainte, mais avec les tableaux natifs, nous pouvons simplement
  248. <code class="notranslate" translate="no">push</code> des valeurs et voir la taille finale en vérifiant leur
  249. <code class="notranslate" translate="no">length</code> à la fin. Avec les TypedArrays, il n'y a pas de fonction push, nous devons donc
  250. faire notre propre gestion des données lorsque nous y ajoutons des valeurs.</p>
  251. <p>Dans cet exemple, connaître la longueur à l'avance est assez facile, car nous utilisons
  252. un grand bloc de données statiques pour commencer.</p>
  253. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const positions = [];
  254. -const normals = [];
  255. -const uvs = [];
  256. +const numVertices = vertices.length;
  257. +const positionNumComponents = 3;
  258. +const normalNumComponents = 3;
  259. +const uvNumComponents = 2;
  260. +const positions = new Float32Array(numVertices * positionNumComponents);
  261. +const normals = new Float32Array(numVertices * normalNumComponents);
  262. +const uvs = new Float32Array(numVertices * uvNumComponents);
  263. +let posNdx = 0;
  264. +let nrmNdx = 0;
  265. +let uvNdx = 0;
  266. for (const vertex of vertices) {
  267. - positions.push(...vertex.pos);
  268. - normals.push(...vertex.norm);
  269. - uvs.push(...vertex.uv);
  270. + positions.set(vertex.pos, posNdx);
  271. + normals.set(vertex.norm, nrmNdx);
  272. + uvs.set(vertex.uv, uvNdx);
  273. + posNdx += positionNumComponents;
  274. + nrmNdx += normalNumComponents;
  275. + uvNdx += uvNumComponents;
  276. }
  277. geometry.setAttribute(
  278. 'position',
  279. - new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
  280. + new THREE.BufferAttribute(positions, positionNumComponents));
  281. geometry.setAttribute(
  282. 'normal',
  283. - new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
  284. + new THREE.BufferAttribute(normals, normalNumComponents));
  285. geometry.setAttribute(
  286. 'uv',
  287. - new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
  288. + new THREE.BufferAttribute(uvs, uvNumComponents));
  289. geometry.setIndex([
  290. 0, 1, 2, 2, 1, 3, // avant
  291. 4, 5, 6, 6, 5, 7, // droite
  292. 8, 9, 10, 10, 9, 11, // arrière
  293. 12, 13, 14, 14, 13, 15, // gauche
  294. 16, 17, 18, 18, 17, 19, // haut
  295. 20, 21, 22, 22, 21, 23, // bas
  296. ]);
  297. </pre>
  298. <p></p><div translate="no" class="threejs_example_container notranslate">
  299. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-cube-typedarrays.html"></iframe></div>
  300. <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-typedarrays.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  301. </div>
  302. <p></p>
  303. <p>Une bonne raison d'utiliser des typedarrays est si vous souhaitez mettre à jour dynamiquement une partie
  304. des sommets.</p>
  305. <p>Je n'ai pas pu trouver un très bon exemple de mise à jour dynamique des sommets, alors j'ai décidé de faire une sphère et de déplacer chaque quadricône vers l'intérieur et l'extérieur depuis le centre. J'espère que c'est un exemple utile.</p>
  306. <p>Voici le code pour générer les positions et les indices d'une sphère. Le code
  307. partage les sommets au sein d'un quadricône, mais ne partage pas les sommets entre
  308. les quadricônes, car nous voulons pouvoir déplacer chaque quadricône séparément.</p>
  309. <p>Comme je suis paresseux, j'ai utilisé une petite hiérarchie de 3 objets <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> pour calculer
  310. les points de la sphère. La façon dont cela fonctionne est expliquée dans <a href="optimize-lots-of-objects.html">l'article sur l'optimisation de nombreux objets</a>.</p>
  311. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeSpherePositions(segmentsAround, segmentsDown) {
  312. const numVertices = segmentsAround * segmentsDown * 6;
  313. const numComponents = 3;
  314. const positions = new Float32Array(numVertices * numComponents);
  315. const indices = [];
  316. const longHelper = new THREE.Object3D();
  317. const latHelper = new THREE.Object3D();
  318. const pointHelper = new THREE.Object3D();
  319. longHelper.add(latHelper);
  320. latHelper.add(pointHelper);
  321. pointHelper.position.z = 1;
  322. const temp = new THREE.Vector3();
  323. function getPoint(lat, long) {
  324. latHelper.rotation.x = lat;
  325. longHelper.rotation.y = long;
  326. longHelper.updateMatrixWorld(true);
  327. return pointHelper.getWorldPosition(temp).toArray();
  328. }
  329. let posNdx = 0;
  330. let ndx = 0;
  331. for (let down = 0; down &lt; segmentsDown; ++down) {
  332. const v0 = down / segmentsDown;
  333. const v1 = (down + 1) / segmentsDown;
  334. const lat0 = (v0 - 0.5) * Math.PI;
  335. const lat1 = (v1 - 0.5) * Math.PI;
  336. for (let across = 0; across &lt; segmentsAround; ++across) {
  337. const u0 = across / segmentsAround;
  338. const u1 = (across + 1) / segmentsAround;
  339. const long0 = u0 * Math.PI * 2;
  340. const long1 = u1 * Math.PI * 2;
  341. positions.set(getPoint(lat0, long0), posNdx); posNdx += numComponents;
  342. positions.set(getPoint(lat1, long0), posNdx); posNdx += numComponents;
  343. positions.set(getPoint(lat0, long1), posNdx); posNdx += numComponents;
  344. positions.set(getPoint(lat1, long1), posNdx); posNdx += numComponents;
  345. indices.push(
  346. ndx, ndx + 1, ndx + 2,
  347. ndx + 2, ndx + 1, ndx + 3,
  348. );
  349. ndx += 4;
  350. }
  351. }
  352. return {positions, indices};
  353. }
  354. </pre>
  355. <p>Nous pouvons ensuite l'appeler comme ceci</p>
  356. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const segmentsAround = 24;
  357. const segmentsDown = 16;
  358. const {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);
  359. </pre>
  360. <p>Comme les positions retournées sont des positions de sphère unitaire, ce sont exactement les mêmes
  361. valeurs dont nous avons besoin pour les normales, nous pouvons donc simplement les dupliquer pour les normales.</p>
  362. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const normals = positions.slice();
  363. </pre>
  364. <p>Et ensuite nous configurons les attributs comme auparavant</p>
  365. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.BufferGeometry();
  366. const positionNumComponents = 3;
  367. const normalNumComponents = 3;
  368. +const positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);
  369. +positionAttribute.setUsage(THREE.DynamicDrawUsage);
  370. geometry.setAttribute(
  371. 'position',
  372. + positionAttribute);
  373. geometry.setAttribute(
  374. 'normal',
  375. new THREE.BufferAttribute(normals, normalNumComponents));
  376. geometry.setIndex(indices);
  377. </pre>
  378. <p>J'ai mis en évidence quelques différences. Nous sauvegardons une référence à l'attribut de position.
  379. Nous le marquons également comme dynamique. C'est une indication pour THREE.js que nous allons modifier
  380. souvent le contenu de l'attribut.</p>
  381. <p>Dans notre boucle de rendu, nous mettons à jour les positions en fonction de leurs normales à chaque image.</p>
  382. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const temp = new THREE.Vector3();
  383. ...
  384. for (let i = 0; i &lt; positions.length; i += 3) {
  385. const quad = (i / 12 | 0);
  386. const ringId = quad / segmentsAround | 0;
  387. const ringQuadId = quad % segmentsAround;
  388. const ringU = ringQuadId / segmentsAround;
  389. const angle = ringU * Math.PI * 2;
  390. temp.fromArray(normals, i);
  391. temp.multiplyScalar(THREE.MathUtils.lerp(1, 1.4, Math.sin(time + ringId + angle) * .5 + .5));
  392. temp.toArray(positions, i);
  393. }
  394. positionAttribute.needsUpdate = true;
  395. </pre>
  396. <p>Et nous réglons <code class="notranslate" translate="no">positionAttribute.needsUpdate</code> à true pour dire à THREE.js d'utiliser nos changements.</p>
  397. <p></p><div translate="no" class="threejs_example_container notranslate">
  398. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-dynamic.html"></iframe></div>
  399. <a class="threejs_center" href="/manual/examples/custom-buffergeometry-dynamic.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  400. </div>
  401. <p></p>
  402. <p>J'espère que ces exemples vous ont été utiles pour comprendre comment utiliser <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> directement pour
  403. créer votre propre géométrie et comment mettre à jour dynamiquement le contenu d'un
  404. <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>.</p>
  405. <!-- needed in English only to prevent warning from outdated translations -->
  406. <p><a href="resources/threejs-geometry.svg"></a></p>
  407. <p><canvas id="c"></canvas></p>
  408. <script type="module" src="../resources/threejs-custom-buffergeometry.js"></script>
  409. </div>
  410. </div>
  411. </div>
  412. <script src="../resources/prettify.js"></script>
  413. <script src="../resources/lesson.js"></script>
  414. </body></html>
粤ICP备19079148号