Browse Source

French-Translate (#31252)

Translate english to french
Alexandre Gagné 7 months ago
parent
commit
28e2425652
57 changed files with 17226 additions and 1700 deletions
  1. 682 5
      manual/fr/align-html-elements-to-3d.html
  2. 173 0
      manual/fr/animation-system.html
  3. 217 5
      manual/fr/backgrounds.html
  4. 283 5
      manual/fr/billboards.html
  5. 180 95
      manual/fr/cameras.html
  6. 366 5
      manual/fr/canvas-textures.html
  7. 415 5
      manual/fr/cleanup.html
  8. 354 0
      manual/fr/color-management.html
  9. 179 0
      manual/fr/creating-a-scene.html
  10. 170 0
      manual/fr/creating-text.html
  11. 120 123
      manual/fr/custom-buffergeometry.html
  12. 41 5
      manual/fr/debugging-glsl.html
  13. 336 5
      manual/fr/debugging-javascript.html
  14. 91 0
      manual/fr/drawing-lines.html
  15. 93 0
      manual/fr/faq.html
  16. 97 44
      manual/fr/fog.html
  17. 272 259
      manual/fr/fundamentals.html
  18. 1719 5
      manual/fr/game.html
  19. 100 0
      manual/fr/how-to-create-vr-content.html
  20. 169 0
      manual/fr/how-to-dispose-of-objects.html
  21. 275 0
      manual/fr/how-to-update-things.html
  22. 142 0
      manual/fr/how-to-use-post-processing.html
  23. 594 5
      manual/fr/indexed-textures.html
  24. 306 0
      manual/fr/installation.html
  25. 146 0
      manual/fr/libraries-and-plugins.html
  26. 171 88
      manual/fr/lights.html
  27. 551 5
      manual/fr/load-gltf.html
  28. 583 5
      manual/fr/load-obj.html
  29. 160 0
      manual/fr/loading-3d-models.html
  30. 10 5
      manual/fr/material-table.html
  31. 129 74
      manual/fr/materials.html
  32. 96 0
      manual/fr/matrix-transformations.html
  33. 615 5
      manual/fr/multiple-scenes.html
  34. 1049 2
      manual/fr/offscreencanvas.html
  35. 455 5
      manual/fr/optimize-lots-of-objects-animated.html
  36. 473 4
      manual/fr/optimize-lots-of-objects.html
  37. 358 5
      manual/fr/picking.html
  38. 184 5
      manual/fr/post-processing.html
  39. 182 157
      manual/fr/prerequisites.html
  40. 175 183
      manual/fr/primitives.html
  41. 158 4
      manual/fr/rendering-on-demand.html
  42. 33 34
      manual/fr/rendertargets.html
  43. 128 153
      manual/fr/responsive.html
  44. 206 99
      manual/fr/scenegraph.html
  45. 32 26
      manual/fr/setup.html
  46. 285 5
      manual/fr/shadertoy.html
  47. 197 109
      manual/fr/shadows.html
  48. 220 134
      manual/fr/textures.html
  49. 306 4
      manual/fr/tips.html
  50. 364 5
      manual/fr/transparency.html
  51. 254 0
      manual/fr/uniform-types.html
  52. 193 0
      manual/fr/useful-links.html
  53. 1079 5
      manual/fr/voxel-geometry.html
  54. 62 0
      manual/fr/webgl-compatibility-check.html
  55. 338 4
      manual/fr/webxr-basics.html
  56. 350 4
      manual/fr/webxr-look-to-select.html
  57. 310 5
      manual/fr/webxr-point-to-select.html

+ 682 - 5
manual/fr/align-html-elements-to-3d.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Aligning HTML Elements to 3D</title>
+    <title>Aligner les éléments HTML en 3D</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Aligning HTML Elements to 3D">
+    <meta name="twitter:title" content="Three.js – Aligner les éléments HTML en 3D">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,689 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Aligning HTML Elements to 3D</h1>
+        <h1>Aligner les éléments HTML en 3D</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/align-html-elements-to-3d.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le premier article
+est <a href="fundamentals.html">les bases de three.js</a>. Si vous ne l'avez pas
+encore lu et que vous débutez avec three.js, vous pourriez vouloir commencer par là. </p>
+<p>Parfois, vous aimeriez afficher du texte dans votre scène 3D. Vous avez plusieurs options,
+chacune avec ses avantages et ses inconvénients.</p>
+<ul>
+<li><p>Utiliser du texte 3D</p>
+<p>Si vous regardez l'<a href="primitives.html">article sur les primitives</a>, vous verrez la <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a> qui
+permet de créer du texte 3D. Cela peut être utile pour des logos volants, mais probablement moins pour des statistiques, des informations,
+ou l'étiquetage de nombreux éléments.</p>
+</li>
+<li><p>Utiliser une texture avec du texte 2D dessiné dessus.</p>
+<p>L'article sur <a href="canvas-textures.html">l'utilisation d'un Canvas comme texture</a> montre comment utiliser
+un canvas comme texture. Vous pouvez dessiner du texte dans un canvas et l'<a href="billboards.html">afficher comme un panneau (billboard)</a>.
+L'avantage ici pourrait être que le texte est intégré à la scène 3D. Pour quelque chose comme un terminal d'ordinateur
+montré dans une scène 3D, cela pourrait être parfait.</p>
+</li>
+<li><p>Utiliser des éléments HTML et les positionner pour correspondre à la 3D</p>
+<p>L'avantage de cette approche est que vous pouvez utiliser tout le HTML. Votre HTML peut contenir plusieurs éléments. Il peut
+être stylisé avec du CSS. Il peut également être sélectionné par l'utilisateur car c'est du vrai texte. </p>
+</li>
+</ul>
+<p>Cet article couvrira cette dernière approche.</p>
+<p>Commençons simplement. Nous allons créer une scène 3D avec quelques primitives et ajouter une étiquette à chaque primitive. Nous commencerons
+avec un exemple tiré de <a href="responsive.html">l'article sur les pages responsives</a> </p>
+<p>Nous allons ajouter des <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> comme nous l'avons fait dans <a href="lights.html">l'article sur l'éclairage</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
++import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const controls = new OrbitControls(camera, canvas);
+controls.target.set(0, 0, 0);
+controls.update();
+</pre>
+<p>Nous devons fournir un élément HTML pour contenir nos éléments d'étiquette</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+-  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="container"&gt;
++    &lt;canvas id="c"&gt;&lt;/canvas&gt;
++    &lt;div id="labels"&gt;&lt;/div&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>En plaçant à la fois le canvas et le <code class="notranslate" translate="no">&lt;div id="labels"&gt;</code> à l'intérieur d'un
+conteneur parent, nous pouvons les faire se superposer avec ce CSS</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
+-    width: 100%;
+-    height: 100%;
++    width: 100%;  /* laisser notre conteneur décider de notre taille */
++    height: 100%;
+    display: block;
+}
++#container {
++  position: relative;  /* fait de ceci l'origine de ses enfants */
++  width: 100%;
++  height: 100%;
++  overflow: hidden;
++}
++#labels {
++  position: absolute;  /* nous permet de nous positionner à l'intérieur du conteneur */
++  left: 0;             /* place notre position en haut à gauche du conteneur */
++  top: 0;
++  color: white;
++}
+</pre>
+<p>ajoutons également du CSS pour les étiquettes elles-mêmes</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#labels&gt;div {
+  position: absolute;  /* nous permet de les positionner à l'intérieur du conteneur */
+  left: 0;             /* place leur position par défaut en haut à gauche du conteneur */
+  top: 0;
+  cursor: pointer;     /* change le curseur en main quand la souris est dessus */
+  font-size: large;
+  user-select: none;   /* empêche la sélection du texte */
+  text-shadow:         /* crée un contour noir */
+    -1px -1px 0 #000,
+     0   -1px 0 #000,
+     1px -1px 0 #000,
+     1px  0   0 #000,
+     1px  1px 0 #000,
+     0    1px 0 #000,
+    -1px  1px 0 #000,
+    -1px  0   0 #000;
+}
+#labels&gt;div:hover {
+  color: red;
+}
+</pre>
+<p>Maintenant, dans notre code, nous n'avons pas grand-chose à ajouter. Nous avions une fonction
+<code class="notranslate" translate="no">makeInstance</code> que nous utilisions pour générer des cubes. Faisons en sorte
+qu'elle ajoute également un élément d'étiquette.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const labelContainerElem = document.querySelector('#labels');
+
+-function makeInstance(geometry, color, x) {
++function makeInstance(geometry, color, x, name) {
+  const material = new THREE.MeshPhongMaterial({color});
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  cube.position.x = x;
+
++  const elem = document.createElement('div');
++  elem.textContent = name;
++  labelContainerElem.appendChild(elem);
+
+-  return cube;
++  return {cube, elem};
+}
+</pre>
+<p>Comme vous pouvez le voir, nous ajoutons un <code class="notranslate" translate="no">&lt;div&gt;</code> au conteneur, un pour chaque cube. Nous
+retournons également un objet avec à la fois le <code class="notranslate" translate="no">cube</code> et l'<code class="notranslate" translate="no">elem</code> pour l'étiquette.</p>
+<p>Pour l'appeler, nous devons fournir un nom pour chacun</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
+-  makeInstance(geometry, 0x44aa88,  0),
+-  makeInstance(geometry, 0x8844aa, -2),
+-  makeInstance(geometry, 0xaa8844,  2),
++  makeInstance(geometry, 0x44aa88,  0, 'Aqua'),
++  makeInstance(geometry, 0x8844aa, -2, 'Purple'),
++  makeInstance(geometry, 0xaa8844,  2, 'Gold'),
+];
+</pre>
+<p>Ce qui reste est le positionnement des éléments d'étiquette au moment du rendu</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tempV = new THREE.Vector3();
+
+...
+
+-cubes.forEach((cube, ndx) =&gt; {
++cubes.forEach((cubeInfo, ndx) =&gt; {
++  const {cube, elem} = cubeInfo;
+  const speed = 1 + ndx * .1;
+  const rot = time * speed;
+  cube.rotation.x = rot;
+  cube.rotation.y = rot;
+
++  // obtient la position du centre du cube
++  cube.updateWorldMatrix(true, false);
++  cube.getWorldPosition(tempV);
++
++  // obtient la coordonnée d'écran normalisée de cette position
++  // x et y seront dans la plage de -1 à +1, avec x = -1 étant
++  // à gauche et y = -1 étant en bas
++  tempV.project(camera);
++
++  // convertit la position normalisée en coordonnées CSS
++  const x = (tempV.x *  .5 + .5) * canvas.clientWidth;
++  const y = (tempV.y * -.5 + .5) * canvas.clientHeight;
++
++  // déplace l'élément à cette position
++  elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
+});
+</pre>
+<p>Et avec cela, nous avons des étiquettes alignées sur leurs objets correspondants.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/align-html-to-3d.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/align-html-to-3d.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Il y a quelques problèmes que nous voudrons probablement résoudre.</p>
+<p>L'un d'eux est que si nous faisons pivoter les objets de manière à ce qu'ils se chevauchent, toutes les étiquettes
+se chevauchent également.</p>
+<div class="threejs_center"><img src="../resources/images/overlapping-labels.png" style="width: 307px;"></div>
+
+<p>Un autre est que si nous dézoomons beaucoup, de sorte que les objets sortent
+du frustum, les étiquettes apparaîtront toujours.</p>
+<p>Une solution possible au problème des objets qui se chevauchent est d'utiliser
+le <a href="picking.html">code de sélection (picking) de l'article sur la sélection</a>.
+Nous passerons la position de l'objet à l'écran, puis nous demanderons
+au <code class="notranslate" translate="no">RayCaster</code> de nous dire quels objets ont été intersectés.
+Si notre objet n'est pas le premier, alors il n'est pas à l'avant.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tempV = new THREE.Vector3();
++const raycaster = new THREE.Raycaster();
+
+...
+
+cubes.forEach((cubeInfo, ndx) =&gt; {
+  const {cube, elem} = cubeInfo;
+  const speed = 1 + ndx * .1;
+  const rot = time * speed;
+  cube.rotation.x = rot;
+  cube.rotation.y = rot;
+
+  // obtient la position du centre du cube
+  cube.updateWorldMatrix(true, false);
+  cube.getWorldPosition(tempV);
+
+  // obtient la coordonnée d'écran normalisée de cette position
+  // x et y seront dans la plage de -1 à +1, avec x = -1 étant
+  // à gauche et y = -1 étant en bas
+  tempV.project(camera);
+
++  // demande au raycaster tous les objets qui intersectent
++  // depuis l'œil vers la position de cet objet
++  raycaster.setFromCamera(tempV, camera);
++  const intersectedObjects = raycaster.intersectObjects(scene.children);
++  // Nous sommes visibles si la première intersection est cet objet.
++  const show = intersectedObjects.length &amp;&amp; cube === intersectedObjects[0].object;
++
++  if (!show) {
++    // cache l'étiquette
++    elem.style.display = 'none';
++  } else {
++    // affiche l'étiquette
++    elem.style.display = '';
+
+    // convertit la position normalisée en coordonnées CSS
+    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;
+    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;
+
+    // déplace l'élément à cette position
+    elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
++  }
+});
+</pre>
+<p>Cela gère le chevauchement.</p>
+<p>Pour gérer la sortie du frustum, nous pouvons ajouter cette vérification si l'origine de
+l'objet est en dehors du frustum en vérifiant <code class="notranslate" translate="no">tempV.z</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-  if (!show) {
++  if (!show || Math.abs(tempV.z) &gt; 1) {
+    // cache l'étiquette
+    elem.style.display = 'none';
+</pre>
+<p>Cela fonctionne <em>plus ou moins</em> car les coordonnées normalisées que nous avons calculées incluent une valeur <code class="notranslate" translate="no">z</code>
+qui va de -1 lorsqu'elle est à la partie <code class="notranslate" translate="no">near</code> de notre frustum de caméra à +1 lorsqu'elle est
+à la partie <code class="notranslate" translate="no">far</code> de notre frustum de caméra.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/align-html-to-3d-w-hiding.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/align-html-to-3d-w-hiding.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Pour la vérification du frustum, la solution ci-dessus échoue car nous ne vérifions que l'origine de l'objet. Pour un objet
+volumineux, cette origine pourrait sortir du frustum, mais la moitié de l'objet pourrait encore s'y trouver.</p>
+<p>Une solution plus correcte serait de vérifier si l'objet lui-même est dans le frustum
+ou non. Malheureusement, cette vérification est lente. Pour 3 cubes, ce ne sera pas un problème,
+mais pour de nombreux objets, cela pourrait l'être.</p>
+<p>Three.js fournit quelques fonctions pour vérifier si la sphère englobante d'un objet est
+dans un frustum</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// au moment de l'initialisation
+const frustum = new THREE.Frustum();
+const viewProjection = new THREE.Matrix4();
+
+...
+
+// avant de vérifier
+camera.updateMatrix();
+camera.updateMatrixWorld();
+camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
+
+...
+
+// puis pour chaque maillage
+someMesh.updateMatrix();
+someMesh.updateMatrixWorld();
+
+viewProjection.multiplyMatrices(
+    camera.projectionMatrix, camera.matrixWorldInverse);
+frustum.setFromProjectionMatrix(viewProjection);
+const inFrustum = frustum.contains(someMesh));
+</pre>
+<p>Notre solution actuelle de chevauchement a des problèmes similaires. La sélection est lente. Nous pourrions
+utiliser la sélection basée sur le GPU comme nous l'avons vu dans l'<a href="picking.html">article sur
+la sélection</a>, mais ce n'est pas non plus gratuit. La solution que vous
+choisirez dépend de vos besoins.</p>
+<p>Un autre problème est l'ordre d'apparition des étiquettes. Si nous modifions le code pour avoir
+des étiquettes plus longues</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
+-  makeInstance(geometry, 0x44aa88,  0, 'Aqua'),
+-  makeInstance(geometry, 0x8844aa, -2, 'Purple'),
+-  makeInstance(geometry, 0xaa8844,  2, 'Gold'),
++  makeInstance(geometry, 0x44aa88,  0, 'Boîte Couleur Aqua'),
++  makeInstance(geometry, 0x8844aa, -2, 'Boîte Couleur Violet'),
++  makeInstance(geometry, 0xaa8844,  2, 'Boîte Couleur Or'),
+];
+</pre>
+<p>et définir le CSS de manière à ce qu'elles ne s'enroulent pas (wrap)</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#labels&gt;div {
++  white-space: nowrap;
+</pre>
+<p>Alors nous pouvons rencontrer ce problème</p>
+<div class="threejs_center"><img src="../resources/images/label-sorting-issue.png" style="width: 401px;"></div>
+
+<p>Vous pouvez voir ci-dessus que la boîte violette est à l'arrière, mais son étiquette est devant la boîte aqua.</p>
+<p>Nous pouvons résoudre ce problème en définissant le <code class="notranslate" translate="no">zIndex</code> de chaque élément. La position projetée a une valeur <code class="notranslate" translate="no">z</code>
+qui va de -1 à l'avant à +1 à l'arrière. Le <code class="notranslate" translate="no">zIndex</code> doit être un entier et va dans la direction
+opposée, ce qui signifie que pour le <code class="notranslate" translate="no">zIndex</code>, les valeurs plus grandes sont à l'avant, donc le code suivant devrait fonctionner.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// convertit la position normalisée en coordonnées CSS
+const x = (tempV.x *  .5 + .5) * canvas.clientWidth;
+const y = (tempV.y * -.5 + .5) * canvas.clientHeight;
+
+// déplace l'élément à cette position
+elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
+
++// définit le zIndex pour le tri
++elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;
+</pre>
+<p>En raison de la façon dont fonctionne la valeur z projetée, nous devons choisir un grand nombre pour étaler les valeurs,
+sinon beaucoup auront la même valeur. Pour s'assurer que les étiquettes ne se chevauchent pas avec d'autres parties de
+la page, nous pouvons demander au navigateur de créer un nouveau <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context">contexte d'empilement</a>
+en définissant le <code class="notranslate" translate="no">z-index</code> du conteneur des étiquettes</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#labels {
+  position: absolute;  /* nous permet de nous positionner à l'intérieur du conteneur */
++  z-index: 0;          /* crée un nouveau contexte d'empilement pour que les enfants ne soient pas triés avec le reste de la page */
+  left: 0;             /* place notre position en haut à gauche du conteneur */
+  top: 0;
+  color: white;
+  z-index: 0;
+}
+</pre>
+<p>et maintenant les étiquettes devraient toujours être dans le bon ordre.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/align-html-to-3d-w-sorting.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/align-html-to-3d-w-sorting.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Tant que nous y sommes, faisons un autre exemple pour montrer un problème supplémentaire.
+Dessinons un globe comme Google Maps et étiquetons les pays.</p>
+<p>J'ai trouvé <a href="http://thematicmapping.org/downloads/world_borders.php">ces données</a>
+qui contiennent les frontières des pays. Elles sont sous licence
+<a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>.</p>
+<p>J'<a href="https://github.com/mrdoob/three.js/blob/master/manual/resources/tools/geo-picking/">ai écrit du code</a>
+pour charger les données et générer les contours des pays ainsi que des données JSON avec les noms
+des pays et leurs emplacements.</p>
+<div class="threejs_center"><img src="../examples/resources/data/world/country-outlines-4k.png" style="background: black; width: 700px"></div>
+
+<p>Les données JSON sont un tableau d'entrées ressemblant à ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-json" translate="no">[
+  {
+    "name": "Algeria",
+    "min": [
+      -8.667223,
+      18.976387
+    ],
+    "max": [
+      11.986475,
+      37.091385
+    ],
+    "area": 238174,
+    "lat": 28.163,
+    "lon": 2.632,
+    "population": {
+      "2005": 32854159
+    }
+  },
+  ...
+</pre>
+<p>où min, max, lat, lon sont tous en degrés de latitude et de longitude.</p>
+<p>Chargeons-les. Le code est basé sur les exemples de l'<a href="optimize-lots-of-objects.html">optimisation de nombreux
+objets</a>. Bien que nous ne dessinions pas beaucoup
+d'objets, nous utiliserons les mêmes solutions pour le <a href="rendering-on-demand.html">rendu à la demande</a>.</p>
+<p>La première chose est de créer une sphère et d'utiliser la texture des contours.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);
+  const geometry = new THREE.SphereGeometry(1, 64, 32);
+  const material = new THREE.MeshBasicMaterial({map: texture});
+  scene.add(new THREE.Mesh(geometry, material));
+}
+</pre>
+<p>Ensuite, chargeons le fichier JSON en créant d'abord un chargeur</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">async function loadJSON(url) {
+  const req = await fetch(url);
+  return req.json();
+}
+</pre>
+<p>puis en l'appelant</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">let countryInfos;
+async function loadCountryData() {
+  countryInfos = await loadJSON('resources/data/world/country-info.json');
+     ...
+  }
+  requestRenderIfNotRequested();
+}
+loadCountryData();
+</pre>
+<p>Maintenant, utilisons ces données pour générer et placer les étiquettes.</p>
+<p>Dans l'article sur l'<a href="optimize-lots-of-objects.html">optimisation de nombreux objets</a>,
+nous avions mis en place un petit graphe de scène d'objets auxiliaires pour faciliter le
+calcul des positions de latitude et de longitude sur notre globe. Consultez cet article
+pour une explication de leur fonctionnement.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const lonFudge = Math.PI * 1.5;
+const latFudge = Math.PI;
+// ces helpers (aides) faciliteront le positionnement des boîtes
+// Nous pouvons faire pivoter le lon helper sur son axe Y pour la longitude
+const lonHelper = new THREE.Object3D();
+// Nous faisons pivoter le latHelper sur son axe X pour la latitude
+const latHelper = new THREE.Object3D();
+lonHelper.add(latHelper);
+// Le position helper déplace l'objet vers le bord de la sphère
+const positionHelper = new THREE.Object3D();
+positionHelper.position.z = 1;
+latHelper.add(positionHelper);
+</pre>
+<p>Nous utiliserons cela pour calculer une position pour chaque étiquette</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const labelParentElem = document.querySelector('#labels');
+for (const countryInfo of countryInfos) {
+  const {lat, lon, name} = countryInfo;
+
+  // ajuste les aides pour pointer vers la latitude et la longitude
+  lonHelper.rotation.y = THREE.MathUtils.degToRad(lon) + lonFudge;
+  latHelper.rotation.x = THREE.MathUtils.degToRad(lat) + latFudge;
+
+  // obtient la position de la lat/lon
+  positionHelper.updateWorldMatrix(true, false);
+  const position = new THREE.Vector3();
+  positionHelper.getWorldPosition(position);
+  countryInfo.position = position;
+
+  // ajoute un élément pour chaque pays
+  const elem = document.createElement('div');
+  elem.textContent = name;
+  labelParentElem.appendChild(elem);
+  countryInfo.elem = elem;
+</pre>
+<p>Le code ci-dessus ressemble beaucoup au code que nous avons écrit pour créer les étiquettes de cube,
+créant un élément par étiquette. Lorsque nous avons terminé, nous avons un tableau, <code class="notranslate" translate="no">countryInfos</code>,
+avec une entrée pour chaque pays, à laquelle nous avons ajouté une propriété <code class="notranslate" translate="no">elem</code> pour
+l'élément d'étiquette de ce pays et une <code class="notranslate" translate="no">position</code> avec sa position sur le
+globe.</p>
+<p>Tout comme nous l'avons fait pour les cubes, nous devons mettre à jour la position des
+étiquettes et le temps de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tempV = new THREE.Vector3();
+
+function updateLabels() {
+  // quitte si nous n'avons pas encore chargé le fichier JSON
+  if (!countryInfos) {
+    return;
+  }
+
+  for (const countryInfo of countryInfos) {
+    const {position, elem} = countryInfo;
+
+    // obtient la coordonnée d'écran normalisée de cette position
+    // x et y seront dans la plage de -1 à +1, avec x = -1 étant
+    // à gauche et y = -1 étant en bas
+    tempV.copy(position);
+    tempV.project(camera);
+
+    // convertit la position normalisée en coordonnées CSS
+    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;
+    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;
+
+    // déplace l'élément à cette position
+    elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
+
+    // définit le zIndex pour le tri
+    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;
+  }
+}
+</pre>
+<p>Vous pouvez voir que le code ci-dessus est sensiblement similaire à l'exemple des cubes précédent.
+La seule différence majeure est que nous avons précalculé les positions des étiquettes au moment de l'initialisation.
+Nous pouvons le faire car le globe ne bouge jamais. Seule notre caméra bouge.</p>
+<p>Enfin, nous devons appeler <code class="notranslate" translate="no">updateLabels</code> dans notre boucle de rendu</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
+  renderRequested = false;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+  controls.update();
+
++  updateLabels();
+
+  renderer.render(scene, camera);
+}
+</pre>
+<p>Et voici ce que nous obtenons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/align-html-elements-to-3d-globe-too-many-labels.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/align-html-elements-to-3d-globe-too-many-labels.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Il y a beaucoup trop d'étiquettes !</p>
+<p>Nous avons 2 problèmes.</p>
+<ol>
+<li><p>Les étiquettes qui font face à l'opposé de nous apparaissent.</p>
+</li>
+<li><p>Il y a trop d'étiquettes.</p>
+</li>
+</ol>
+<p>Pour le problème n°1, nous ne pouvons pas vraiment utiliser le <code class="notranslate" translate="no">RayCaster</code> comme nous l'avons fait ci-dessus car il n'y a
+rien à intersecter, à part la sphère. Au lieu de cela, ce que nous pouvons faire est de vérifier si ce
+pays particulier est tourné vers l'opposé de nous ou non. Cela fonctionne car les positions des étiquettes
+sont autour d'une sphère. En fait, nous utilisons une sphère unitaire, une sphère avec
+un rayon de 1,0. Cela signifie que les positions sont déjà des vecteurs unitaires, ce qui
+rend les calculs relativement faciles.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tempV = new THREE.Vector3();
++const cameraToPoint = new THREE.Vector3();
++const cameraPosition = new THREE.Vector3();
++const normalMatrix = new THREE.Matrix3();
+
+function updateLabels() {
+  // quitte si nous n'avons pas encore chargé le fichier JSON
+  if (!countryInfos) {
+    return;
+  }
+
++  const minVisibleDot = 0.2;
++  // obtient une matrice qui représente une orientation relative de la caméra
++  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);
++  // obtient la position de la caméra
++  camera.getWorldPosition(cameraPosition);
+  for (const countryInfo of countryInfos) {
+    const {position, elem} = countryInfo;
+
++    // Oriente la position en fonction de l'orientation de la caméra.
++    // Comme la sphère est à l'origine et que la sphère est une sphère unitaire
++    // cela nous donne un vecteur direction relatif à la caméra pour la position.
++    tempV.copy(position);
++    tempV.applyMatrix3(normalMatrix);
++
++    // calcule la direction vers cette position depuis la caméra
++    cameraToPoint.copy(position);
++    cameraToPoint.applyMatrix4(camera.matrixWorldInverse).normalize();
++
++    // obtient le produit scalaire de la direction relative à la caméra vers cette position
++    // sur le globe avec la direction de la caméra vers ce point.
++    // 1 = fait face directement à la caméra
++    // 0 = exactement tangente à la sphère vue de la caméra
++    // &lt; 0 = fait face à l'opposé
++    const dot = tempV.dot(cameraToPoint);
++
++    // si l'orientation ne nous fait pas face, la cacher.
++    if (dot &lt; minVisibleDot) {
++      elem.style.display = 'none';
++      continue;
++    }
++
++    // restaure le style d'affichage par défaut de l'élément
++    elem.style.display = '';
+
+    // obtient la coordonnée d'écran normalisée de cette position
+    // x et y seront dans la plage de -1 à +1, avec x = -1 étant
+    // à gauche et y = -1 étant en bas
+    tempV.copy(position);
+    tempV.project(camera);
+
+    // convertit la position normalisée en coordonnées CSS
+    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;
+    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;
+
+    // déplace l'élément à cette position
+    countryInfo.elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
+
+    // définit le zIndex pour le tri
+    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;
+  }
+}
+</pre>
+<p>Ci-dessus, nous utilisons les positions comme direction et obtenons cette direction par rapport à la
+caméra. Ensuite, nous obtenons la direction relative à la caméra depuis la caméra vers cette position sur le globe et calculons le <em>produit scalaire</em>. Le produit scalaire renvoie le cosinus
+de l'angle entre les deux vecteurs. Cela nous donne une valeur de -1
+à +1, où -1 signifie que l'étiquette fait face à la caméra, 0 signifie que l'étiquette est exactement
+sur le bord de la sphère par rapport à la caméra, et toute valeur supérieure à zéro est
+derrière. Nous utilisons ensuite cette valeur pour afficher ou masquer l'élément.</p>
+<div class="spread">
+  <div>
+    <div data-diagram="dotProduct" style="height: 400px"></div>
+  </div>
+</div>
+
+<p>Dans le diagramme ci-dessus, nous pouvons voir le produit scalaire de la direction vers laquelle l'étiquette est
+orientée et de la direction de la caméra vers cette position. Si vous faites pivoter la
+direction, vous verrez que le produit scalaire est de -1,0 lorsque la direction est directement
+face à la caméra, il est de 0,0 lorsqu'il est exactement tangent à la sphère par rapport
+à la caméra, ou pour le dire autrement, il est de 0 lorsque les 2 vecteurs sont
+perpendiculaires l'un à l'autre, à 90 degrés. Il est supérieur à zéro lorsque l'étiquette est
+derrière la sphère.</p>
+<p>Pour le problème n°2, trop d'étiquettes, nous avons besoin d'un moyen de décider quelles étiquettes
+afficher. Une façon serait de n'afficher les étiquettes que pour les grands pays.
+Les données que nous chargeons contiennent les valeurs min et max pour la superficie qu'un
+pays couvre. À partir de là, nous pouvons calculer une superficie, puis utiliser cette
+superficie pour décider d'afficher ou non le pays.</p>
+<p>Au moment de l'initialisation, calculons la superficie</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const labelParentElem = document.querySelector('#labels');
+for (const countryInfo of countryInfos) {
+  const {lat, lon, min, max, name} = countryInfo;
+
+  // ajuste les aides pour pointer vers la latitude et la longitude
+  lonHelper.rotation.y = THREE.MathUtils.degToRad(lon) + lonFudge;
+  latHelper.rotation.x = THREE.MathUtils.degToRad(lat) + latFudge;
+
+  // obtient la position de la lat/lon
+  positionHelper.updateWorldMatrix(true, false);
+  const position = new THREE.Vector3();
+  positionHelper.getWorldPosition(position);
+  countryInfo.position = position;
+
++  // calcule la superficie pour chaque pays
++  const width = max[0] - min[0];
++  const height = max[1] - min[1];
++  const area = width * height;
++  countryInfo.area = area;
+
+  // ajoute un élément pour chaque pays
+  const elem = document.createElement('div');
+  elem.textContent = name;
+  labelParentElem.appendChild(elem);
+  countryInfo.elem = elem;
+}
+</pre>
+<p>Puis au moment du rendu, utilisons la superficie pour décider d'afficher l'étiquette
+ou non</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const large = 20 * 20;
+const maxVisibleDot = 0.2;
+// obtient une matrice qui représente une orientation relative de la caméra
+normalMatrix.getNormalMatrix(camera.matrixWorldInverse);
+// obtient la position de la caméra
+camera.getWorldPosition(cameraPosition);
+for (const countryInfo of countryInfos) {
+-  const {position, elem} = countryInfo;
++  const {position, elem, area} = countryInfo;
++  // assez grand ?
++  if (area &lt; large) {
++    elem.style.display = 'none';
++    continue;
++  }
+
+  ...
+</pre>
+<p>Enfin, comme je ne suis pas sûr des bonnes valeurs pour ces paramètres, ajoutons
+une GUI pour que nous puissions jouer avec elles</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
++import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const settings = {
++  minArea: 20,
++  maxVisibleDot: -0.2,
++};
++const gui = new GUI({width: 300});
++gui.add(settings, 'minArea', 0, 50).onChange(requestRenderIfNotRequested);
++gui.add(settings, 'maxVisibleDot', -1, 1, 0.01).onChange(requestRenderIfNotRequested);
+
+function updateLabels() {
+  if (!countryInfos) {
+    return;
+  }
+
+-  const large = 20 * 20;
+-  const maxVisibleDot = -0.2;
++  const large = settings.minArea * settings.minArea;
+  // obtient une matrice qui représente une orientation relative de la caméra
+  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);
+  // obtient la position de la caméra
+  camera.getWorldPosition(cameraPosition);
+  for (const countryInfo of countryInfos) {
+
+    ...
+
+    // si l'orientation ne nous fait pas face, la cacher.
+-    if (dot &gt; maxVisibleDot) {
++    if (dot &gt; settings.maxVisibleDot) {
+      elem.style.display = 'none';
+      continue;
+    }
+</pre>
+<p>et voici le résultat</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/align-html-elements-to-3d-globe.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/align-html-elements-to-3d-globe.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Vous pouvez voir qu'en faisant pivoter la Terre, les étiquettes qui passent derrière disparaissent.
+Ajustez le <code class="notranslate" translate="no">minVisibleDot</code> pour voir le changement de seuil.
+Vous pouvez également ajuster la valeur de <code class="notranslate" translate="no">minArea</code> pour voir apparaître des pays plus grands ou plus petits.</p>
+<p>Plus j'ai travaillé là-dessus, plus j'ai réalisé l'énorme travail
+investi dans Google Maps. Eux aussi doivent décider quelles étiquettes afficher. Je suis à peu près sûr qu'ils utilisent toutes sortes de critères. Par exemple, votre position actuelle, votre paramètre de langue par défaut, les paramètres de votre compte si vous en avez un, ils utilisent probablement la population ou la popularité, ils pourraient donner la priorité aux pays au centre de la vue, etc... Beaucoup de choses à considérer.</p>
+<p>En tout cas, j'espère que ces exemples vous ont donné une idée de la façon d'aligner les éléments HTML
+avec votre 3D. Quelques choses que je pourrais changer.</p>
+<p>Prochaine étape, faisons en sorte que vous puissiez <a href="indexed-textures.html">sélectionner et surligner un pays</a>.</p>
+<p><link rel="stylesheet" href="../resources/threejs-align-html-elements-to-3d.css"></p>
+<script type="module" src="../resources/threejs-align-html-elements-to-3d.js"></script>
 
         </div>
       </div>

+ 173 - 0
manual/fr/animation-system.html

@@ -0,0 +1,173 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Système d'Animation</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Système d'Animation">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Système d'Animation</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+ 
+          <h2>Vue d'ensemble</h2>
+
+		<p class="desc">
+			Au sein du système d'animation de three.js, vous pouvez animer diverses propriétés de vos modèles :
+			les os d'un modèle skinné et riggé, les morph targets, différentes propriétés de matériaux
+			(couleurs, opacité, booléens), la visibilité et les transformations. Les propriétés animées peuvent être introduites en fondu,
+			dissoutes en fondu, fondues enchaînées et déformées. L'influence (weight) et l'échelle temporelle (time scales) de différentes animations simultanées
+			sur le même objet ainsi que sur différents objets peuvent être modifiées
+			indépendamment. Diverses animations sur le même objet et sur différents objets peuvent être
+			synchronisées.<br /><br />
+
+			Pour atteindre tout cela dans un seul système homogène, le système d'animation de three.js
+			[link:https://github.com/mrdoob/three.js/issues/6881 a complètement changé en 2015]
+			(méfiez-vous des informations obsolètes !), et il a maintenant une architecture similaire à
+			Unity/Unreal Engine 4. Cette page donne un bref aperçu des principaux composants du
+			système et de leur fonctionnement ensemble.
+
+		</p>
+
+		<h3>Clips d'Animation</h3>
+
+		<p class="desc">
+
+			Si vous avez importé avec succès un objet 3D animé (peu importe qu'il ait des
+			os, des morph targets, ou les deux) — par exemple en l'exportant depuis Blender avec l'
+			[link:https://github.com/KhronosGroup/glTF-Blender-IO exportateur glTF pour Blender] et
+			en le chargeant dans une scène three.js à l'aide de `GLTFLoader` — l'un des champs de réponse
+			devrait être un tableau nommé "animations", contenant les clips d'animation
+			pour ce modèle (voir une liste des chargeurs possibles ci-dessous).<br /><br />
+
+			Chaque `AnimationClip` contient généralement les données pour une certaine activité de l'objet. Si le
+			mesh est un personnage, par exemple, il peut y avoir un AnimationClip pour un cycle de marche, un second
+			pour un saut, un troisième pour un pas de côté, et ainsi de suite.
+
+		</p>
+
+		<h3>Pistes d'Images Clés</h3>
+
+		<p class="desc">
+
+			À l'intérieur d'un tel `AnimationClip`, les données pour chaque propriété animée sont stockées dans une
+			`KeyframeTrack` séparée. En supposant qu'un objet personnage a un squelette,
+			une piste d'images clés pourrait stocker les données des changements de position de l'os de l'avant-bras
+			au fil du temps, une piste différente les données des changements de rotation du même os, une troisième
+			la position, la rotation ou l'échelle d'un autre os, et ainsi de suite. Il devrait être clair
+			qu'un AnimationClip peut être composé de nombreuses pistes de ce type.<br /><br />
+
+			En supposant que le modèle a des morph targets (par exemple un morph
+			target montrant un visage amical et un autre montrant un visage en colère), chaque piste contient les
+			informations sur la manière dont l'influence d'un certain morph target change pendant la performance
+			du clip.
+
+		</p>
+
+		<h3>Mixeur d'Animation</h3>
+
+		<p class="desc">
+
+			Les données stockées ne forment que la base des animations - la lecture réelle est contrôlée par le
+			`AnimationMixer`. Vous pouvez l'imaginer non seulement comme un lecteur d'animations, mais
+			comme une simulation d'un matériel comme une véritable console de mixage, qui peut contrôler plusieurs animations
+			simultanément, en les mélangeant et en les fusionnant.
+
+		</p>
+
+		<h3>Actions d'Animation</h3>
+
+		<p class="desc">
+
+			Le `AnimationMixer` lui-même n'a que très peu de propriétés et de méthodes (générales), car il
+			peut être contrôlé par les actions d'animation. En configurant une
+			`AnimationAction`, vous pouvez déterminer quand un certain `AnimationClip` doit être lu, mis en pause
+			ou arrêté sur l'un des mixeurs, si et combien de fois le clip doit être répété, s'il
+			doit être exécuté avec un fondu ou une échelle temporelle, et quelques éléments supplémentaires, tels que le fondu enchaîné
+			ou la synchronisation.
+
+		</p>
+
+		<h3>Groupes d'Objets d'Animation</h3>
+
+		<p class="desc">
+
+			Si vous souhaitez qu'un groupe d'objets reçoive un état d'animation partagé, vous pouvez utiliser un
+			`AnimationObjectGroup`.
+
+		</p>
+
+		<h3>Formats et Chargeurs Pris en Charge</h3>
+
+		<p class="desc">
+			Notez que tous les formats de modèle n'incluent pas l'animation (OBJ notamment ne le fait pas), et que seuls certains
+			chargeurs three.js supportent les séquences `AnimationClip`. Plusieurs qui <i>supportent</i>
+			ce type d'animation :
+		</p>
+
+			<ul>
+				<li>THREE.ObjectLoader</li>
+				<li>THREE.BVHLoader</li>
+				<li>THREE.ColladaLoader</li>
+				<li>THREE.FBXLoader</li>
+				<li>THREE.GLTFLoader</li>
+			</ul>
+
+		<p class="desc">
+			Notez que 3ds max et Maya ne peuvent actuellement pas exporter plusieurs animations (c'est-à-dire des animations qui ne sont pas
+			sur la même ligne de temps) directement dans un seul fichier.
+		</p>
+
+		<h2>Exemple</h2>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+let mesh;
+
+// Créer un AnimationMixer, et obtenir la liste des instances de AnimationClip
+const mixer = new THREE.AnimationMixer( mesh );
+const clips = mesh.animations;
+
+// Mettre à jour le mixeur à chaque image
+function update () {
+  mixer.update( deltaSeconds );
+}
+
+// Jouer une animation spécifique
+const clip = THREE.AnimationClip.findByName( clips, 'dance' );
+const action = mixer.clipAction( clip );
+action.play();
+
+// Jouer toutes les animations
+clips.forEach( function ( clip ) {
+  mixer.clipAction( clip ).play();
+} );
+</pre>
+         
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 217 - 5
manual/fr/backgrounds.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Backgrounds and Skyboxes</title>
+    <title>Arrière-plans et Skyboxes</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Backgrounds and Skyboxes">
+    <meta name="twitter:title" content="Three.js – Arrière-plans et Skyboxes">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,224 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Backgrounds and Skyboxes</h1>
+        <h1>Arrière-plans et Skyboxes</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/backgrounds.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>La plupart des articles ici utilisent une couleur unie pour l'arrière-plan.</p>
+<p>Ajouter un arrière-plan statique peut être aussi simple que de définir du CSS. En prenant
+un exemple de <a href="responsive.html">l'article sur comment rendre THREE.js responsive</a>
+nous n'avons besoin de changer que 2 choses.</p>
+<p>Nous devons ajouter du CSS à notre canvas pour définir son arrière-plan comme une image.</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;style&gt;
+body {
+    margin: 0;
+}
+#c {
+    width: 100%;
+    height: 100%;
+    display: block;
++    background: url(resources/images/daikanyama.jpg) no-repeat center center;
++    background-size: cover;
+}
+&lt;/style&gt;
+</pre>
+<p>et nous devons dire au <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a> d'utiliser <code class="notranslate" translate="no">alpha</code> pour que les endroits où nous ne dessinons rien soient transparents.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+-  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
++  const renderer = new THREE.WebGLRenderer({
++    antialias: true,
++    canvas,
++    alpha: true,
++  });
+</pre>
+<p>Et nous obtenons un arrière-plan.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/background-css.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/background-css.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Si nous voulons que l'arrière-plan puisse être affecté par des <a href="post-processing.html">effets de post-traitement</a>
+alors nous devons dessiner l'arrière-plan en utilisant
+THREE.js.</p>
+<p>THREE.js rend cela quelque peu simple. Nous pouvons simplement définir l'arrière-plan de la scène sur
+une texture.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
+const bgTexture = loader.load('resources/images/daikanyama.jpg');
+bgTexture.colorSpace = THREE.SRGBColorSpace;
+scene.background = bgTexture;
+</pre>
+<p>ce qui nous donne</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/background-scene-background.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/background-scene-background.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela nous donne une image de fond, mais elle est étirée pour s'adapter à l'écran.</p>
+<p>Nous pouvons résoudre ce problème en définissant les propriétés <code class="notranslate" translate="no">repeat</code> et <code class="notranslate" translate="no">offset</code> de
+la texture pour n'afficher qu'une partie de l'image.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+
+   ...
+
++  // Définir les propriétés repeat et offset de la texture de fond
++  // pour maintenir l'aspect correct de l'image.
++  // Note : l'image peut ne pas avoir encore été chargée.
++  const canvasAspect = canvas.clientWidth / canvas.clientHeight;
++  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;
++  const aspect = imageAspect / canvasAspect;
++
++  bgTexture.offset.x = aspect &gt; 1 ? (1 - 1 / aspect) / 2 : 0;
++  bgTexture.repeat.x = aspect &gt; 1 ? 1 / aspect : 1;
++
++  bgTexture.offset.y = aspect &gt; 1 ? 0 : (1 - aspect) / 2;
++  bgTexture.repeat.y = aspect &gt; 1 ? 1 : aspect;
+
+  ...
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>et maintenant THREE.js dessine l'arrière-plan. Il n'y a pas de différence visible avec
+la version CSS en haut, mais maintenant si nous utilisions un <a href="post-processing.html">effet de post-traitement</a>
+l'arrière-plan serait également affecté.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/background-scene-background-fixed-aspect.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/background-scene-background-fixed-aspect.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Bien sûr, un arrière-plan statique n'est généralement pas ce que l'on souhaite dans une scène 3D. Au lieu
+de cela, nous voulons généralement une sorte de <em>skybox</em>. Une skybox est exactement cela, une boîte avec le ciel
+dessiné dessus. Nous plaçons la caméra à l'intérieur de la boîte et on dirait qu'il y a un ciel en arrière-plan.</p>
+<p>La manière la plus courante d'implémenter une skybox est de créer un cube, d'y appliquer une texture,
+et de le dessiner depuis l'intérieur. Sur chaque côté du cube, placez une texture (en utilisant
+les coordonnées de texture) qui ressemble à une image de l'horizon. Il est également fréquent
+d'utiliser une sphère ou un dôme céleste avec une texture dessinée dessus. Vous pouvez
+probablement le comprendre par vous-même. Il suffit de faire un cube ou une sphère,
+<a href="textures.html">appliquer une texture</a>, le marquer comme <code class="notranslate" translate="no">THREE.BackSide</code> pour que nous
+rendions l'intérieur au lieu de l'extérieur, et soit de le mettre directement dans votre scène
+comme ci-dessus, soit de créer 2 scènes : une spéciale pour dessiner la skybox/sphère/dôme et la
+scène normale pour dessiner tout le reste. Vous utiliseriez votre <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> normale pour
+dessiner. Pas besoin de la <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
+<p>Une autre solution consiste à utiliser une <em>Cubemap</em>. Une Cubemap est un type de texture
+spécial qui a 6 côtés, les côtés d'un cube. Au lieu d'utiliser des coordonnées de texture
+standard, elle utilise une direction depuis le centre pointant vers l'extérieur pour décider
+où obtenir une couleur.</p>
+<p>Voici les 6 images d'une cubemap provenant du musée d'histoire de l'ordinateur à Mountain
+View, Californie.</p>
+<div class="threejs_center">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/pos-x.jpg" style="width: 200px" class="border">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/neg-x.jpg" style="width: 200px" class="border">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/pos-y.jpg" style="width: 200px" class="border">
+</div>
+<div class="threejs_center">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/neg-y.jpg" style="width: 200px" class="border">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/pos-z.jpg" style="width: 200px" class="border">
+  <img src="../examples/resources/images/cubemaps/computer-history-museum/neg-z.jpg" style="width: 200px" class="border">
+</div>
+
+<p>Pour les utiliser, nous utilisons le <a href="/docs/#api/en/loaders/CubeTextureLoader"><code class="notranslate" translate="no">CubeTextureLoader</code></a> pour les charger, puis nous l'utilisons comme arrière-plan de la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const loader = new THREE.CubeTextureLoader();
+  const texture = loader.load([
+    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',
+    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',
+    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',
+    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',
+    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',
+    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',
+  ]);
+  scene.background = texture;
+}
+</pre>
+<p>Au moment du rendu, nous n'avons pas besoin d'ajuster la texture comme nous l'avons fait ci-dessus</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+
+   ...
+
+-  // Définir les propriétés repeat et offset de la texture de fond
+-  // pour maintenir l'aspect correct de l'image.
+-  // Note : l'image peut ne pas avoir encore été chargée.
+-  const canvasAspect = canvas.clientWidth / canvas.clientHeight;
+-  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;
+-  const aspect = imageAspect / canvasAspect;
+-
+-  bgTexture.offset.x = aspect &gt; 1 ? (1 - 1 / aspect) / 2 : 0;
+-  bgTexture.repeat.x = aspect &gt; 1 ? 1 / aspect : 1;
+-
+-  bgTexture.offset.y = aspect &gt; 1 ? 0 : (1 - aspect) / 2;
+-  bgTexture.repeat.y = aspect &gt; 1 ? 1 : aspect;
+
+  ...
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Ajoutons des contrôles pour pouvoir faire pivoter la caméra.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
+const aspect = 2;  // the canvas default
+const near = 0.1;
+-const far = 5;
++const far = 100;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+-camera.position.z = 2;
++camera.position.z = 3;
+
++const controls = new OrbitControls(camera, canvas);
++controls.target.set(0, 0, 0);
++controls.update();
+</pre>
+<p>et essayez-le. Faites glisser l'exemple pour faire pivoter la caméra et voir la cubemap nous entourer.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/background-cubemap.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/background-cubemap.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Une autre option consiste à utiliser une carte équirectangulaire. C'est le type d'image qu'une <a href="https://google.com/search?q=360+camera">caméra 360</a> prend.</p>
+<p><a href="https://hdrihaven.com/hdri/?h=tears_of_steel_bridge">En voici une</a> que j'ai trouvée sur <a href="https://hdrihaven.com">ce site</a>.</p>
+<div class="threejs_center"><img src="../examples/resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg" style="width: 600px"></div>
+
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+-  const loader = new THREE.CubeTextureLoader();
+-  const texture = loader.load([
+-    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',
+-    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',
+-    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',
+-  ]);
+-  scene.background = texture;
++  const loader = new THREE.TextureLoader();
++  const texture = loader.load(
++    'resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg',
++    () =&gt; {
++      texture.mapping = THREE.EquirectangularReflectionMapping;
++      texture.colorSpace = THREE.SRGBColorSpace;
++      scene.background = texture;
++    });
+}
+</pre>
+<p>Et c'est tout ce qu'il y a à faire.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/background-equirectangularmap.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/background-equirectangularmap.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Plutôt que de le faire au moment du chargement, vous pouvez également convertir une image équirectangulaire
+en cubemap au préalable. <a href="https://matheowis.github.io/HDRI-to-CubeMap/">Voici un site qui le fera pour vous</a>.</p>
 
         </div>
       </div>

+ 283 - 5
manual/fr/billboards.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Billboards</title>
+    <title>Panneaux</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Billboards">
+    <meta name="twitter:title" content="Three.js – Panneaux">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,290 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Billboards</h1>
+        <h1>Panneaux</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/billboards.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Dans <a href="canvas-textures.html">un article précédent</a> nous avons utilisé une <a href="/docs/#api/en/textures/CanvasTexture"><code class="notranslate" translate="no">CanvasTexture</code></a>
+pour créer des étiquettes / badges sur les personnages. Parfois, nous aimerions créer des étiquettes ou
+d'autres éléments qui font toujours face à la caméra. Three.js fournit le <a href="/docs/#api/en/objects/Sprite"><code class="notranslate" translate="no">Sprite</code></a> et le
+<a href="/docs/#api/en/materials/SpriteMaterial"><code class="notranslate" translate="no">SpriteMaterial</code></a> pour y parvenir.</p>
+<p>Modifions l'exemple de badge de <a href="canvas-textures.html">l'article sur les textures de canevas</a>
+pour utiliser <a href="/docs/#api/en/objects/Sprite"><code class="notranslate" translate="no">Sprite</code></a> et le <a href="/docs/#api/en/materials/SpriteMaterial"><code class="notranslate" translate="no">SpriteMaterial</code></a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makePerson(x, labelWidth, size, name, color) {
+  const canvas = makeLabelCanvas(labelWidth, size, name);
+  const texture = new THREE.CanvasTexture(canvas);
+  // car notre canevas n'est probablement pas une puissance de 2
+  // dans les deux dimensions, définissez le filtrage de manière appropriée.
+  texture.minFilter = THREE.LinearFilter;
+  texture.wrapS = THREE.ClampToEdgeWrapping;
+  texture.wrapT = THREE.ClampToEdgeWrapping;
+
+-  const labelMaterial = new THREE.MeshBasicMaterial({
++  const labelMaterial = new THREE.SpriteMaterial({
+    map: texture,
+-    side: THREE.DoubleSide,
+    transparent: true,
+  });
+
+  const root = new THREE.Object3D();
+  root.position.x = x;
+
+  const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
+  root.add(body);
+  body.position.y = bodyHeight / 2;
+
+  const head = new THREE.Mesh(headGeometry, bodyMaterial);
+  root.add(head);
+  head.position.y = bodyHeight + headRadius * 1.1;
+
+-  const label = new THREE.Mesh(labelGeometry, labelMaterial);
++  const label = new THREE.Sprite(labelMaterial);
+  root.add(label);
+  label.position.y = bodyHeight * 4 / 5;
+  label.position.z = bodyRadiusTop * 1.01;
+</pre>
+<p>et les étiquettes font maintenant toujours face à la caméra</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/billboard-labels-w-sprites.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/billboard-labels-w-sprites.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Un problème est que, sous certains angles, les étiquettes intersectent maintenant les
+personnages. </p>
+<div class="threejs_center"><img src="../resources/images/billboard-label-z-issue.png" style="width: 455px;"></div>
+
+<p>Nous pouvons déplacer la position des étiquettes pour corriger cela.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+// si les unités sont des mètres, alors 0.01 ici donne une taille
++// de l'étiquette en centimètres.
++const labelBaseScale = 0.01;
+const label = new THREE.Sprite(labelMaterial);
+root.add(label);
+-label.position.y = bodyHeight * 4 / 5;
+-label.position.z = bodyRadiusTop * 1.01;
++label.position.y = head.position.y + headRadius + size * labelBaseScale;
+
+-// si les unités sont des mètres, alors 0.01 ici donne une taille
+-// de l'étiquette en centimètres.
+-const labelBaseScale = 0.01;
+label.scale.x = canvas.width  * labelBaseScale;
+label.scale.y = canvas.height * labelBaseScale;
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/billboard-labels-w-sprites-adjust-height.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/billboard-labels-w-sprites-adjust-height.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Une autre chose que nous pouvons faire avec les panneaux d'affichage est de dessiner des façades.</p>
+<p>Au lieu de dessiner des objets 3D, nous dessinons des plans 2D avec une image
+d'objets 3D. C'est souvent plus rapide que de dessiner des objets 3D.</p>
+<p>Par exemple, créons une scène avec une grille d'arbres. Nous allons créer chaque
+arbre à partir d'un cylindre pour la base et d'un cône pour le sommet.</p>
+<p>Nous créons d'abord la géométrie du cône et du cylindre ainsi que les matériaux que
+tous les arbres partageront</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const trunkRadius = .2;
+const trunkHeight = 1;
+const trunkRadialSegments = 12;
+const trunkGeometry = new THREE.CylinderGeometry(
+    trunkRadius, trunkRadius, trunkHeight, trunkRadialSegments);
+
+const topRadius = trunkRadius * 4;
+const topHeight = trunkHeight * 2;
+const topSegments = 12;
+const topGeometry = new THREE.ConeGeometry(
+    topRadius, topHeight, topSegments);
+
+const trunkMaterial = new THREE.MeshPhongMaterial({color: 'brown'});
+const topMaterial = new THREE.MeshPhongMaterial({color: 'green'});
+</pre>
+<p>Ensuite, nous allons créer une fonction qui crée un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>
+chacun pour le tronc et le sommet d'un arbre
+et les parentent à un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeTree(x, z) {
+  const root = new THREE.Object3D();
+  const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
+  trunk.position.y = trunkHeight / 2;
+  root.add(trunk);
+
+  const top = new THREE.Mesh(topGeometry, topMaterial);
+  top.position.y = trunkHeight + topHeight / 2;
+  root.add(top);
+
+  root.position.set(x, 0, z);
+  scene.add(root);
+
+  return root;
+}
+</pre>
+<p>Ensuite, nous allons créer une boucle pour placer une grille d'arbres.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (let z = -50; z &lt;= 50; z += 10) {
+  for (let x = -50; x &lt;= 50; x += 10) {
+    makeTree(x, z);
+  }
+}
+</pre>
+<p>Ajoutons également un plan de sol tant que nous y sommes</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// ajouter le sol
+{
+  const size = 400;
+  const geometry = new THREE.PlaneGeometry(size, size);
+  const material = new THREE.MeshPhongMaterial({color: 'gray'});
+  const mesh = new THREE.Mesh(geometry, material);
+  mesh.rotation.x = Math.PI * -0.5;
+  scene.add(mesh);
+}
+</pre>
+<p>et changeons l'arrière-plan en bleu clair</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+-scene.background = new THREE.Color('white');
++scene.background = new THREE.Color('lightblue');
+</pre>
+<p>et nous obtenons une grille d'arbres</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/billboard-trees-no-billboards.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/billboard-trees-no-billboards.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Il y a 11x11 ou 121 arbres. Chaque arbre est constitué d'un cône de 12 polygones
+et d'un tronc de 48 polygones, donc chaque arbre fait 60 polygones. 121 * 60
+= 7260 polygones. Ce n'est pas énorme, mais bien sûr, un arbre 3D plus détaillé
+pourrait avoir entre 1000 et 3000 polygones. S'ils avaient 3000 polygones chacun,
+alors 121 arbres représenteraient 363000 polygones à dessiner.</p>
+<p>En utilisant des façades, nous pouvons réduire ce nombre.</p>
+<p>Nous pourrions créer manuellement une façade dans un logiciel de dessin, mais écrivons
+du code pour essayer d'en générer une.</p>
+<p>Écrivons du code pour rendre un objet dans une texture
+en utilisant un <code class="notranslate" translate="no">RenderTarget</code>. Nous avons abordé le rendu vers une <code class="notranslate" translate="no">RenderTarget</code>
+dans <a href="rendertargets.html">l'article sur les cibles de rendu</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
+  const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
+  const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);
+  const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
+
+  camera.position.copy(boxCenter);
+  camera.position.z += distance;
+
+  // choisir des valeurs proches et éloignées pour le frustum qui
+  // contiendra la boîte.
+  camera.near = boxSize / 100;
+  camera.far = boxSize * 100;
+
+  camera.updateProjectionMatrix();
+}
+
+function makeSpriteTexture(textureSize, obj) {
+  const rt = new THREE.WebGLRenderTarget(textureSize, textureSize);
+
+  const aspect = 1;  // car la cible de rendu est carrée
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+
+  scene.add(obj);
+
+  // calculer la boîte qui contient obj
+  const box = new THREE.Box3().setFromObject(obj);
+
+  const boxSize = box.getSize(new THREE.Vector3());
+  const boxCenter = box.getCenter(new THREE.Vector3());
+
+  // régler la caméra pour cadrer la boîte
+  const fudge = 1.1;
+  const size = Math.max(...boxSize.toArray()) * fudge;
+  frameArea(size, size, boxCenter, camera);
+
+  renderer.autoClear = false;
+  renderer.setRenderTarget(rt);
+  renderer.render(scene, camera);
+  renderer.setRenderTarget(null);
+  renderer.autoClear = true;
+
+  scene.remove(obj);
+
+  return {
+    position: boxCenter.multiplyScalar(fudge),
+    scale: size,
+    texture: rt.texture,
+  };
+}
+</pre>
+<p>Quelques points à noter concernant le code ci-dessus :</p>
+<p>Nous utilisons le champ de vision (<code class="notranslate" translate="no">fov</code>) défini au-dessus de ce code.</p>
+<p>Nous calculons une boîte qui contient l'arbre de la même manière
+que nous l'avons fait dans <a href="load-obj.html">l'article sur le chargement d'un fichier .obj</a>
+avec quelques modifications mineures.</p>
+<p>Nous appelons <code class="notranslate" translate="no">frameArea</code> à nouveau, adapté de <a href="load-obj.html">l'article sur le chargement d'un fichier .obj</a>.
+Dans ce cas, nous calculons à quelle distance la caméra doit se trouver de l'objet,
+compte tenu de son champ de vision, pour contenir l'objet. Nous positionnons ensuite la caméra en -z à cette distance
+du centre de la boîte qui contient l'objet.</p>
+<p>Nous multiplions la taille que nous voulons ajuster par 1,1 (<code class="notranslate" translate="no">fudge</code>) pour nous assurer que l'arbre rentre
+complètement dans la cible de rendu. Le problème ici est que la taille que nous utilisons pour
+calculer si l'objet rentre dans la vue de la caméra ne prend pas en compte
+que les bords mêmes de l'objet finiront par sortir de la zone que nous avons calculée.
+Nous pourrions calculer comment faire rentrer 100% de la boîte, mais cela gaspillerait aussi de l'espace,
+alors au lieu de cela, nous 'truquons' un peu.</p>
+<p>Ensuite, nous rendons sur la cible de rendu et retirons l'objet de
+la scène. </p>
+<p>Il est important de noter que nous avons besoin des lumières dans la scène, mais nous
+devons nous assurer que rien d'autre n'est dans la scène.</p>
+<p>Nous devons également ne pas définir de couleur d'arrière-plan sur la scène</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+-scene.background = new THREE.Color('lightblue');
+</pre>
+<p>Enfin, nous avons créé la texture, nous la renvoyons ainsi que la position et l'échelle
+dont nous avons besoin pour créer la façade afin qu'elle apparaisse au même endroit.</p>
+<p>Nous créons ensuite un arbre et appelons ce code en le lui passant</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// créer la texture du panneau d'affichage
+const tree = makeTree(0, 0);
+const facadeSize = 64;
+const treeSpriteInfo = makeSpriteTexture(facadeSize, tree);
+</pre>
+<p>Nous pouvons ensuite créer une grille de façades au lieu d'une grille de modèles d'arbres</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function makeSprite(spriteInfo, x, z) {
++  const {texture, offset, scale} = spriteInfo;
++  const mat = new THREE.SpriteMaterial({
++    map: texture,
++    transparent: true,
++  });
++  const sprite = new THREE.Sprite(mat);
++  scene.add(sprite);
++  sprite.position.set(
++      offset.x + x,
++      offset.y,
++      offset.z + z);
++  sprite.scale.set(scale, scale, scale);
++}
+
+for (let z = -50; z &lt;= 50; z += 10) {
+  for (let x = -50; x &lt;= 50; x += 10) {
+-    makeTree(x, z);
++    makeSprite(treeSpriteInfo, x, z);
+  }
+}
+</pre>
+<p>Dans le code ci-dessus, nous appliquons le décalage et l'échelle nécessaires pour positionner la façade afin qu'elle
+apparaisse au même endroit où l'arbre d'origine aurait apparu.</p>
+<p>Maintenant que nous avons terminé de créer la texture de la façade de l'arbre, nous pouvons à nouveau définir l'arrière-plan</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.background = new THREE.Color('lightblue');
+</pre>
+<p>et maintenant nous obtenons une scène de façades d'arbres</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/billboard-trees-static-billboards.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/billboard-trees-static-billboards.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Comparez avec les modèles d'arbres ci-dessus et vous pouvez voir que l'apparence est assez similaire.
+Nous avons utilisé une texture basse résolution, seulement 64x64 pixels, donc les façades sont pixelisées.
+Vous pourriez augmenter la résolution. Souvent, les façades ne sont utilisées qu'à grande distance lorsqu'elles sont assez petites,
+donc une texture basse résolution est suffisante et cela permet d'éviter de dessiner des arbres détaillés qui ne font
+que quelques pixels de taille lorsqu'ils sont loin.</p>
+<p>Un autre problème est que nous ne voyons l'arbre que d'un côté.
+Cela est souvent résolu en rendant plus de façades, par exemple depuis 8 directions autour de l'objet,
+puis en définissant quelle façade afficher en fonction de la direction depuis laquelle la caméra regarde la façade.</p>
+<p>Que vous utilisiez des façades ou non, cela dépend de vous, mais j'espère que cet article
+vous a donné des idées et suggéré des solutions si vous décidez de les utiliser.</p>
 
         </div>
       </div>

+ 180 - 95
manual/fr/cameras.html

@@ -26,27 +26,39 @@
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là.</p>
-<p>Parlons des caméras dans Three.js. Nous en avons déjà parlé dans <a href="fundamentals.html">le premier article</a> mais ici nous allons entrer dans le détail.</p>
-<p>La caméra la plus courante dans Three.js et celle que nous avons utilisée jusqu'à présent, la <a href="https://threejs.org/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Elle donne une vue 3D où les choses lointaines semblent plus petites que les plus proches.</p>
-<p>La <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit un <em>frustum</em>. <a href="https://fr.wikipedia.org/wiki/Tronc_(g%C3%A9om%C3%A9trie">Un frustum (tronc ou pyramide tronquée) est une forme pyramidale solide dont la pointe est coupée</a>). Par nom de solide, j'entends par exemple un cube, un cône, une sphère, un cylindre et un frustum sont tous des noms de différents types de solides.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js.
+Le premier article traitait <a href="fundamentals.html">des bases</a>.
+Si vous ne l'avez pas encore lu, vous pourriez vouloir commencer par là.</p>
+<p>Parlons des caméras dans three.js. Nous avons abordé une partie de cela dans le <a href="fundamentals.html">premier article</a>, mais nous allons le couvrir plus en détail ici.</p>
+<p>La caméra la plus courante dans three.js, et celle que nous avons utilisée jusqu'à présent, est
+la <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Elle donne une vue 3D où les objets au loin apparaissent
+plus petits que les objets proches.</p>
+<p>La <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit un <em>frustum</em>. <a href="https://en.wikipedia.org/wiki/Frustum">Un <em>frustum</em> est une forme pyramidale solide dont l'extrémité est coupée</a>.
+Par nom de solide, j'entends par exemple qu'un cube, un cône, une sphère, un cylindre
+et un frustum sont tous des noms de différents types de solides.</p>
 <div class="spread">
   <div><div data-diagram="shapeCube"></div><div>cube</div></div>
-  <div><div data-diagram="shapeCone"></div><div>cone</div></div>
-  <div><div data-diagram="shapeSphere"></div><div>sphere</div></div>
-  <div><div data-diagram="shapeCylinder"></div><div>cylinder</div></div>
+  <div><div data-diagram="shapeCone"></div><div>cône</div></div>
+  <div><div data-diagram="shapeSphere"></div><div>sphère</div></div>
+  <div><div data-diagram="shapeCylinder"></div><div>cylindre</div></div>
   <div><div data-diagram="shapeFrustum"></div><div>frustum</div></div>
 </div>
 
-<p>Je le signale seulement parce que je ne le savais pas. Et quand je voyais le mot <em>frustum</em> dans un livre mes yeux buggaient. Comprendre que c'est le nom d'un type de forme solide a rendu ces descriptions soudainement plus logiques 😅</p>
-<p>Une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit son frustum selon 4 propriétés. <code class="notranslate" translate="no">near</code> définit l'endroit où commence l'avant du frustum. <code class="notranslate" translate="no">far</code> où il finit. <code class="notranslate" translate="no">fov</code>, le champ de vision, définit la hauteur de l'avant et de l'arrière du tronc en fonction de la propriété <code class="notranslate" translate="no">near</code>. L'<code class="notranslate" translate="no">aspect</code> se rapporte à la largeur de l'avant et de l'arrière du tronc. La largeur du tronc est juste la hauteur multipliée par l'<code class="notranslate" translate="no">aspect</code>.</p>
+<p>Je ne le signale que parce que je ne le savais pas pendant des années. Un livre ou une page mentionnait
+<em>frustum</em> et mes yeux se voilaient. Comprendre que c'est le nom d'un type de forme solide
+a rendu ces descriptions soudainement plus logiques 😅</p>
+<p>Une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit son frustum en fonction de 4 propriétés. <code class="notranslate" translate="no">near</code> définit où
+commence l'avant du frustum. <code class="notranslate" translate="no">far</code> définit où il se termine. <code class="notranslate" translate="no">fov</code>, le champ de vision, définit
+la hauteur de l'avant et de l'arrière du frustum en calculant la hauteur correcte pour obtenir
+le champ de vision spécifié à <code class="notranslate" translate="no">near</code> unités de la caméra. L'<code class="notranslate" translate="no">aspect</code> définit
+la largeur de l'avant et de l'arrière du frustum. La largeur du frustum est simplement la hauteur
+multipliée par l'aspect.</p>
 <p><img src="../resources/frustum-3d.svg" width="500" class="threejs_center"></p>
-<p>Utilisons la scène de <a href="lights.html">l'article précédent</a> avec son plan, sa sphère et son cube, et faisons en sorte que nous puissions ajuster les paramètres de la caméra.</p>
-<p>Pour ce faire, nous allons créer un <code class="notranslate" translate="no">MinMaxGUIHelper</code> pour les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> où <code class="notranslate" translate="no">far</code>
-est toujours supérieur <code class="notranslate" translate="no">near</code>. Il aura des propriétés <code class="notranslate" translate="no">min</code> et <code class="notranslate" translate="no">max</code> que lil-gui
-pourra ajuster. Une fois ajustés, ils définiront les 2 propriétés que nous spécifions.</p>
+<p>Utilisons la scène de <a href="lights.html">l'article précédent</a> qui contient un plan au sol,
+une sphère et un cube, et faisons en sorte de pouvoir ajuster les paramètres de la caméra.</p>
+<p>Pour ce faire, nous allons créer un <code class="notranslate" translate="no">MinMaxGUIHelper</code> pour les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> afin que <code class="notranslate" translate="no">far</code>
+soit toujours supérieur à <code class="notranslate" translate="no">near</code>. Il aura des propriétés <code class="notranslate" translate="no">min</code> et <code class="notranslate" translate="no">max</code> que lil-gui
+ajustera. Lorsqu'elles seront ajustées, elles définiront les 2 propriétés que nous spécifions.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class MinMaxGUIHelper {
   constructor(obj, minProp, maxProp, minDif) {
     this.obj = obj;
@@ -66,11 +78,11 @@ pourra ajuster. Une fois ajustés, ils définiront les 2 propriétés que nous s
   }
   set max(v) {
     this.obj[this.maxProp] = v;
-    this.min = this.min;  // this will call the min setter
+    this.min = this.min;  // ceci appellera le setter de min
   }
 }
 </pre>
-<p>Maintenant, nous pouvons configurer lil-gui comme ça</p>
+<p>Maintenant, nous pouvons configurer notre interface graphique comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateCamera() {
   camera.updateProjectionMatrix();
 }
@@ -81,18 +93,25 @@ const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
 gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
 gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
 </pre>
-<p>Chaque fois que les paramètres de la caméra changent, il faut appeler la fonction
-<a href="/docs/#api/en/cameras/PerspectiveCamera#updateProjectionMatrix"><code class="notranslate" translate="no">updateProjectionMatrix</code></a>. Nous avons donc créé une fonction <code class="notranslate" translate="no">updateCamera</code> transmise à lil-gui pour l'appeler lorsque les choses changent.</p>
+<p>Chaque fois que les paramètres de la caméra changent, nous devons appeler la fonction
+<a href="/docs/#api/en/cameras/PerspectiveCamera#updateProjectionMatrix"><code class="notranslate" translate="no">updateProjectionMatrix</code></a> de la caméra.
+Nous avons donc créé une fonction nommée <code class="notranslate" translate="no">updateCamera</code> et l'avons passée à lil-gui pour qu'elle soit appelée lorsque les choses changent.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-perspective.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-perspective.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-perspective.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Vous pouvez ajuster les valeurs et voir comment elles fonctionnent. Notez que nous n'avons pas rendu <code class="notranslate" translate="no">aspect</code> réglable car il est pris à partir de la taille de la fenêtre, donc si vous souhaitez ajuster l'aspect, ouvrez l'exemple dans une nouvelle fenêtre, puis redimensionnez la fenêtre.</p>
-<p>Néanmoins, je pense que c'est un peu difficile à voir, alors modifions l'exemple pour qu'il ait 2 caméras. L'un montrera notre scène telle que nous la voyons ci-dessus, l'autre montrera une autre caméra regardant la scène que la première caméra dessine et montrant le frustum de cette caméra.</p>
-<p>Pour ce faire, nous pouvons utiliser la fonction ciseaux de three.js. Modifions-le pour dessiner 2 scènes avec 2 caméras côte à côte en utilisant la fonction ciseaux.</p>
-<p>Tout d'abord, utilisons du HTML et du CSS pour définir 2 éléments côte à côte. Cela nous aidera également avec les événements afin que les deux caméras puissent facilement avoir leurs propres <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
+<p>Vous pouvez ajuster les valeurs et voir comment elles fonctionnent. Notez que nous n'avons pas rendu l'<code class="notranslate" translate="no">aspect</code> modifiable car
+il est tiré de la taille de la fenêtre. Donc, si vous voulez ajuster l'aspect, ouvrez l'exemple
+dans une nouvelle fenêtre et redimensionnez-la.</p>
+<p>Néanmoins, je pense que c'est un peu difficile à voir, alors changeons l'exemple pour qu'il comporte 2 caméras.
+L'une montrera notre scène telle que nous la voyons ci-dessus, l'autre montrera une autre caméra regardant la
+scène que la première caméra dessine et affichant le frustum de cette caméra.</p>
+<p>Pour ce faire, nous pouvons utiliser la fonction scissor de three.js.
+Changeons-le pour dessiner 2 scènes avec 2 caméras côte à côte en utilisant la fonction scissor.</p>
+<p>Tout d'abord, utilisons du HTML et du CSS pour définir 2 éléments côte à côte. Cela nous
+aidera également avec les événements afin que les deux caméras puissent facilement avoir leurs propres <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
   &lt;canvas id="c"&gt;&lt;/canvas&gt;
 +  &lt;div class="split"&gt;
@@ -101,7 +120,8 @@ gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera)
 +  &lt;/div&gt;
 &lt;/body&gt;
 </pre>
-<p>Et le CSS qui fera apparaître ces 2 vues côte à côte sur le canevas</p>
+<p>Et le CSS qui fera apparaître ces 2 vues côte à côte superposées sur
+le canvas</p>
 <pre class="prettyprint showlinemods notranslate lang-css" translate="no">.split {
   position: absolute;
   left: 0;
@@ -115,23 +135,25 @@ gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera)
   height: 100%;
 }
 </pre>
-<p>Ensuite, ajoutons un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a>. Un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> dessine le frustum d'une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a>.</p>
+<p>Puis dans notre code, nous ajouterons un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a>. Un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> dessine le frustum d'une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameraHelper = new THREE.CameraHelper(camera);
 
 ...
 
 scene.add(cameraHelper);
 </pre>
-<p>Récupérons maintenant nos 2 éléments.</p>
+<p>Maintenant, cherchons les 2 éléments de vue.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const view1Elem = document.querySelector('#view1');
 const view2Elem = document.querySelector('#view2');
 </pre>
-<p>Et nous allons configurer nos <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> pour qu'ils répondent uniquement au premier élément.</p>
+<p>Et nous configurerons nos <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> existants pour qu'ils ne répondent qu'au premier
+élément de vue.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const controls = new OrbitControls(camera, canvas);
 +const controls = new OrbitControls(camera, view1Elem);
 </pre>
-<p>Ajoutons une nouvelle <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> et un second <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.
-Le deuxième <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> est lié à la deuxième caméra et reçoit view2Elem en paramètre.</p>
+<p>Créons une deuxième <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> et un deuxième <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.
+Le deuxième <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> est lié à la deuxième caméra et reçoit l'entrée
+du deuxième élément de vue.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera2 = new THREE.PerspectiveCamera(
   60,  // fov
   2,   // aspect
@@ -145,8 +167,11 @@ const controls2 = new OrbitControls(camera2, view2Elem);
 controls2.target.set(0, 5, 0);
 controls2.update();
 </pre>
-<p>Enfin, nous devons rendre la scène du point de vue de chaque caméra en utilisant la fonction <code class="notranslate" translate="no">setScissor</code> pour ne rendre qu'une partie du canvas.</p>
-<p>Voici une fonction qui, étant donné un élément, calculera le rectangle de cet élément qui chevauche le canvas. Il définira ensuite les ciseaux et la fenêtre sur ce rectangle et renverra l'aspect pour cette taille.</p>
+<p>Enfin, nous devons rendre la scène du point de vue de chaque
+caméra en utilisant la fonction scissor pour ne rendre qu'une partie du canvas.</p>
+<p>Voici une fonction qui, étant donné un élément, calculera le rectangle
+de cet élément qui chevauche le canvas. Elle définira ensuite le scissor
+et le viewport sur ce rectangle et renverra l'aspect pour cette taille.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function setScissorForElement(elem) {
   const canvasRect = canvas.getBoundingClientRect();
   const elemRect = elem.getBoundingClientRect();
@@ -160,16 +185,16 @@ controls2.update();
   const width = Math.min(canvasRect.width, right - left);
   const height = Math.min(canvasRect.height, bottom - top);
 
-  // configurer les ciseaux pour ne rendre que cette partie du canvas
+  // configurer le scissor pour ne rendre que cette partie du canvas
   const positiveYUpBottom = canvasRect.height - bottom;
   renderer.setScissor(left, positiveYUpBottom, width, height);
   renderer.setViewport(left, positiveYUpBottom, width, height);
 
-  // retourne aspect
+  // retourner l'aspect
   return width / height;
 }
 </pre>
-<p>Et maintenant, nous pouvons utiliser cette fonction pour dessiner la scène deux fois dans notre fonction <code class="notranslate" translate="no">render</code></p>
+<p>Et maintenant, nous pouvons utiliser cette fonction pour dessiner la scène deux fois dans notre fonction <code class="notranslate" translate="no">render</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">  function render() {
 
 -    if (resizeRendererToDisplaySize(renderer)) {
@@ -180,36 +205,36 @@ controls2.update();
 
 +    resizeRendererToDisplaySize(renderer);
 +
-+    // déclenche la fonction setScissorTest
++    // activer le scissor
 +    renderer.setScissorTest(true);
 +
-+    // rend la vue originelle
++    // rendre la vue originale
 +    {
 +      const aspect = setScissorForElement(view1Elem);
 +
-+      // ajuste la caméra pour cet aspect
++      // ajuster la caméra pour cet aspect
 +      camera.aspect = aspect;
 +      camera.updateProjectionMatrix();
 +      cameraHelper.update();
 +
-+      // ne pas ajouter le camera helper dans la vue originelle
++      // ne pas dessiner l'helper de caméra dans la vue originale
 +      cameraHelper.visible = false;
 +
 +      scene.background.set(0x000000);
 +
-+      // rendu
++      // rendre
 +      renderer.render(scene, camera);
 +    }
 +
-+    // rendu de la 2e caméra
++    // rendre depuis la 2ème caméra
 +    {
 +      const aspect = setScissorForElement(view2Elem);
 +
-+      // ajuste la caméra
++      // ajuster la caméra pour cet aspect
 +      camera2.aspect = aspect;
 +      camera2.updateProjectionMatrix();
 +
-+      // camera helper dans la 2e vue
++      // dessiner l'helper de caméra dans la 2ème vue
 +      cameraHelper.visible = true;
 +
 +      scene.background.set(0x000040);
@@ -225,8 +250,10 @@ controls2.update();
   requestAnimationFrame(render);
 }
 </pre>
-<p>Le code ci-dessus définit la couleur d'arrière-plan de la scène lors du rendu de la deuxième vue en bleu foncé juste pour faciliter la distinction des deux vues.</p>
-<p>Nous pouvons également supprimer notre code <code class="notranslate" translate="no">updateCamera</code> puisque nous mettons tout à jour dans la fonction <code class="notranslate" translate="no">render</code>.</p>
+<p>Le code ci-dessus définit la couleur de fond de la scène lors du rendu de la
+deuxième vue sur bleu foncé juste pour faciliter la distinction entre les deux vues.</p>
+<p>Nous pouvons également supprimer notre code <code class="notranslate" translate="no">updateCamera</code> puisque nous mettons tout à jour
+dans la fonction <code class="notranslate" translate="no">render</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function updateCamera() {
 -  camera.updateProjectionMatrix();
 -}
@@ -243,14 +270,27 @@ const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
 <p>Et maintenant, vous pouvez utiliser une vue pour voir le frustum de l'autre.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-perspective-2-scenes.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-perspective-2-scenes.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-perspective-2-scenes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Sur la gauche, vous pouvez voir la vue d'origine et sur la droite, vous pouvez voir une vue montrant le frustum sur la gauche. Lorsque vous ajustez <code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code>, <code class="notranslate" translate="no">fov</code> et déplacez la caméra avec la souris, vous pouvez voir que seul ce qui se trouve à l'intérieur du frustum montré à droite apparaît dans la scène à gauche.</p>
-<p>Ajustez <code class="notranslate" translate="no">near</code> d'environ 20 et vous verrez facilement le devant des objets disparaître car ils ne sont plus dans le tronc. Ajustez <code class="notranslate" translate="no">far</code> en dessous de 35 et vous commencerez à voir le sol disparaître car il n'est plus dans le tronc.</p>
-<p>Cela soulève la question, pourquoi ne pas simplement définir <code class="notranslate" translate="no">near</code> de 0,0000000001 et <code class="notranslate" translate="no">far</code> de 100000000000000 ou quelque chose comme ça pour que vous puissiez tout voir? Parce que votre GPU n'a qu'une précision limitée pour décider si quelque chose est devant ou derrière quelque chose d'autre. Cette précision se répartit entre <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Pire, par défaut la précision au plus près de la caméra est précise tandis que celle la plus lointaine de la caméra est grossière. Les unités commencent par <code class="notranslate" translate="no">near</code> et s'étendent lentement à mesure qu'elles s'approchent de <code class="notranslate" translate="no">far</code>.</p>
-<p>En commençant par l'exemple du haut, modifions le code pour insérer 20 sphères d'affilée.</p>
+<p>À gauche, vous pouvez voir la vue originale et à droite, vous pouvez
+voir une vue montrant le frustum de la caméra de gauche. En ajustant
+<code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code>, <code class="notranslate" translate="no">fov</code> et en déplaçant la caméra avec la souris, vous pouvez voir que
+seul ce qui se trouve à l'intérieur du frustum affiché à droite apparaît dans la scène de
+gauche.</p>
+<p>Ajustez <code class="notranslate" translate="no">near</code> jusqu'à environ 20 et vous verrez facilement l'avant des objets
+disparaître car ils ne sont plus dans le frustum. Ajustez <code class="notranslate" translate="no">far</code> en dessous d'environ 35
+et vous commencerez à voir le plan au sol disparaître car il n'est plus dans
+le frustum.</p>
+<p>Cela soulève la question : pourquoi ne pas simplement régler <code class="notranslate" translate="no">near</code> sur 0.0000000001 et <code class="notranslate" translate="no">far</code>
+sur 10000000000000 ou quelque chose de similaire afin de tout voir ?
+La raison est que votre GPU n'a qu'une précision limitée pour décider si quelque chose
+est devant ou derrière autre chose. Cette précision est répartie entre
+<code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Pire encore, par défaut, la précision près de la caméra est détaillée
+et la précision loin de la caméra est grossière. Les unités commencent à <code class="notranslate" translate="no">near</code>
+et s'étendent lentement à mesure qu'elles s'approchent de <code class="notranslate" translate="no">far</code>.</p>
+<p>En partant de l'exemple du haut, changeons le code pour insérer 20 sphères d'affilée.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const sphereRadius = 3;
   const sphereWidthDivisions = 32;
@@ -266,30 +306,33 @@ const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
   }
 }
 </pre>
-<p>et définissons <code class="notranslate" translate="no">near</code> à 0.00001</p>
+<p>et réglons <code class="notranslate" translate="no">near</code> sur 0.00001</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 45;
-const aspect = 2;  // valeur par défaut
+const aspect = 2;  // the canvas default
 -const near = 0.1;
 +const near = 0.00001;
 const far = 100;
 const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 </pre>
-<p>Nous devons également modifier un peu le code de lil-gui pour autoriser 0,00001 si la valeur est modifiée.</p>
+<p>Nous devons également ajuster un peu le code de l'interface graphique pour permettre 0.00001 si la valeur est éditée.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
 +gui.add(minMaxGUIHelper, 'min', 0.00001, 50, 0.00001).name('near').onChange(updateCamera);
 </pre>
 <p>Que pensez-vous qu'il va se passer ?</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-z-fighting.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-z-fighting.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-z-fighting.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Ceci est un exemple de <em>z fighting</em> où le GPU de votre ordinateur n'a pas assez de précision pour décider quels pixels sont devant et quels pixels sont derrière.</p>
-<p>Juste au cas où le problème ne s'afficherait pas sur votre machine, voici ce que je vois sur la mienne.</p>
+<p>C'est un exemple de <em>z-fighting</em> (chevauchement en Z) où le GPU de votre ordinateur n'a pas
+assez de précision pour décider quels pixels sont devant et quels pixels sont derrière.</p>
+<p>Juste au cas où le problème n'apparaîtrait pas sur votre machine, voici ce que je vois sur la mienne</p>
 <div class="threejs_center"><img src="../resources/images/z-fighting.png" style="width: 570px;"></div>
 
-<p>Une solution consiste à indiquer à Three.js d'utiliser une méthode différente pour calculer quels pixels sont devant et lesquels sont derrière. Nous pouvons le faire en activant <code class="notranslate" translate="no">logarithmicDepthBuffer</code> lorsque nous créons le <a href="https://threejs.org/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
+<p>Une solution consiste à indiquer à three.js d'utiliser une méthode différente pour calculer quels
+pixels sont devant et quels pixels sont derrière. Nous pouvons le faire en activant
+<code class="notranslate" translate="no">logarithmicDepthBuffer</code> lorsque nous créons le <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
 +const renderer = new THREE.WebGLRenderer({
 +  antialias: true,
@@ -297,20 +340,37 @@ const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 +  logarithmicDepthBuffer: true,
 +});
 </pre>
-<p>et avec ça, ça devrait marcher.</p>
+<p>et avec cela, cela pourrait fonctionner</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-logarithmic-depth-buffer.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-logarithmic-depth-buffer.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-logarithmic-depth-buffer.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Si cela n'a pas résolu le problème pour vous, vous avez rencontré une raison pour laquelle vous ne pouvez pas toujours utiliser cette solution. Cette raison est due au fait que seuls certains GPU le prennent en charge. En septembre 2018, presque aucun appareil mobile ne prenait en charge cette solution, contrairement à la plupart des ordinateurs de bureau.</p>
-<p>Une autre raison de ne pas choisir cette solution est qu'elle peut être nettement plus lente que la solution standard.</p>
-<p>Même avec cette solution, la résolution est encore limitée. Rendez <code class="notranslate" translate="no">near</code> encore plus petit ou <code class="notranslate" translate="no">far</code> plus grand et vous finirez par rencontrer les mêmes problèmes.</p>
-<p>Cela signifie que vous devez toujours faire un effort pour choisir un paramètre <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> qui correspond à votre cas d'utilisation. Placez <code class="notranslate" translate="no">near</code> aussi loin que possible de la caméra sans que rien ne disparaisse. Placez <code class="notranslate" translate="no">far</code> aussi près que possible de la caméra et, de même, de façon à ce que tout reste visible. Si vous essayez de dessiner une scène géante et de montrer en gros plan un visage de façon à voir ses cils, tandis qu'en arrière-plan il soit possible de voir les montagnes à 50 kilomètres de distance, vous devrez trouver d'autres solutions créatives, nous-y reviendrons peut-être plus tard. Pour l'instant, sachez que vous devez prendre soin de choisir des valeurs <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> appropriées à vos besoins.</p>
-<p>La deuxième caméra la plus courante est l'<a href="https://threejs.org/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>. Plutôt que de définir un frustum, il faut spécifier une boîte avec les paramètres <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>, <code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Comme elle projette une boîte, il n'y a pas de perspective.</p>
-<p>Changeons notre exemple précédent pour utiliser une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> dans la première vue.</p>
-<p>D'abord, paramétrons notre <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
+<p>Si cela n'a pas résolu le problème pour vous, alors vous avez rencontré l'une des raisons pour lesquelles
+vous ne pouvez pas toujours utiliser cette solution. Cette raison est que seuls certains GPU
+la supportent. En septembre 2018, presque aucun appareil mobile ne supportait cette
+solution, alors que la plupart des ordinateurs de bureau le faisaient.</p>
+<p>Une autre raison de ne pas choisir cette solution est qu'elle peut être significativement plus lente
+que la solution standard.</p>
+<p>Même avec cette solution, la résolution reste limitée. Rendez <code class="notranslate" translate="no">near</code> encore
+plus petit ou <code class="notranslate" translate="no">far</code> encore plus grand et vous rencontrerez finalement les mêmes problèmes.</p>
+<p>Ce que cela signifie, c'est que vous devriez toujours faire un effort pour choisir un paramètre <code class="notranslate" translate="no">near</code>
+et <code class="notranslate" translate="no">far</code> qui convient à votre cas d'utilisation. Réglez <code class="notranslate" translate="no">near</code> aussi loin de la caméra
+que possible sans que les objets ne disparaissent. Réglez <code class="notranslate" translate="no">far</code> aussi près de la caméra
+que possible sans que les objets ne disparaissent. Si vous essayez de dessiner une scène géante
+et de montrer un gros plan du visage de quelqu'un afin que vous puissiez voir ses cils
+tout en voyant à l'arrière-plan des montagnes à 50 kilomètres
+de distance, eh bien, vous devrez alors trouver d'autres solutions créatives que
+nous aborderons peut-être plus tard. Pour l'instant, sachez simplement que vous devez faire attention
+à choisir des valeurs <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> appropriées à vos besoins.</p>
+<p>La deuxième caméra la plus courante est l'<a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>. Au lieu
+de spécifier un frustum, elle spécifie une boîte avec les paramètres <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>
+<code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Comme elle projette une boîte,
+il n'y a pas de perspective.</p>
+<p>Changeons l'exemple à 2 vues ci-dessus pour utiliser une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>
+dans la première vue.</p>
+<p>Tout d'abord, configurons une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = -1;
 const right = 1;
 const top = 1;
@@ -320,13 +380,19 @@ const far = 50;
 const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
 camera.zoom = 0.2;
 </pre>
-<p>Définissons <code class="notranslate" translate="no">left</code> and <code class="notranslate" translate="no">bottom</code> à -1 et <code class="notranslate" translate="no">right</code> et <code class="notranslate" translate="no">top</code> à 1. On devrait obtenir une boîte de 2 unités de large et 2 unités de haut, mais nous allons ajuster <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">top</code> en fonction de l'aspect du rectangle sur lequel nous dessinons. Nous utiliserons la propriété <code class="notranslate" translate="no">zoom</code> pour faciliter le réglage du nombre d'unités réellement affichées par la caméra.</p>
-<p>Ajoutons un nouveau paramètre à lil-gui pour le <code class="notranslate" translate="no">zoom</code>.</p>
+<p>Nous réglons <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">bottom</code> à -1 et <code class="notranslate" translate="no">right</code> et <code class="notranslate" translate="no">top</code> à 1. Cela créerait
+une boîte de 2 unités de large et 2 unités de haut, mais nous allons ajuster <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">top</code>
+en fonction de l'aspect du rectangle dans lequel nous dessinons. Nous utiliserons la propriété
+<code class="notranslate" translate="no">zoom</code> pour faciliter l'ajustement du nombre d'unités réellement affichées par la caméra.</p>
+<p>Ajoutons un paramètre GUI pour <code class="notranslate" translate="no">zoom</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
 +gui.add(camera, 'zoom', 0.01, 1, 0.01).listen();
 </pre>
-<p>L'appel à <code class="notranslate" translate="no">listen</code> dit à lil-gui de surveiller les changements. Il faut faire cela parce que <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> peut contrôler le zoom. Par exemple, la molette de défilement d'une souris zoomera via les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
-<p>Enfin, nous avons juste besoin de changer la partie qui rend le côté gauche pour mettre à jour la <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
+<p>L'appel à <code class="notranslate" translate="no">listen</code> indique à lil-gui de surveiller les changements. Ceci est ici car
+les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> peuvent également contrôler le zoom. Par exemple, la molette de la souris effectuera
+un zoom via les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
+<p>Enfin, il nous suffit de modifier la partie qui rend le côté
+gauche pour mettre à jour l'<a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const aspect = setScissorForElement(view1Elem);
 
@@ -337,22 +403,32 @@ camera.zoom = 0.2;
   camera.updateProjectionMatrix();
   cameraHelper.update();
 
-  // ne pas dessiner le camera helper dans la vue d'origine
+  // ne pas dessiner l'helper de caméra dans la vue originale
   cameraHelper.visible = false;
 
   scene.background.set(0x000000);
   renderer.render(scene, camera);
 }
 </pre>
-<p>et maintenant vous pouvez voir une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> au boulot.</p>
+<p>et maintenant vous pouvez voir une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> en action.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-orthographic-2-scenes.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-orthographic-2-scenes.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-orthographic-2-scenes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est souvent utilisée pour dessiner des objets en 2D. Il faut décider du nombre d'unités que la caméra doit afficher. Par exemple, si vous voulez qu'un pixel du canvas corresponde à une unité de la camera avec l'origine au centre, vous pouvez faire quelque chose comme.</p>
+<p>Une autre utilisation courante pour une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est de dessiner les
+vues du dessus, du dessous, de gauche, de droite, de face, d'arrière d'un programme de modélisation
+3D ou de l'éditeur d'un moteur de jeu.</p>
+<div class="threejs_center"><img src="../resources/images/quad-viewport.png" style="width: 574px;"></div>
 
+<p>Dans la capture d'écran ci-dessus, vous pouvez voir qu'une vue est une vue en perspective et 3 vues sont
+des vues orthographiques.</p>
+<p>Une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est le plus souvent utilisée si vous utilisez three.js
+pour dessiner des éléments 2D. Vous décidez combien d'unités vous voulez que la caméra
+affiche. Par exemple, si vous voulez qu'un pixel du canvas corresponde
+à une unité dans la caméra, vous pourriez faire quelque chose comme</p>
+<p>Pour placer l'origine au centre et avoir 1 pixel = 1 unité three.js, quelque chose comme</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = -canvas.width / 2;
 camera.right = canvas.width / 2;
 camera.top = canvas.height / 2;
@@ -361,7 +437,8 @@ camera.near = -1;
 camera.far = 1;
 camera.zoom = 1;
 </pre>
-<p>Ou si nous voulions que l'origine soit en haut à gauche comme un canvas 2D, nous pourrions utiliser ceci</p>
+<p>Ou si nous voulions que l'origine soit en haut à gauche, comme sur un
+canvas 2D, nous pourrions utiliser ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = 0;
 camera.right = canvas.width;
 camera.top = 0;
@@ -370,20 +447,22 @@ camera.near = -1;
 camera.far = 1;
 camera.zoom = 1;
 </pre>
-<p>Dans ce cas, le coin supérieur gauche serait à 0,0 tout comme un canvas 2D.</p>
-<p>Essayons! Commençons par configurer la caméra.</p>
+<p>Dans ce cas, le coin supérieur gauche serait 0,0, comme sur un canvas 2D.</p>
+<p>Essayons ! Tout d'abord, configurons la caméra.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = 0;
-const right = 300;  // taille par défaut
+const right = 300;  // taille par défaut du canvas
 const top = 0;
-const bottom = 150;  // taille par défaut
+const bottom = 150;  // taille par défaut du canvas
 const near = -1;
 const far = 1;
 const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
 camera.zoom = 1;
 </pre>
-<p>Chargeons ensuite 6 textures et créons 6 plans, un pour chaque texture. Chaque plan sera un enfant d'un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> pour faciliter le décalage du plan afin que son centre semble être dans son coin supérieur gauche.</p>
-<p>Pour travailler en local sur votre machine, vous aurez besoin d'une <a href="setup.html">configuration spécifique</a>.
-Vous voudrez peut-être en savoir plus sur <a href="textures.html">l'utilisation des textures</a>.</p>
+<p>Puis chargeons 6 textures et créons 6 plans, un pour chaque texture.
+Nous associerons chaque plan à un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> pour faciliter le décalage
+du plan afin que son centre apparaisse à son coin supérieur gauche.</p>
+<p>Si vous l'exécutez localement, vous devrez également avoir effectué la <a href="setup.html">configuration</a>.
+Vous pourriez également vouloir lire l'article sur l'<a href="textures.html">utilisation des textures</a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
 const textures = [
   loader.load('resources/images/flower-1.jpg'),
@@ -410,7 +489,8 @@ const planes = textures.map((texture) =&gt; {
   return planePivot;
 });
 </pre>
-<p>et nous devons mettre à jour la caméra si la taille de la toile change.</p>
+<p>et nous devons mettre à jour la caméra si la taille du canvas
+change.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
 
   if (resizeRendererToDisplaySize(renderer)) {
@@ -421,17 +501,17 @@ const planes = textures.map((texture) =&gt; {
 
   ...
 </pre>
-<p><code class="notranslate" translate="no">planes</code> est un tableau de <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">THREE.Mesh</code></a>.
+<p><code class="notranslate" translate="no">planes</code> est un tableau de <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">THREE.Mesh</code></a>, un pour chaque plan.
 Déplaçons-les en fonction du temps.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
-  time *= 0.001;  // convertir en secondes;
+  time *= 0.001;  // convertir en secondes ;
 
   ...
 
   const distAcross = Math.max(20, canvas.width - planeSize);
   const distDown = Math.max(20, canvas.height - planeSize);
 
-  // distance totale à parcourir
+  // distance totale pour se déplacer en travers et en arrière
   const xRange = distAcross * 2;
   const yRange = distDown * 2;
   const speed = 180;
@@ -440,12 +520,12 @@ Déplaçons-les en fonction du temps.</p>
     // calculer un temps unique pour chaque plan
     const t = time * speed + ndx * 300;
 
-    // définir une valeur entre 0 et une plage
+    // obtenir une valeur entre 0 et la plage
     const xt = t % xRange;
     const yt = t % yRange;
 
-    // définit notre position en avant si 0 à la moitié de la plage
-     // et vers l'arrière si la moitié de la plage à la plage
+    // définir notre position en avant si 0 à la moitié de la plage
+    // et en arrière si la moitié de la plage à la plage
     const x = xt &lt; distAcross ? xt : xRange - xt;
     const y = yt &lt; distDown   ? yt : yRange - yt;
 
@@ -454,18 +534,23 @@ Déplaçons-les en fonction du temps.</p>
 
   renderer.render(scene, camera);
 </pre>
-<p>Et vous pouvez voir les images rebondir parfaitement sur les bords du canvas en utilisant les mathématiques des pixels, tout comme un canvas 2D.</p>
+<p>Et vous pouvez voir les images rebondir parfaitement au pixel près sur les bords du
+canvas en utilisant des calculs de pixels, tout comme un canvas 2D.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-orthographic-canvas-top-left-origin.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/cameras-orthographic-canvas-top-left-origin.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/cameras-orthographic-canvas-top-left-origin.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Une autre utilisation courante d'une caméra orthographique est de dessiner les vues haut, bas, gauche, droite, avant et arrière d'un programme de modélisation 3D ou d'un éditeur de moteur de jeu.</p>
+<p>Une autre utilisation courante pour une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est de dessiner les
+vues du dessus, du dessous, de gauche, de droite, de face, d'arrière d'un programme de modélisation
+3D ou de l'éditeur d'un moteur de jeu.</p>
 <div class="threejs_center"><img src="../resources/images/quad-viewport.png" style="width: 574px;"></div>
 
-<p>Dans la capture d'écran ci-dessus, vous pouvez voir qu'une vue est une vue en perspective et que les 3 autres vues sont des vues orthogonales.</p>
-<p>C'est la base des caméras. Nous aborderons quelques façons courantes de déplacer les caméras dans d'autres articles. Pour l'instant passons aux <a href="shadows.html">ombres</a>.</p>
+<p>Dans la capture d'écran ci-dessus, vous pouvez voir qu'une vue est une vue en perspective et 3 vues sont
+des vues orthographiques.</p>
+<p>Voilà les bases des caméras. Nous aborderons quelques méthodes courantes pour déplacer les caméras
+dans d'autres articles. Pour l'instant, passons aux <a href="shadows.html">ombres</a>.</p>
 <p><canvas id="c"></canvas></p>
 <script type="module" src="../resources/threejs-cameras.js"></script>
 
@@ -479,4 +564,4 @@ Déplaçons-les en fonction du temps.</p>
 
 
 
-</body></html>
+</body></html>

+ 366 - 5
manual/fr/canvas-textures.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Canvas Textures</title>
+    <title>Textures sur Canvas</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Canvas Textures">
+    <meta name="twitter:title" content="Three.js – Textures sur Canvas">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,373 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Canvas Textures</h1>
+        <h1>Textures sur Canvas</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/canvas-textures.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article fait suite à <a href="textures.html">l'article sur les textures</a>.
+Si vous ne l'avez pas encore lu, vous devriez probablement commencer par là.</p>
+<p>Dans <a href="textures.html">l'article précédent sur les textures</a>, nous avons principalement utilisé
+des fichiers image pour les textures. Cependant, il arrive parfois que nous souhaitions générer une texture
+à l'exécution. Une façon de procéder est d'utiliser une <a href="/docs/#api/en/textures/CanvasTexture"><code class="notranslate" translate="no">CanvasTexture</code></a>.</p>
+<p>Une texture sur canvas prend un <code class="notranslate" translate="no">&lt;canvas&gt;</code> en entrée. Si vous ne savez pas comment
+dessiner avec l'API canvas 2D sur un canvas, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial">il existe un bon tutoriel sur MDN</a>.</p>
+<p>Créons un simple programme canvas. En voici un qui dessine des points à des endroits aléatoires et dans des couleurs aléatoires.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const ctx = document.createElement('canvas').getContext('2d');
+document.body.appendChild(ctx.canvas);
+ctx.canvas.width = 256;
+ctx.canvas.height = 256;
+ctx.fillStyle = '#FFF';
+ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+
+function randInt(min, max) {
+  if (max === undefined) {
+    max = min;
+    min = 0;
+  }
+  return Math.random() * (max - min) + min | 0;
+}
+
+function drawRandomDot() {
+  ctx.fillStyle = `#${randInt(0x1000000).toString(16).padStart(6, '0')}`;
+  ctx.beginPath();
+
+  const x = randInt(256);
+  const y = randInt(256);
+  const radius = randInt(10, 64);
+  ctx.arc(x, y, radius, 0, Math.PI * 2);
+  ctx.fill();
+}
+
+function render() {
+  drawRandomDot();
+  requestAnimationFrame(render);
+}
+requestAnimationFrame(render);
+</pre>
+<p>C'est assez simple.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/canvas-random-dots.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/canvas-random-dots.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Utilisons-le maintenant pour texturer quelque chose. Nous allons commencer avec l'exemple de texturation
+d'un cube tiré de <a href="textures.html">l'article précédent</a>.
+Nous allons supprimer le code qui charge une image et utiliser à la place
+notre canvas en créant une <a href="/docs/#api/en/textures/CanvasTexture"><code class="notranslate" translate="no">CanvasTexture</code></a> et en lui passant le canvas que nous avons créé.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [];  // juste un tableau que nous pouvons utiliser pour faire tourner les cubes
+-const loader = new THREE.TextureLoader();
+-
++const ctx = document.createElement('canvas').getContext('2d');
++ctx.canvas.width = 256;
++ctx.canvas.height = 256;
++ctx.fillStyle = '#FFF';
++ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
++const texture = new THREE.CanvasTexture(ctx.canvas);
+
+const material = new THREE.MeshBasicMaterial({
+-  map: loader.load('resources/images/wall.jpg'),
++  map: texture,
+});
+const cube = new THREE.Mesh(geometry, material);
+scene.add(cube);
+cubes.push(cube);  // add to our list of cubes to rotate
+</pre>
+<p>Puis appelez le code pour dessiner un point aléatoire dans notre boucle de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
++  drawRandomDot();
++  texture.needsUpdate = true;
+
+  cubes.forEach((cube, ndx) =&gt; {
+    const speed = .2 + ndx * .1;
+    const rot = time * speed;
+    cube.rotation.x = rot;
+    cube.rotation.y = rot;
+  });
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>La seule chose supplémentaire que nous devons faire est de définir la propriété <code class="notranslate" translate="no">needsUpdate</code>
+de la <a href="/docs/#api/en/textures/CanvasTexture"><code class="notranslate" translate="no">CanvasTexture</code></a> pour indiquer à three.js de mettre à jour la texture avec
+le contenu le plus récent du canvas.</p>
+<p>Et avec cela, nous avons un cube texturé sur canvas.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/canvas-textured-cube.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/canvas-textured-cube.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Notez que si vous souhaitez utiliser three.js pour dessiner dans le canvas, vous êtes
+il est préférable d'utiliser un <code class="notranslate" translate="no">RenderTarget</code>, ce qui est abordé dans <a href="rendertargets.html">cet article</a>.</p>
+<p>Un cas d'utilisation courant pour les textures sur canvas est d'afficher du texte dans une scène.
+Par exemple, si vous vouliez afficher le nom d'une personne sur le badge de son personnage,
+vous pourriez utiliser une texture sur canvas pour texturer le badge.</p>
+<p>Créons une scène avec 3 personnes et donnons à chaque personne un badge
+ou une étiquette.</p>
+<p>Reprenons l'exemple ci-dessus et supprimons tout ce qui est lié au cube. Ensuite, mettons le fond en blanc et ajoutons deux <a href="lights.html">lumières</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
++scene.background = new THREE.Color('white');
++
++function addLight(position) {
++  const color = 0xFFFFFF;
++  const intensity = 1;
++  const light = new THREE.DirectionalLight(color, intensity);
++  light.position.set(...position);
++  scene.add(light);
++  scene.add(light.target);
++}
++addLight([-3, 1, 1]);
++addLight([ 2, 1, .5]);
+</pre>
+<p>Écrivons du code pour créer une étiquette en utilisant le canvas 2D.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function makeLabelCanvas(size, name) {
++  const borderSize = 2;
++  const ctx = document.createElement('canvas').getContext('2d');
++  const font =  `${size}px bold sans-serif`;
++  ctx.font = font;
++  // mesurer la longueur du nom
++  const doubleBorderSize = borderSize * 2;
++  const width = ctx.measureText(name).width + doubleBorderSize;
++  const height = size + doubleBorderSize;
++  ctx.canvas.width = width;
++  ctx.canvas.height = height;
++
++  // besoin de redéfinir la police après avoir redimensionné le canvas
++  ctx.font = font;
++  ctx.textBaseline = 'top';
++
++  ctx.fillStyle = 'blue';
++  ctx.fillRect(0, 0, width, height);
++  ctx.fillStyle = 'white';
++  ctx.fillText(name, borderSize, borderSize);
++
++  return ctx.canvas;
++}
+</pre>
+<p>Ensuite, nous allons créer des personnages simples à partir d'un cylindre pour le corps, d'une sphère
+pour la tête et d'un plan pour l'étiquette.</p>
+<p>Commençons par créer la géométrie partagée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const bodyRadiusTop = .4;
++const bodyRadiusBottom = .2;
++const bodyHeight = 2;
++const bodyRadialSegments = 6;
++const bodyGeometry = new THREE.CylinderGeometry(
++    bodyRadiusTop, bodyRadiusBottom, bodyHeight, bodyRadialSegments);
++
++const headRadius = bodyRadiusTop * 0.8;
++const headLonSegments = 12;
++const headLatSegments = 5;
++const headGeometry = new THREE.SphereGeometry(
++    headRadius, headLonSegments, headLatSegments);
++
++const labelGeometry = new THREE.PlaneGeometry(1, 1);
+</pre>
+<p>Ensuite, créons une fonction pour construire une personne à partir de ces
+éléments.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function makePerson(x, size, name, color) {
++  const canvas = makeLabelCanvas(size, name);
++  const texture = new THREE.CanvasTexture(canvas);
++  // parce que notre canvas n'est probablement pas une puissance de 2
++  // dans les deux dimensions, définissez le filtrage de manière appropriée.
++  texture.minFilter = THREE.LinearFilter;
++  texture.wrapS = THREE.ClampToEdgeWrapping;
++  texture.wrapT = THREE.ClampToEdgeWrapping;
++
++  const labelMaterial = new THREE.MeshBasicMaterial({
++    map: texture,
++    side: THREE.DoubleSide,
++    transparent: true,
++  });
++  const bodyMaterial = new THREE.MeshPhongMaterial({
++    color,
++    flatShading: true,
++  });
++
++  const root = new THREE.Object3D();
++  root.position.x = x;
++
++  const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
++  root.add(body);
++  body.position.y = bodyHeight / 2;
++
++  const head = new THREE.Mesh(headGeometry, bodyMaterial);
++  root.add(head);
++  head.position.y = bodyHeight + headRadius * 1.1;
++
++  const label = new THREE.Mesh(labelGeometry, labelMaterial);
++  root.add(label);
++  label.position.y = bodyHeight * 4 / 5;
++  label.position.z = bodyRadiusTop * 1.01;
++
++  // si les unités sont des mètres, alors 0.01 ici ajuste la taille
++  // de l'étiquette en centimètres.
++  const labelBaseScale = 0.01;
++  label.scale.x = canvas.width  * labelBaseScale;
++  label.scale.y = canvas.height * labelBaseScale;
++
++  scene.add(root);
++  return root;
++}
+</pre>
+<p>Vous pouvez voir ci-dessus que nous plaçons le corps, la tête et l'étiquette sur un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> racine et ajustons leurs positions. Cela nous permettrait de déplacer l'objet racine si nous voulions déplacer les personnages. Le corps mesure 2 unités de haut. Si 1 unité équivaut à 1 mètre, alors le code ci-dessus tente de créer l'étiquette en centimètres, de sorte qu'elle mesurera 'size' centimètres de haut et la largeur nécessaire pour contenir le texte.</p>
+<p>Nous pouvons ensuite créer des personnages avec des étiquettes.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+makePerson(-3, 32, 'Purple People Eater', 'purple');
++makePerson(-0, 32, 'Green Machine', 'green');
++makePerson(+3, 32, 'Red Menace', 'red');
+</pre>
+<p>Il ne reste plus qu'à ajouter des <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>
+afin de pouvoir déplacer la caméra.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
++import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
+const aspect = 2;  // the canvas default
+const near = 0.1;
+-const far = 5;
++const far = 50;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+-camera.position.z = 2;
++camera.position.set(0, 2, 5);
+
++const controls = new OrbitControls(camera, canvas);
++controls.target.set(0, 2, 0);
++controls.update();
+</pre>
+<p>et nous obtenons des étiquettes simples.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/canvas-textured-labels.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/canvas-textured-labels.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Quelques points à noter.</p>
+<ul>
+<li>Si vous zoomez, les étiquettes deviennent de faible résolution.</li>
+</ul>
+<p>Il n'y a pas de solution simple. Il existe des techniques de rendu de police plus complexes, mais je ne connais pas de solutions sous forme de plugin. De plus, elles nécessiteraient que l'utilisateur télécharge les données de la police, ce qui serait lent.</p>
+<p>Une solution consiste à augmenter la résolution des étiquettes.
+Essayez de doubler la taille passée en paramètre et de diviser par deux la valeur actuelle de <code class="notranslate" translate="no">labelBaseScale</code>.</p>
+<ul>
+<li>Les étiquettes s'allongent plus le nom est long.</li>
+</ul>
+<p>Si vous vouliez résoudre ce problème, vous choisiriez plutôt une étiquette de taille fixe et compresseriez le texte.</p>
+<p>C'est assez facile. Passez une largeur de base et adaptez le texte à cette largeur comme ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function makeLabelCanvas(size, name) {
++function makeLabelCanvas(baseWidth, size, name) {
+  const borderSize = 2;
+-  const ctx = document.createElement('canvas').getContext('2d');
+  const font =  `${size}px bold sans-serif`;
+  ctx.font = font;
+  // mesurer la longueur du nom
++  const textWidth = ctx.measureText(name).width;
+
+  const doubleBorderSize = borderSize * 2;
+-  const width = ctx.measureText(name).width + doubleBorderSize;
++  const width = baseWidth + doubleBorderSize;
+  const height = size + doubleBorderSize;
+  ctx.canvas.width = width;
+  ctx.canvas.height = height;
+
+  // besoin de redéfinir la police après avoir redimensionné le canvas
+  ctx.font = font;
+-  ctx.textBaseline = 'top';
++  ctx.textBaseline = 'middle';
++  ctx.textAlign = 'center';
+
+  ctx.fillStyle = 'blue';
+  ctx.fillRect(0, 0, width, height);
+
++  // adapter à la taille mais ne pas étirer
++  const scaleFactor = Math.min(1, baseWidth / textWidth);
++  ctx.translate(width / 2, height / 2);
++  ctx.scale(scaleFactor, 1);
+  ctx.fillStyle = 'white';
+  ctx.fillText(name, borderSize, borderSize);
+
+  return ctx.canvas;
+}
+</pre>
+<p>Nous pouvons ensuite passer une largeur pour les étiquettes.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function makePerson(x, size, name, color) {
+-  const canvas = makeLabelCanvas(size, name);
++function makePerson(x, labelWidth, size, name, color) {
++  const canvas = makeLabelCanvas(labelWidth, size, name);
+
+...
+
+}
+
+-makePerson(-3, 32, 'Purple People Eater', 'purple');
+-makePerson(-0, 32, 'Green Machine', 'green');
+-makePerson(+3, 32, 'Red Menace', 'red');
++makePerson(-3, 150, 32, 'Purple People Eater', 'purple');
++makePerson(-0, 150, 32, 'Green Machine', 'green');
++makePerson(+3, 150, 32, 'Red Menace', 'red');
+</pre>
+<p>et nous obtenons des étiquettes dont le texte est centré et adapté à la taille.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/canvas-textured-labels-scale-to-fit.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/canvas-textured-labels-scale-to-fit.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Ci-dessus, nous avons utilisé un nouveau canvas pour chaque texture. Utiliser ou non un canvas par texture dépend de vous. Si vous avez besoin de les mettre à jour souvent, avoir un canvas par texture est probablement la meilleure option. Si elles sont rarement ou jamais mises à jour, vous pouvez choisir d'utiliser un seul canvas pour plusieurs textures en forçant three.js à utiliser la texture. Modifions le code ci-dessus pour faire exactement cela.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const ctx = document.createElement('canvas').getContext('2d');
+
+function makeLabelCanvas(baseWidth, size, name) {
+  const borderSize = 2;
+-  const ctx = document.createElement('canvas').getContext('2d');
+  const font =  `${size}px bold sans-serif`;
+
+  ...
+
+}
+
++const forceTextureInitialization = function() {
++  const material = new THREE.MeshBasicMaterial();
++  const geometry = new THREE.PlaneGeometry();
++  const scene = new THREE.Scene();
++  scene.add(new THREE.Mesh(geometry, material));
++  const camera = new THREE.Camera();
++
++  return function forceTextureInitialization(texture) {
++    material.map = texture;
++    renderer.render(scene, camera);
++  };
++}();
+
+function makePerson(x, labelWidth, size, name, color) {
+  const canvas = makeLabelCanvas(labelWidth, size, name);
+  const texture = new THREE.CanvasTexture(canvas);
+  // parce que notre canvas n'est probablement pas une puissance de 2
+  // dans les deux dimensions, définissez le filtrage de manière appropriée.
+  texture.minFilter = THREE.LinearFilter;
+  texture.wrapS = THREE.ClampToEdgeWrapping;
+  texture.wrapT = THREE.ClampToEdgeWrapping;
++  forceTextureInitialization(texture);
+
+  ...
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/canvas-textured-labels-one-canvas.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/canvas-textured-labels-one-canvas.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Un autre problème est que les étiquettes ne font pas toujours face à la caméra. Si vous utilisez les étiquettes comme des badges, c'est probablement une bonne chose. Si vous utilisez les étiquettes pour afficher les noms des joueurs dans un jeu en 3D, vous pourriez vouloir que les étiquettes fassent toujours face à la caméra. Nous aborderons comment faire cela dans <a href="billboards.html">un article sur les billboards</a>.</p>
+<p>Pour les étiquettes en particulier, <a href="align-html-elements-to-3d.html">une autre solution consiste à utiliser le HTML</a>. Les étiquettes dans cet article sont <em>à l'intérieur du monde 3D</em>, ce qui est bien si vous voulez qu'elles soient cachées par d'autres objets, tandis que les <a href="align-html-elements-to-3d.html">étiquettes HTML</a> sont toujours au-dessus.</p>
 
         </div>
       </div>

+ 415 - 5
manual/fr/cleanup.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Cleanup</title>
+    <title>Nettoyage</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Cleanup">
+    <meta name="twitter:title" content="Three.js – Nettoyage">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,422 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Cleanup</h1>
+        <h1>Nettoyage</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/cleanup.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Les applications Three.js utilisent souvent beaucoup de mémoire. Un modèle 3D
+peut occuper de 1 à 20 Mo de mémoire pour l'ensemble de ses sommets.
+Un modèle peut utiliser de nombreuses textures qui, même si elles sont
+compressées en fichiers jpg, doivent être décompressées
+pour être utilisées. Chaque texture 1024x1024 prend 4 à 6 Mo
+de mémoire.</p>
+<p>La plupart des applications three.js chargent les ressources au moment
+de l'initialisation et les utilisent ensuite indéfiniment jusqu'à ce que la page soit
+fermée. Mais que se passe-t-il si vous souhaitez charger et modifier des ressources
+au fil du temps ?</p>
+<p>Contrairement à la plupart des codes JavaScript, three.js ne peut pas nettoyer
+automatiquement ces ressources. Le navigateur les nettoiera
+si vous changez de page, mais sinon, c'est à vous
+de les gérer. C'est un problème lié à la conception de WebGL,
+et three.js n'a donc d'autre choix que de vous confier la
+responsabilité de libérer les ressources.</p>
+<p>Vous libérez les ressources three.js en appelant la fonction <code class="notranslate" translate="no">dispose</code> sur
+les <a href="textures.html">textures</a>,
+les <a href="primitives.html">géométries</a>, et les
+<a href="materials.html">matériaux</a>.</p>
+<p>Vous pourriez le faire manuellement. Au début, vous pourriez créer
+certaines de ces ressources</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const boxGeometry = new THREE.BoxGeometry(...);
+const boxTexture = textureLoader.load(...);
+const boxMaterial = new THREE.MeshPhongMaterial({map: texture});
+</pre>
+<p>puis, lorsque vous avez terminé avec elles, vous les libéreriez</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">boxGeometry.dispose();
+boxTexture.dispose();
+boxMaterial.dispose();
+</pre>
+<p>À mesure que vous utilisez de plus en plus de ressources, cela deviendrait de plus en
+plus fastidieux.</p>
+<p>Pour aider à réduire cette tâche fastidieuse, créons une classe pour suivre
+les ressources. Nous demanderons ensuite à cette classe de faire le nettoyage
+pour nous.</p>
+<p>Voici une première ébauche d'une telle classe</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+    if (resource.dispose) {
+      this.resources.add(resource);
+    }
+    return resource;
+  }
+  untrack(resource) {
+    this.resources.delete(resource);
+  }
+  dispose() {
+    for (const resource of this.resources) {
+      resource.dispose();
+    }
+    this.resources.clear();
+  }
+}
+</pre>
+<p>Utilisons cette classe avec le premier exemple de <a href="textures.html">l'article sur les textures</a>.
+Nous pouvons créer une instance de cette classe</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const resTracker = new ResourceTracker();
+</pre>
+<p>et pour faciliter son utilisation, créons une fonction liée pour la méthode <code class="notranslate" translate="no">track</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const resTracker = new ResourceTracker();
++const track = resTracker.track.bind(resTracker);
+</pre>
+<p>Maintenant, pour l'utiliser, il suffit d'appeler <code class="notranslate" translate="no">track</code> pour chaque géométrie, texture, et matériau
+que nous créons</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const boxWidth = 1;
+const boxHeight = 1;
+const boxDepth = 1;
+-const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
++const geometry = track(new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth));
+
+const cubes = [];  // an array we can use to rotate the cubes
+const loader = new THREE.TextureLoader();
+
+-const material = new THREE.MeshBasicMaterial({
+-  map: loader.load('resources/images/wall.jpg'),
+-});
++const material = track(new THREE.MeshBasicMaterial({
++  map: track(loader.load('resources/images/wall.jpg')),
++}));
+const cube = new THREE.Mesh(geometry, material);
+scene.add(cube);
+cubes.push(cube);  // add to our list of cubes to rotate
+</pre>
+<p>Et ensuite, pour les libérer, nous voudrions retirer les cubes de la scène
+et appeler <code class="notranslate" translate="no">resTracker.dispose</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (const cube of cubes) {
+  scene.remove(cube);
+}
+cubes.length = 0;  // clears the cubes array
+resTracker.dispose();
+</pre>
+<p>Cela fonctionnerait, mais je trouve fastidieux de devoir retirer les cubes de la
+scène. Ajoutons cette fonctionnalité au <code class="notranslate" translate="no">ResourceTracker</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+-    if (resource.dispose) {
++    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
+    return resource;
+  }
+  untrack(resource) {
+    this.resources.delete(resource);
+  }
+  dispose() {
+    for (const resource of this.resources) {
+-      resource.dispose();
++      if (resource instanceof THREE.Object3D) {
++        if (resource.parent) {
++          resource.parent.remove(resource);
++        }
++      }
++      if (resource.dispose) {
++        resource.dispose();
++      }
++    }
+    this.resources.clear();
+  }
+}
+</pre>
+<p>Et maintenant nous pouvons suivre les cubes</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = track(new THREE.MeshBasicMaterial({
+  map: track(loader.load('resources/images/wall.jpg')),
+}));
+const cube = track(new THREE.Mesh(geometry, material));
+scene.add(cube);
+cubes.push(cube);  // add to our list of cubes to rotate
+</pre>
+<p>Nous n'avons plus besoin du code pour retirer les cubes de la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-for (const cube of cubes) {
+-  scene.remove(cube);
+-}
+cubes.length = 0;  // clears the cube array
+resTracker.dispose();
+</pre>
+<p>Organisons ce code afin de pouvoir rajouter le cube,
+la texture et le matériau.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+*const cubes = [];  // just an array we can use to rotate the cubes
+
++function addStuffToScene() {
+  const resTracker = new ResourceTracker();
+  const track = resTracker.track.bind(resTracker);
+
+  const boxWidth = 1;
+  const boxHeight = 1;
+  const boxDepth = 1;
+  const geometry = track(new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth));
+
+  const loader = new THREE.TextureLoader();
+
+  const material = track(new THREE.MeshBasicMaterial({
+    map: track(loader.load('resources/images/wall.jpg')),
+  }));
+  const cube = track(new THREE.Mesh(geometry, material));
+  scene.add(cube);
+  cubes.push(cube);  // add to our list of cubes to rotate
++  return resTracker;
++}
+</pre>
+<p>Et ensuite, écrivons du code pour ajouter et supprimer des choses au fil du temps.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function waitSeconds(seconds = 0) {
+  return new Promise(resolve =&gt; setTimeout(resolve, seconds * 1000));
+}
+
+async function process() {
+  for (;;) {
+    const resTracker = addStuffToScene();
+    await wait(2);
+    cubes.length = 0;  // remove the cubes
+    resTracker.dispose();
+    await wait(1);
+  }
+}
+process();
+</pre>
+<p>Ce code va créer le cube, la texture et le matériau, attendre 2 secondes, puis les libérer et attendre 1 seconde
+et répéter.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cleanup-simple.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/cleanup-simple.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela semble donc fonctionner.</p>
+<p>Cependant, pour un fichier chargé, le travail est un peu plus conséquent. La plupart des chargeurs ne renvoient qu'un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>
+comme racine de la hiérarchie des objets qu'ils chargent, nous devons donc découvrir toutes les ressources
+qu'il contient.</p>
+<p>Mettons à jour notre <code class="notranslate" translate="no">ResourceTracker</code> pour essayer de faire cela.</p>
+<p>Nous allons d'abord vérifier si l'objet est un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>, puis suivre sa géométrie, son matériau et ses enfants.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
++    if (resource instanceof THREE.Object3D) {
++      this.track(resource.geometry);
++      this.track(resource.material);
++      this.track(resource.children);
++    }
+    return resource;
+  }
+  ...
+}
+</pre>
+<p>Maintenant, comme <code class="notranslate" translate="no">resource.geometry</code>, <code class="notranslate" translate="no">resource.material</code> et <code class="notranslate" translate="no">resource.children</code>
+peuvent être nuls ou indéfinis, nous allons vérifier en haut de <code class="notranslate" translate="no">track</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
++    if (!resource) {
++      return resource;
++    }
+
+    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
+    if (resource instanceof THREE.Object3D) {
+      this.track(resource.geometry);
+      this.track(resource.material);
+      this.track(resource.children);
+    }
+    return resource;
+  }
+  ...
+}
+</pre>
+<p>De plus, comme <code class="notranslate" translate="no">resource.children</code> est un tableau et que <code class="notranslate" translate="no">resource.material</code> peut être
+un tableau, vérifions s'il s'agit de tableaux.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+    if (!resource) {
+      return resource;
+    }
+
+*    // handle children and when material is an array of materials.
+*    // Gérer les enfants et lorsque le matériau est un tableau de matériaux.
+    if (Array.isArray(resource)) {
+      resource.forEach(resource =&gt; this.track(resource));
+      return resource;
+    }
+
+    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
+    if (resource instanceof THREE.Object3D) {
+      this.track(resource.geometry);
+      this.track(resource.material);
+      this.track(resource.children);
+    }
+    return resource;
+  }
+  ...
+}
+</pre>
+<p>Et enfin, nous devons parcourir les propriétés et les uniformes
+d'un matériau à la recherche de textures.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ResourceTracker {
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+    if (!resource) {
+      return resource;
+    }
+
+*    // handle children and when material is an array of materials or
+*    // uniform is array of textures
+*    // Gérer les enfants et lorsque le matériau est un tableau de matériaux ou
+*    // qu'un uniforme est un tableau de textures
+    if (Array.isArray(resource)) {
+      resource.forEach(resource =&gt; this.track(resource));
+      return resource;
+    }
+
+    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
+    if (resource instanceof THREE.Object3D) {
+      this.track(resource.geometry);
+      this.track(resource.material);
+      this.track(resource.children);
+-    }
++    } else if (resource instanceof THREE.Material) {
++      // We have to check if there are any textures on the material
++      // Nous devons vérifier s'il y a des textures sur le matériau
++      for (const value of Object.values(resource)) {
++        if (value instanceof THREE.Texture) {
++          this.track(value);
++        }
++      }
++      // We also have to check if any uniforms reference textures or arrays of textures
++      // Nous devons aussi vérifier si des uniformes font référence à des textures ou à des tableaux de textures
++      if (resource.uniforms) {
++        for (const value of Object.values(resource.uniforms)) {
++          if (value) {
++            const uniformValue = value.value;
++            if (uniformValue instanceof THREE.Texture ||
++                Array.isArray(uniformValue)) {
++              this.track(uniformValue);
++            }
++          }
++        }
++      }
++    }
+    return resource;
+  }
+  ...
+}
+</pre>
+<p>Et avec cela, prenons un exemple de <a href="load-gltf.html">l'article sur le chargement de fichiers gltf</a>
+et faisons-le charger et libérer des fichiers.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gltfLoader = new GLTFLoader();
+function loadGLTF(url) {
+  return new Promise((resolve, reject) =&gt; {
+    gltfLoader.load(url, resolve, undefined, reject);
+  });
+}
+
+function waitSeconds(seconds = 0) {
+  return new Promise(resolve =&gt; setTimeout(resolve, seconds * 1000));
+}
+
+const fileURLs = [
+  'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf',
+  'resources/models/3dbustchallange_submission/scene.gltf',
+  'resources/models/mountain_landscape/scene.gltf',
+  'resources/models/simple_house_scene/scene.gltf',
+];
+
+async function loadFiles() {
+  for (;;) {
+    for (const url of fileURLs) {
+      const resMgr = new ResourceTracker();
+      const track = resMgr.track.bind(resMgr);
+      const gltf = await loadGLTF(url);
+      const root = track(gltf.scene);
+      scene.add(root);
+
+      // compute the box that contains all the stuff
+      // from root and below
+      // calculer la boîte qui contient tout le contenu
+      // à partir de la racine et en dessous
+      const box = new THREE.Box3().setFromObject(root);
+
+      const boxSize = box.getSize(new THREE.Vector3()).length();
+      const boxCenter = box.getCenter(new THREE.Vector3());
+
+      // set the camera to frame the box
+      // définir la caméra pour cadrer la boîte
+      frameArea(boxSize * 1.1, boxSize, boxCenter, camera);
+
+      await waitSeconds(2);
+      renderer.render(scene, camera);
+
+      resMgr.dispose();
+
+      await waitSeconds(1);
+
+    }
+  }
+}
+loadFiles();
+</pre>
+<p>et nous obtenons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cleanup-loaded-files.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/cleanup-loaded-files.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Quelques notes sur le code.</p>
+<p>Si nous voulions charger 2 fichiers ou plus à la fois et les libérer à
+tout moment, nous utiliserions un <code class="notranslate" translate="no">ResourceTracker</code> par fichier.</p>
+<p>Ci-dessus, nous suivons uniquement <code class="notranslate" translate="no">gltf.scene</code> juste après le chargement.
+Sur la base de notre implémentation actuelle de <code class="notranslate" translate="no">ResourceTracker</code>,
+cela suivra toutes les ressources juste chargées. Si nous ajoutions plus
+d'éléments à la scène, nous devrions décider de les suivre ou non.</p>
+<p>Par exemple, disons qu'après avoir chargé un personnage, nous mettons un outil
+dans sa main en faisant de l'outil un enfant de sa main. Tel quel,
+cet outil ne sera pas libéré. Je suppose que la plupart du temps,
+c'est ce que nous voulons. </p>
+<p>Cela soulève un point. À l'origine, lorsque j'ai écrit pour la première fois le <code class="notranslate" translate="no">ResourceTracker</code>
+ci-dessus, je parcourais tout à l'intérieur de la méthode <code class="notranslate" translate="no">dispose</code> au lieu de <code class="notranslate" translate="no">track</code>.
+Ce n'est que plus tard, en réfléchissant au cas de l'outil en tant qu'enfant de la main ci-dessus,
+qu'il est devenu clair que suivre exactement ce qu'il faut libérer dans <code class="notranslate" translate="no">track</code> était plus
+flexible et sans doute plus correct, car nous pouvions alors suivre ce qui avait été chargé
+depuis le fichier plutôt que de simplement libérer l'état du graphe de scène plus tard.</p>
+<p>Honnêtement, je ne suis pas satisfait à 100% de <code class="notranslate" translate="no">ResourceTracker</code>. Faire les choses de cette
+manière n'est pas courant dans les moteurs 3D. Nous ne devrions pas avoir à deviner quelles
+ressources ont été chargées, nous devrions le savoir. Il serait bien que three.js
+change de sorte que tous les chargeurs de fichiers renvoient un objet standard avec
+des références à toutes les ressources chargées. Du moins pour l'instant,
+three.js ne nous donne pas plus d'informations lors du chargement d'une scène, donc cette
+solution semble fonctionner.</p>
+<p>J'espère que vous trouverez cet exemple utile ou du moins une bonne référence pour ce qui est
+nécessaire pour libérer des ressources dans three.js</p>
 
         </div>
       </div>

+ 354 - 0
manual/fr/color-management.html

@@ -0,0 +1,354 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Gestion des couleurs</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Gestion des couleurs">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+<style>
+  blockquote {
+    font-size: 0.8em;
+    line-height: 1.5em;
+    margin-left: 0;
+    border-left: 4px solid #cccccc;
+    padding: 1em 2em 1em 2em;
+  }
+
+  blockquote p:first-child {
+    margin-top: 0;
+  }
+
+  blockquote p:last-child {
+    margin-bottom: 0;
+  }
+
+  figure {
+    width: 100%;
+    margin: 1em 0;
+    font-style: italic;
+  }
+
+  figure img {
+    width: 100%;
+  }
+
+  figure.float {
+    float: right;
+    max-width: 30%;
+    margin: 1em;
+  }
+
+  @media all and ( max-width: 640px ) {
+
+    figure.float {
+      float: none;
+      max-width: 100%;
+    }
+
+  }
+</style>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Gestion des couleurs</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+        
+			<h2>Qu'est-ce qu'un espace couleur ?</h2>
+
+	<p>
+		Chaque espace couleur est une collection de plusieurs décisions de conception, choisies ensemble pour prendre en charge une
+		vaste gamme de couleurs tout en satisfaisant aux contraintes techniques liées à la précision et aux technologies
+		d'affichage. Lors de la création d'un actif 3D, ou de l'assemblage d'actifs 3D dans une scène, il est
+		important de connaître ces propriétés et la façon dont les propriétés d'un espace couleur sont liées
+		aux autres espaces couleur de la scène.
+	</p>
+
+	<figure class="float">
+		<img src="../resources/srgb_gamut.png" alt="">
+		<figcaption>
+			Couleurs sRGB et point blanc (D65) affichés dans le diagramme de chromaticité de référence CIE 1931.
+			La région colorée représente une projection 2D du gamut sRGB, qui est un volume 3D.
+			Source : <a href="https://en.wikipedia.org/wiki/SRGB" target="_blank" rel="noopener">Wikipedia</a>
+		</figcaption>
+	</figure>
+
+	<ul>
+		<li>
+			<b>Primaires de couleur :</b> Les couleurs primaires (par exemple rouge, vert, bleu) ne sont pas absolues ; elles sont
+			sélectionnées à partir du spectre visible en fonction des contraintes de précision limitée et des capacités des
+			périphériques d'affichage disponibles. Les couleurs sont exprimées comme un ratio des couleurs primaires.
+		</li>
+		<li>
+			<b>Point blanc :</b> La plupart des espaces couleur sont conçus de telle sorte qu'une somme pondérée égale des
+			primaires <i>R = G = B</i> apparaisse sans couleur, ou "achromatique". L'apparence
+			des valeurs achromatiques (comme le blanc ou le gris) dépend de la perception humaine, qui à son tour dépend
+			fortement du contexte de l'observateur. Un espace couleur spécifie son "point blanc" pour équilibrer
+			ces besoins. Le point blanc défini par l'espace couleur sRGB est
+			[link:https://en.wikipedia.org/wiki/Illuminant_D65 D65].
+		</li>
+		<li>
+			<b>Fonctions de transfert :</b> Après avoir choisi le gamut et un modèle de couleur, nous devons encore
+			définir des correspondances ("fonctions de transfert") des valeurs numériques vers/depuis l'espace couleur. Est-ce que <i>r = 0.5</i>
+			représente 50 % moins d'éclairage physique que <i>r = 1.0</i> ? Ou 50 % moins lumineux, tel que perçu
+			par un œil humain moyen ? Ce sont des choses différentes, et cette différence peut être représentée par
+			une fonction mathématique. Les fonctions de transfert peuvent être <i>linéaires</i> ou <i>non linéaires</i>, en fonction
+			des objectifs de l'espace couleur. sRGB définit des fonctions de transfert non linéaires. Ces
+			fonctions sont parfois approximées par des <i>fonctions gamma</i>, mais le terme "gamma" est
+			ambigu et doit être évité dans ce contexte.
+		</li>
+	</ul>
+
+	Ces trois paramètres — primaires de couleur, point blanc et fonctions de transfert — définissent un espace
+	couleur, chacun choisi pour des objectifs particuliers. Ayant défini les paramètres, quelques termes supplémentaires
+	sont utiles :
+
+	<ul>
+		<li>
+			<b>Modèle de couleur :</b> Syntaxe pour identifier numériquement les couleurs dans le gamut choisi —
+			un système de coordonnées pour les couleurs. Dans three.js, nous sommes principalement concernés par le modèle de couleur
+			RGB, ayant trois coordonnées <i>r, g, b ∈ [0,1]</i> ("domaine fermé") ou
+			<i>r, g, b ∈ [0,∞]</i> ("domaine ouvert") représentant chacune une fraction d'une couleur
+			primaire. D'autres modèles de couleur (HSL, Lab, LCH) sont couramment utilisés pour le contrôle artistique.
+		</li>
+		<li>
+			<b>Gamut de couleur :</b> Une fois que les primaires de couleur et un point blanc ont été choisis, ceux-ci représentent
+			un volume dans le spectre visible (un "gamut"). Les couleurs ne se trouvant pas dans ce volume ("hors gamut")
+			ne peuvent pas être exprimées par des valeurs RGB [0,1] en domaine fermé. Dans le domaine ouvert [0,∞], le gamut est
+			techniquement infini.
+		</li>
+	</ul>
+
+	<p>
+		Considérez deux espaces couleur très courants : `SRGBColorSpace` ("sRGB") et
+		`LinearSRGBColorSpace` ("Linear-sRGB"). Les deux utilisent les mêmes primaires et le même point blanc,
+		et ont donc le même gamut de couleur. Les deux utilisent le modèle de couleur RGB. Ils ne diffèrent que par
+		les fonctions de transfert — Linear-sRGB est linéaire par rapport à l'intensité lumineuse physique.
+		sRGB utilise les fonctions de transfert sRGB non linéaires, et ressemble plus étroitement à la façon dont
+		l'œil humain perçoit la lumière et à la réactivité des périphériques d'affichage courants.
+	</p>
+
+	<p>
+		Cette différence est importante. Les calculs d'éclairage et autres opérations de rendu doivent
+		généralement avoir lieu dans un espace couleur linéaire. Cependant, les couleurs linéaires sont moins efficaces pour
+		être stockées dans une image ou un framebuffer, et ne paraissent pas correctes lorsqu'elles sont visualisées par un observateur humain.
+		En conséquence, les textures d'entrée et l'image finale rendue utiliseront généralement l'espace couleur sRGB non linéaire.
+	</p>
+
+	<blockquote>
+		<p>
+			ℹ️ <i><b>AVIS :</b> Bien que certains écrans modernes prennent en charge des gamuts plus larges comme Display-P3,
+				les API graphiques de la plateforme web reposent largement sur sRGB. Les applications utilisant three.js
+				aujourd'hui utiliseront généralement uniquement les espaces couleur sRGB et Linear-sRGB.</i>
+		</p>
+	</blockquote>
+
+	<h2>Rôles des espaces couleur</h2>
+
+	<p>
+		Les flux de travail linéaires — requis pour les méthodes de rendu modernes — impliquent généralement plus d'un
+		espace couleur, chacun assigné à un rôle particulier. Les espaces couleur linéaires et non linéaires sont
+		appropriés pour différents rôles, expliqués ci-dessous.
+	</p>
+
+	<h3>Espace couleur d'entrée</h3>
+
+	<p>
+		Les couleurs fournies à three.js — à partir de sélecteurs de couleur, de textures, de modèles 3D et d'autres sources —
+		ont chacune un espace couleur associé. Celles qui ne sont pas déjà dans l'espace couleur de travail Linear-sRGB
+		doivent être converties, et les textures doivent recevoir l'affectation correcte <i>texture.colorSpace</i>.
+		Certaines conversions (pour les couleurs hexadécimales et CSS en sRGB) peuvent être effectuées automatiquement si
+		l'API THREE.ColorManagement est activée avant l'initialisation des couleurs :
+	</p>
+
+	<code>
+THREE.ColorManagement.enabled = true;
+	</code>
+
+	<p>
+		THREE.ColorManagement est activé par défaut.
+	</p>
+
+	<ul>
+		<li>
+			<b>Matériaux, lumières et shaders :</b> Les couleurs dans les matériaux, les lumières et les shaders stockent
+			les composants RGB dans l'espace couleur de travail Linear-sRGB.
+		</li>
+		<li>
+			<b>Couleurs de sommet :</b> `BufferAttribute` stockent les composants RGB dans l'espace couleur de travail
+			Linear-sRGB.
+		</li>
+		<li>
+			<b>Textures de couleur :</b> Les `Texture` PNG ou JPEG contenant des informations de couleur
+			(comme .map ou .emissiveMap) utilisent l'espace couleur sRGB en domaine fermé, et doivent être annotées avec
+			<i>texture.colorSpace = SRGBColorSpace</i>. Des formats comme OpenEXR (parfois utilisés pour .envMap ou
+			.lightMap) utilisent l'espace couleur Linear-sRGB indiqué par <i>texture.colorSpace = LinearSRGBColorSpace</i>,
+			et peuvent contenir des valeurs dans le domaine ouvert [0,∞].
+		</li>
+		<li>
+			<b>Textures non couleur :</b> Les textures qui ne stockent pas d'informations de couleur (comme .normalMap
+			ou .roughnessMap) n'ont pas d'espace couleur associé, et utilisent généralement l'annotation de texture (par défaut) de
+			<i>texture.colorSpace = NoColorSpace</i>. Dans de rares cas, les données non couleur
+			peuvent être représentées avec d'autres encodages non linéaires pour des raisons techniques.
+		</li>
+	</ul>
+
+	<blockquote>
+		<p>
+			⚠️ <i><b>AVERTISSEMENT :</b> De nombreux formats de modèles 3D ne définissent pas correctement ou de manière cohérente
+			les informations d'espace couleur. Bien que three.js tente de gérer la plupart des cas, les problèmes
+			sont fréquents avec les anciens formats de fichiers. Pour de meilleurs résultats, utilisez glTF 2.0 (`GLTFLoader`)
+			et testez les modèles 3D dans des visualiseurs en ligne tôt pour confirmer que l'actif lui-même est correct.</i>
+		</p>
+	</blockquote>
+
+	<h3>Espace couleur de travail</h3>
+
+	<p>
+		Le rendu, l'interpolation et de nombreuses autres opérations doivent être effectuées dans un espace couleur de travail
+		linéaire en domaine ouvert, dans lequel les composants RGB sont proportionnels à l'illumination physique.
+		Dans three.js, l'espace couleur de travail est Linear-sRGB.
+	</p>
+
+	<h3>Espace couleur de sortie</h3>
+
+	<p>
+		La sortie vers un périphérique d'affichage, une image ou une vidéo peut impliquer une conversion de l'espace couleur de travail
+		Linear-sRGB en domaine ouvert vers un autre espace couleur. La conversion est définie par
+		(`WebGLRenderer.outputColorSpace`). Lors de l'utilisation du post-traitement, cela nécessite OutputPass.
+	</p>
+
+	<ul>
+		<li>
+			<b>Affichage :</b> Les couleurs écrites sur un canevas WebGL pour l'affichage doivent être dans l'espace
+			couleur sRGB.
+		</li>
+		<li>
+			<b>Image :</b> Les couleurs écrites dans une image doivent utiliser l'espace couleur approprié pour
+			le format et l'utilisation. Les images entièrement rendues écrites dans des textures PNG ou JPEG utilisent généralement
+			l'espace couleur sRGB. Les images contenant de l'émission, des lightmaps ou d'autres données non confinées à la plage [0,1]
+			utiliseront généralement l'espace couleur Linear-sRGB en domaine ouvert, et un format d'image compatible comme OpenEXR.
+		</li>
+	</ul>
+
+	<blockquote>
+		<p>
+			⚠️ <i><b>AVERTISSEMENT :</b> Les cibles de rendu peuvent utiliser soit sRGB soit Linear-sRGB. sRGB utilise
+			mieux la précision limitée. Dans le domaine fermé, 8 bits suffisent souvent pour sRGB tandis que ≥12 bits
+			(half float) peuvent être nécessaires pour Linear-sRGB. Si les étapes ultérieures du pipeline nécessitent
+			une entrée Linear-sRGB, les conversions supplémentaires peuvent entraîner un léger coût de performance.</i>
+		</p>
+	</blockquote>
+
+	<p>
+		Les matériaux personnalisés basés sur `ShaderMaterial` et `RawShaderMaterial` doivent implémenter leur propre conversion d'espace couleur de sortie.
+		Pour les instances de `ShaderMaterial`, ajouter le chunk de shader `colorspace_fragment` à la fonction `main()` du shader de fragment devrait être suffisant.
+	</p>
+
+	<h2>Utilisation des instances THREE.Color</h2>
+
+	<p>
+		Les méthodes lisant ou modifiant des instances `Color` supposent que les données sont déjà dans
+		l'espace couleur de travail de three.js, Linear-sRGB. Les composants RGB et HSL sont des
+		représentations directes des données stockées par l'instance Color, et ne sont jamais convertis
+		implicitement. Les données de couleur peuvent être explicitement converties avec <i>.convertLinearToSRGB()</i>
+		ou <i>.convertSRGBToLinear()</i>.
+	</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+// RGB components (no change).
+color.r = color.g = color.b = 0.5;
+console.log( color.r ); // → 0.5
+
+// Manual conversion.
+color.r = 0.5;
+color.convertSRGBToLinear();
+console.log( color.r ); // → 0.214041140
+</pre>
+
+	<p>
+		Avec <i>ColorManagement.enabled = true</i> (recommandé), certaines conversions
+		sont effectuées automatiquement. Comme les couleurs hexadécimales et CSS sont généralement sRGB, les méthodes `Color`
+		convertiront automatiquement ces entrées de sRGB vers Linear-sRGB dans les setters, ou convertiront de
+		Linear-sRGB vers sRGB lors du retour d'une sortie hexadécimale ou CSS à partir des getters.
+	</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+// Hexadecimal conversion.
+color.setHex( 0x808080 );
+console.log( color.r ); // → 0.214041140
+console.log( color.getHex() ); // → 0x808080
+
+// CSS conversion.
+color.setStyle( 'rgb( 0.5, 0.5, 0.5 )' );
+console.log( color.r ); // → 0.214041140
+
+// Override conversion with 'colorSpace' argument.
+color.setHex( 0x808080, LinearSRGBColorSpace );
+console.log( color.r ); // → 0.5
+console.log( color.getHex( LinearSRGBColorSpace ) ); // → 0x808080
+console.log( color.getHex( SRGBColorSpace ) ); // → 0xBCBCBC
+</pre>
+
+	<h2>Erreurs courantes</h2>
+
+	<p>
+		Lorsqu'une couleur ou une texture individuelle est mal configurée, elle apparaîtra plus foncée ou plus claire que
+		prévu. Lorsque l'espace couleur de sortie du renderer est mal configuré, toute la scène peut apparaître
+		plus foncée (par exemple, conversion manquante vers sRGB) ou plus claire (par exemple, une double conversion vers sRGB avec
+		post-traitement). Dans chaque cas, le problème peut ne pas être uniforme, et simplement augmenter/diminuer
+		l'éclairage ne le résout pas.
+	</p>
+
+	<p>
+		Un problème plus subtil apparaît lorsque <i>à la fois</i> les espaces couleur d'entrée et les espaces couleur de sortie
+		sont incorrects — les niveaux de luminosité globaux peuvent être corrects, mais les couleurs peuvent changer
+		de manière inattendue sous différents éclairages, ou l'ombrage peut sembler plus brûlé et moins doux
+		que prévu. Ces deux erreurs ne font pas une seule bonne chose, et il est important que l'espace
+		couleur de travail soit linéaire ("référé à la scène") et l'espace couleur de sortie soit non linéaire
+		("référé à l'affichage").
+	</p>
+
+	<h2>Pour aller plus loin</h2>
+
+	<ul>
+		<li>
+			<a href="https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear" target="_blank" rel="noopener">GPU Gems 3 : The Importance of Being Linear</a>, par Larry Gritz et Eugene d'Eon
+		</li>
+		<li>
+			<a href="https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/" target="_blank" rel="noopener">Ce que chaque programmeur devrait savoir sur le gamma</a>, par John Novak
+		</li>
+		<li>
+			<a href="https://hg2dc.com/" target="_blank" rel="noopener">Le guide du routard de la couleur numérique</a>, par Troy Sobotka
+		</li>
+		<li>
+			<a href="https://docs.blender.org/manual/en/latest/render/color_management.html" target="_blank" rel="noopener">Gestion des couleurs</a>, Blender
+		</li>
+	</ul>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 179 - 0
manual/fr/creating-a-scene.html

@@ -0,0 +1,179 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Créer une scène</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Créer une scène">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Créer une scène</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+            
+          <p>Le but de cette section est de donner une brève introduction à three.js. Nous commencerons par configurer une scène, avec un cube en rotation. Un exemple fonctionnel est fourni en bas de la page au cas où vous seriez bloqué et auriez besoin d'aide.</p>
+
+		<h2>Avant de commencer</h2>
+
+		<p>
+			Si vous ne l'avez pas encore fait, parcourez le guide `Installation`. Nous supposerons que vous avez déjà configuré la même structure de projet (incluant <i>index.html</i> et <i>main.js</i>), avez installé three.js, et utilisez soit un outil de build, soit un serveur local avec un CDN et des import maps.
+		</p>
+
+		<h2>Créer la scène</h2>
+
+		<p>Pour pouvoir afficher quoi que ce soit avec three.js, nous avons besoin de trois éléments : une scène, une caméra et un moteur de rendu (renderer), afin que nous puissions afficher la scène avec la caméra.</p>
+
+		<p><i>main.js —</i></p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import * as THREE from 'three';
+
+const scene = new THREE.Scene();
+const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
+
+const renderer = new THREE.WebGLRenderer();
+renderer.setSize( window.innerWidth, window.innerHeight );
+document.body.appendChild( renderer.domElement );
+</pre>
+
+		<p>Prenons un instant pour expliquer ce qui se passe ici. Nous avons maintenant configuré la scène, notre caméra et le moteur de rendu.</p>
+
+		<p>Il existe plusieurs caméras différentes dans three.js. Pour l'instant, utilisons une `PerspectiveCamera`.</p>
+
+		<p>Le premier attribut est le `champ de vision`. Le FOV est l'étendue de la scène visible sur l'écran à un moment donné. La valeur est en degrés.</p>
+
+		<p>Le deuxième est le `rapport d'aspect`. Vous voudrez presque toujours utiliser la largeur de l'élément divisée par la hauteur, sinon vous obtiendrez le même résultat que lorsque vous regardez de vieux films sur une télévision grand écran - l'image semble écrasée.</p>
+
+		<p>Les deux attributs suivants sont les plans de découpe `near` (proche) et `far` (éloigné). Cela signifie que les objets plus éloignés de la caméra que la valeur de `far` ou plus proches que `near` ne seront pas rendus. Vous n'avez pas à vous en soucier maintenant, mais vous pourriez vouloir utiliser d'autres valeurs dans vos applications pour obtenir de meilleures performances.</p>
+
+		<p>Vient ensuite le moteur de rendu (renderer). En plus de créer l'instance du moteur de rendu, nous devons également définir la taille à laquelle nous voulons qu'il affiche notre application. C'est une bonne idée d'utiliser la largeur et la hauteur de la zone que nous voulons remplir avec notre application - dans ce cas, la largeur et la hauteur de la fenêtre du navigateur. Pour les applications gourmandes en performances, vous pouvez également donner des valeurs plus petites à `setSize`, comme `window.innerWidth/2` et `window.innerHeight/2`, ce qui fera afficher l'application à un quart de la taille.</p>
+
+		<p>Si vous souhaitez conserver la taille de votre application mais l'afficher à une résolution inférieure, vous pouvez le faire en appelant `setSize` avec `false` comme argument `updateStyle` (le troisième argument). Par exemple, `setSize(window.innerWidth/2, window.innerHeight/2, false)` affichera votre application à moitié résolution, étant donné que votre &lt;canvas&gt; a une largeur et une hauteur de 100%.</p>
+
+		<p>Enfin, nous ajoutons l'élément `renderer` à notre document HTML. C'est un élément &lt;canvas&gt; que le moteur de rendu utilise pour nous afficher la scène.</p>
+
+		<p><em>"Tout ça c'est bien beau, mais où est ce cube que vous avez promis ?"</em> Ajoutons-le maintenant.</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+const geometry = new THREE.BoxGeometry( 1, 1, 1 );
+const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+const cube = new THREE.Mesh( geometry, material );
+scene.add( cube );
+
+camera.position.z = 5;
+</pre>
+
+		<p>Pour créer un cube, nous avons besoin d'une `BoxGeometry`. C'est un objet qui contient tous les points (`vertices`) et le remplissage (`faces`) du cube. Nous explorerons cela plus en détail à l'avenir.</p>
+
+		<p>En plus de la géométrie, nous avons besoin d'un matériau pour le colorer. Three.js est livré avec plusieurs matériaux, mais nous nous en tiendrons au `MeshBasicMaterial` pour l'instant. Tous les matériaux prennent un objet de propriétés qui leur seront appliquées. Pour simplifier les choses au maximum, nous fournissons seulement un attribut de couleur `0x00ff00`, qui est le vert. Cela fonctionne de la même manière que les couleurs dans CSS ou Photoshop (`couleurs hexadécimales`).</p>
+
+		<p>La troisième chose dont nous avons besoin est un `Mesh`. Un mesh est un objet qui prend une géométrie et lui applique un matériau, que nous pouvons ensuite insérer dans notre scène et déplacer librement.</p>
+
+		<p>Par défaut, lorsque nous appelons `scene.add()`, l'élément que nous ajoutons sera ajouté aux coordonnées `(0,0,0)`. Cela entraînerait la caméra et le cube à être l'un dans l'autre. Pour éviter cela, nous déplaçons simplement un peu la caméra.</p>
+
+		<h2>Afficher la scène</h2>
+
+		<p>Si vous copiez le code ci-dessus dans le fichier main.js que nous avons créé précédemment, vous ne pourrez rien voir. C'est parce que nous n'affichons encore rien. Pour cela, nous avons besoin de ce qu'on appelle une boucle de rendu ou d'animation.</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+function animate() {
+  renderer.render( scene, camera );
+}
+renderer.setAnimationLoop( animate );
+</pre>
+
+		<p>Cela créera une boucle qui fait que le moteur de rendu dessine la scène à chaque rafraîchissement de l'écran (sur un écran typique, cela signifie 60 fois par seconde). Si vous débutez dans l'écriture de jeux dans le navigateur, vous pourriez dire <em>"pourquoi ne pas simplement créer un setInterval ?"</em> Le fait est que - nous pourrions, mais `requestAnimationFrame` qui est utilisé en interne dans `WebGLRenderer` présente un certain nombre d'avantages. Le plus important est peut-être qu'il se met en pause lorsque l'utilisateur navigue vers un autre onglet du navigateur, évitant ainsi de gaspiller sa précieuse puissance de traitement et l'autonomie de sa batterie.</p>
+
+		<h2>Animer le cube</h2>
+
+		<p>Si vous insérez tout le code ci-dessus dans le fichier que vous avez créé avant de commencer, vous devriez voir une boîte verte. Rendons le tout un peu plus intéressant en le faisant pivoter.</p>
+
+		<p>Ajoutez le code suivant juste au-dessus de l'appel `renderer.render` dans votre fonction `animate` :</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+cube.rotation.x += 0.01;
+cube.rotation.y += 0.01;
+</pre>
+
+		<p>Cela sera exécuté à chaque image (normalement 60 fois par seconde) et donnera au cube une belle animation de rotation. En gros, tout ce que vous voulez déplacer ou modifier pendant que l'application est en cours d'exécution doit passer par la boucle d'animation. Vous pouvez bien sûr appeler d'autres fonctions à partir de là, afin de ne pas vous retrouver avec une fonction `animate` de plusieurs centaines de lignes.</p>
+
+		<h2>Le résultat</h2>
+		<p>Félicitations ! Vous avez maintenant terminé votre première application three.js. C'est simple, mais il faut bien commencer quelque part.</p>
+
+		<p>Le code complet est disponible ci-dessous et sous forme d'un [link:https://jsfiddle.net/tswh48fL/ exemple live] modifiable. Jouez avec pour mieux comprendre comment cela fonctionne.</p>
+
+		<p><i>index.html —</i></p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+  &lt;head&gt;
+    &lt;meta charset="utf-8"&gt;
+    &lt;title&gt;Ma première application three.js&lt;/title&gt;
+    &lt;style&gt;
+      body { margin: 0; }
+    &lt;/style&gt;
+  &lt;/head&gt;
+  &lt;body&gt;
+    &lt;script type="module" src="/main.js"&gt;&lt;/script&gt;
+  &lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+		<p><i>main.js —</i></p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import * as THREE from 'three';
+
+const scene = new THREE.Scene();
+const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
+
+const renderer = new THREE.WebGLRenderer();
+renderer.setSize( window.innerWidth, window.innerHeight );
+renderer.setAnimationLoop( animate );
+document.body.appendChild( renderer.domElement );
+
+const geometry = new THREE.BoxGeometry( 1, 1, 1 );
+const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+const cube = new THREE.Mesh( geometry, material );
+scene.add( cube );
+
+camera.position.z = 5;
+
+function animate() {
+
+  cube.rotation.x += 0.01;
+  cube.rotation.y += 0.01;
+
+  renderer.render( scene, camera );
+
+}
+</pre>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 170 - 0
manual/fr/creating-text.html

@@ -0,0 +1,170 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Création de texte</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Création de texte">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Création de texte</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <div>
+            <p>
+              Il y a souvent des moments où vous pourriez avoir besoin d'utiliser du texte dans votre application three.js - voici
+              quelques façons de le faire.
+            </p>
+          </div>
+
+          <h2>1. DOM + CSS</h2>
+          <div>
+            <p>
+              L'utilisation de HTML est généralement la manière la plus simple et la plus rapide d'ajouter du texte. C'est la méthode
+              utilisée pour les superpositions descriptives dans la plupart des exemples three.js.
+            </p>
+            <p>Vous pouvez ajouter du contenu à un</p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+&lt;div id="info"&gt;Description&lt;/div&gt;
+</pre>
+            <p>
+              et utiliser du balisage CSS pour le positionner absolument à une position au-dessus de tous les autres avec un
+              z-index, surtout si vous exécutez three.js en plein écran.
+            </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+#info {
+  position: absolute;
+  top: 10px;
+  width: 100%;
+  text-align: center;
+  z-index: 100;
+  display:block;
+}
+</pre>
+
+          </div>
+
+
+          <h2>2. Utiliser `CSS2DRenderer` ou `CSS3DRenderer`</h2>
+          <div>
+            <p>
+              Utilisez ces moteurs de rendu pour dessiner du texte de haute qualité contenu dans des éléments DOM dans votre scène three.js.
+              C'est similaire à 1., sauf qu'avec ces moteurs de rendu, les éléments peuvent être intégrés de manière plus étroite et dynamique dans la scène.
+            </p>
+          </div>
+
+
+          <h2>3. Dessiner du texte sur un canvas et l'utiliser comme `Texture`</h2>
+          <div>
+            <p>Utilisez cette méthode si vous souhaitez dessiner facilement du texte sur un plan dans votre scène three.js.</p>
+          </div>
+
+
+          <h2>4. Créer un modèle dans votre application 3D préférée et exporter vers three.js</h2>
+          <div>
+            <p>Utilisez cette méthode si vous préférez travailler avec vos applications 3D et importer les modèles dans three.js.</p>
+          </div>
+
+
+          <h2>5. Géométrie de Texte Procédurale</h2>
+          <div>
+            <p>
+              Si vous préférez travailler purement en THREE.js ou créer des géométries de texte 3D procédurales et dynamiques,
+              vous pouvez créer un maillage dont la géométrie est une instance de THREE.TextGeometry :
+            </p>
+            <p>
+              <code>new THREE.TextGeometry( text, parameters );</code>
+            </p>
+            <p>
+              Pour que cela fonctionne, cependant, votre TextGeometry aura besoin d'une instance de THREE.Font
+              à définir sur son paramètre « font ».
+
+              Consultez la page `TextGeometry` pour plus d'informations sur la manière dont cela peut être fait, des descriptions de chaque
+              paramètre accepté, et une liste des polices JSON qui sont incluses dans la distribution THREE.js elle-même.
+            </p>
+
+            <h3>Exemples</h3>
+
+            <p>
+              [example:webgl_geometry_text WebGL / géométrie / texte]<br />
+              [example:webgl_shadowmap WebGL / shadowmap]
+            </p>
+
+            <p>
+              Si Typeface est indisponible, ou si vous souhaitez utiliser une police qui ne s'y trouve pas, il existe un tutoriel
+              avec un script python pour blender qui vous permet d'exporter du texte au format JSON de Three.js :
+              [link:http://www.jaanga.com/2012/03/blender-to-threejs-create-3d-text-with.html]
+            </p>
+
+          </div>
+
+
+          <h2>6. Polices bitmap</h2>
+          <div>
+            <p>
+              Les BMFonts (polices bitmap) permettent de regrouper les glyphes dans une seule BufferGeometry. Le rendu BMFont
+              prend en charge le retour à la ligne, l'espacement des lettres, le crénage, les champs de distance signés avec dérivées standard,
+              les champs de distance signés multicanaux, les polices multi-textures, et plus encore.
+              Voir [link:https://github.com/felixmariotto/three-mesh-ui three-mesh-ui] ou [link:https://github.com/Jam3/three-bmfont-text three-bmfont-text].
+            </p>
+            <p>
+              Des polices standard sont disponibles dans des projets comme
+              [link:https://github.com/etiennepinchon/aframe-fonts A-Frame Fonts], ou vous pouvez créer les vôtres
+              à partir de n'importe quelle police .TTF, en optimisant pour n'inclure que les caractères requis pour un projet.
+            </p>
+            <p>
+              Quelques outils utiles :
+            </p>
+            <ul>
+              <li>[link:http://msdf-bmfont.donmccurdy.com/ msdf-bmfont-web] <i>(basé sur le web)</i></li>
+              <li>[link:https://github.com/soimy/msdf-bmfont-xml msdf-bmfont-xml] <i>(ligne de commande)</i></li>
+              <li>[link:https://github.com/libgdx/libgdx/wiki/Hiero hiero] <i>(application de bureau)</i></li>
+            </ul>
+          </div>
+
+
+          <h2>7. Troika Text</h2>
+          <div>
+            <p>
+              Le paquet [link:https://www.npmjs.com/package/troika-three-text troika-three-text] rend
+              du texte antialiasé de qualité en utilisant une technique similaire à celle des BMFonts, mais fonctionne directement
+              avec n'importe quel fichier de police .TTF ou .WOFF, vous n'avez donc pas à prégénérer une texture de glyphe hors ligne. Il ajoute
+              également des capacités, notamment :
+            </p>
+            <ul>
+              <li>Effets comme les contours, les ombres portées et la courbure</li>
+              <li>La possibilité d'appliquer n'importe quel Material three.js, même un ShaderMaterial personnalisé</li>
+              <li>Prise en charge des ligatures de police, des scripts avec lettres jointes et de la mise en page de droite à gauche/bidirectionnelle</li>
+              <li>Optimisation pour de grandes quantités de texte dynamique, effectuant la majeure partie du travail hors du thread principal dans un web worker</li>
+            </ul>
+          </div>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 120 - 123
manual/fr/custom-buffergeometry.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Custom BufferGeometry</title>
+    <title>BufferGeometry Personnalisée</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Custom BufferGeometry">
+    <meta name="twitter:title" content="Three.js – BufferGeometry Personnalisée">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,47 +22,46 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Custom BufferGeometry</h1>
+        <h1>BufferGeometry Personnalisée</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> is three.js's way of representing all geometry. A <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>
-essentially a collection <em>named</em> of <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s.
-Each <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> represents an array of one type of data: positions,
-normals, colors, uv, etc... Together, the named <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s represent
-<em>parallel arrays</em> of all the data for each vertex.</p>
+          <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>
+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.
+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,
+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
+des <em>tableaux parallèles</em> de toutes les données pour chaque sommet.</p>
 <div class="threejs_center"><img src="../resources/threejs-attributes.svg" style="width: 700px"></div>
 
-<p>Above you can see we have 4 attributes: <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>.
-They represent <em>parallel arrays</em> which means that the Nth set of data in each
-attribute belongs to the same vertex. The vertex at index = 4 is highlighted
-to show that the parallel data across all attributes defines one vertex.</p>
-<p>This brings up a point, here's a diagram of a cube with one corner highlighted.</p>
+<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>.
+Ils représentent des <em>tableaux parallèles</em>, ce qui signifie que le N-ième ensemble de données de chaque
+attribut appartient au même sommet. Le sommet à l'index = 4 est mis en évidence
+pour montrer que les données parallèles à travers tous les attributs définissent un seul sommet.</p>
+<p>Cela soulève un point, voici un diagramme d'un cube avec un coin mis en évidence.</p>
 <div class="threejs_center"><img src="../resources/cube-faces-vertex.svg" style="width: 500px"></div>
 
-<p>Thinking about it that single corner needs a different normal for each face of the
-cube. A normal is info about which direction something faces. In the diagram
-the normals are presented by the arrows around the corner vertex showing that each
-face that shares that vertex position needs a normal that points in a different direction.</p>
-<p>That corner needs different UVs for each face as well. UVs are texture coordinates
-that specify which part of a texture being drawn on a triangle corresponds to that
-vertex position. You can see the green face needs that vertex to have a UV that corresponds
-to the top right corner of the F texture, the blue face needs a UV that corresponds to the
-top left corner of the F texture, and the red face needs a UV that corresponds to the bottom
-left corner of the F texture.</p>
-<p>A single <em>vertex</em> is the combination of all of its parts. If a vertex needs any
-part to be different then it must be a different vertex.</p>
-<p>As a simple example let's make a cube using <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. A cube is interesting
-because it appears to share vertices at the corners but really
-does not. For our example we'll list out all the vertices with all their data
-and then convert that data into parallel arrays and finally use those to make
-<a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>s and add them to a <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
-<p>We start with a list of all the data needed for the cube. Remember again
-that if a vertex has any unique parts it has to be a separate vertex. As such
-to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triangle,
-6 faces = 36 vertices.</p>
+<p>En y réfléchissant, ce coin unique nécessite une normale différente pour chaque face du
+cube. Une normale est une information sur la direction vers laquelle quelque chose fait face. Dans le diagramme,
+les normales sont représentées par les flèches autour du sommet d'angle, montrant que chaque
+face qui partage cette position de sommet a besoin d'une normale qui pointe dans une direction différente.</p>
+<p>Ce coin a également besoin d'UV différents pour chaque face. Les UV sont des coordonnées de texture
+qui spécifient quelle partie d'une texture dessinée sur un triangle correspond à cette
+position de sommet. Vous pouvez voir que la face verte a besoin que ce sommet ait une UV qui corresponde
+au coin supérieur droit de la texture F, la face bleue a besoin d'une UV qui corresponde au
+coin supérieur gauche de la texture F, et la face rouge a besoin d'une UV qui corresponde au coin
+inférieur gauche de la texture F.</p>
+<p>Un seul <em>sommet</em> est la combinaison de toutes ses parties. Si un sommet a besoin d'une
+partie différente, alors il doit s'agir d'un sommet différent.</p>
+<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
+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,
+puis convertir ces données en tableaux parallèles et enfin les utiliser pour créer des
+<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>
+<p>Nous commençons par une liste de toutes les données nécessaires pour le cube. Rappelez-vous encore une fois
+que si un sommet a des parties uniques, il doit s'agir d'un sommet distinct. En conséquence,
+pour faire un cube, il faut 36 sommets. 2 triangles par face, 3 sommets par triangle,
+6 faces = 36 sommets.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
-  // front
+  // avant
   { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
   { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },
   { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },
@@ -70,7 +69,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },
   { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
-  // right
+  // droite
   { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
   { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], },
@@ -78,7 +77,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], },
   { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },
   { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
-  // back
+  // arrière
   { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
   { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },
   { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], },
@@ -86,7 +85,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], },
   { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },
   { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
-  // left
+  // gauche
   { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 0], },
   { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },
   { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], },
@@ -94,7 +93,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], },
   { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },
   { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 1], },
-  // top
+  // haut
   { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 0], },
   { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], },
@@ -102,7 +101,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], },
   { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },
   { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 1], },
-  // bottom
+  // bas
   { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 0], },
   { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], },
   { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 1], },
@@ -112,7 +111,7 @@ to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triang
   { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 1], },
 ];
 </pre>
-<p>We can then translate all of that into 3 parallel arrays</p>
+<p>Nous pouvons ensuite traduire tout cela en 3 tableaux parallèles</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const positions = [];
 const normals = [];
 const uvs = [];
@@ -122,8 +121,8 @@ for (const vertex of vertices) {
   uvs.push(...vertex.uv);
 }
 </pre>
-<p>Finally we can create a <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> and then a <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> for each array
-and add it to the <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
+<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
+et l'ajouter à la <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">  const geometry = new THREE.BufferGeometry();
   const positionNumComponents = 3;
   const normalNumComponents = 3;
@@ -138,31 +137,31 @@ and add it to the <a href="/docs/#api/en/core/BufferGeometry"><code class="notra
       'uv',
       new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
 </pre>
-<p>Note that the names are significant. You must name your attributes the names
-that match what three.js expects (unless you are creating a custom shader).
-In this case <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">normal</code>, and <code class="notranslate" translate="no">uv</code>. If you want vertex colors then
-name your attribute <code class="notranslate" translate="no">color</code>.</p>
-<p>Above we created 3 JavaScript native arrays, <code class="notranslate" translate="no">positions</code>, <code class="notranslate" translate="no">normals</code> and <code class="notranslate" translate="no">uvs</code>.
-We then convert those into
+<p>Notez que les noms sont importants. Vous devez nommer vos attributs avec les noms
+que three.js attend (sauf si vous créez un shader personnalisé).
+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,
+nommez votre attribut <code class="notranslate" translate="no">color</code>.</p>
+<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>.
+Nous les avons ensuite convertis en
 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArrays</a>
-of type <code class="notranslate" translate="no">Float32Array</code>. A <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> requires a TypedArray not a native
-array. A <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> also requires you to tell it how many components there
-are per vertex. For the positions and normals we have 3 components per vertex,
-x, y, and z. For the UVs we have 2, u and v.</p>
+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.
+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
+par sommet. Pour les positions et les normales, nous avons 3 composants par sommet,
+x, y et z. Pour les UV, nous en avons 2, u et v.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-cube.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <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>
 </div>
 
 <p></p>
-<p>That's a lot of data. A small thing we can do is use indices to reference
-the vertices. Looking back at our cube data, each face is made from 2 triangles
-with 3 vertices each, 6 vertices total, but 2 of those vertices are exactly the same;
-The same position, the same normal, and the same uv.
-So, we can remove the matching vertices and then
-reference them by index. First we remove the matching vertices.</p>
+<p>C'est beaucoup de données. Une petite chose que nous pouvons faire est d'utiliser des indices pour référencer
+les sommets. En revenant à nos données de cube, chaque face est composée de 2 triangles
+avec 3 sommets chacun, soit 6 sommets au total, mais 2 de ces sommets sont exactement les mêmes ;
+La même position, la même normale et la même uv.
+Nous pouvons donc supprimer les sommets correspondants et les
+référencer par index. Nous commençons par supprimer les sommets correspondants.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
-  // front
+  // avant
   { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 0], }, // 0
   { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], }, // 1
   { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], }, // 2
@@ -170,7 +169,7 @@ reference them by index. First we remove the matching vertices.</p>
 -  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },
 -  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 1], }, // 3
-  // right
+  // droite
   { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 0], }, // 4
   { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], }, // 5
 -
@@ -178,7 +177,7 @@ reference them by index. First we remove the matching vertices.</p>
 -  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], }, // 6
   { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 1], }, // 7
-  // back
+  // arrière
   { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 0], }, // 8
   { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], }, // 9
 -
@@ -186,7 +185,7 @@ reference them by index. First we remove the matching vertices.</p>
 -  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },
   { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], }, // 10
   { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 1], }, // 11
-  // left
+  // gauche
   { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 0], }, // 12
   { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], }, // 13
 -
@@ -194,7 +193,7 @@ reference them by index. First we remove the matching vertices.</p>
 -  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },
   { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], }, // 14
   { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 1], }, // 15
-  // top
+  // haut
   { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 0], }, // 16
   { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], }, // 17
 -
@@ -202,7 +201,7 @@ reference them by index. First we remove the matching vertices.</p>
 -  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },
   { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], }, // 18
   { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 1], }, // 19
-  // bottom
+  // bas
   { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 0], }, // 20
   { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], }, // 21
 -
@@ -212,8 +211,8 @@ reference them by index. First we remove the matching vertices.</p>
   { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 1], }, // 23
 ];
 </pre>
-<p>So now we have 24 unique vertices. Then we specify 36 indices
-for the 36 vertices we need drawn to make 12 triangles by calling <a href="/docs/#api/en/core/BufferGeometry.setIndex"><code class="notranslate" translate="no">BufferGeometry.setIndex</code></a> with an array of indices.</p>
+<p>Nous avons donc maintenant 24 sommets uniques. Ensuite, nous spécifions 36 indices
+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>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.setAttribute(
     'position',
     new THREE.BufferAttribute(positions, positionNumComponents));
@@ -225,45 +224,45 @@ geometry.setAttribute(
     new THREE.BufferAttribute(uvs, uvNumComponents));
 
 +geometry.setIndex([
-+   0,  1,  2,   2,  1,  3,  // front
-+   4,  5,  6,   6,  5,  7,  // right
-+   8,  9, 10,  10,  9, 11,  // back
-+  12, 13, 14,  14, 13, 15,  // left
-+  16, 17, 18,  18, 17, 19,  // top
-+  20, 21, 22,  22, 21, 23,  // bottom
++   0,  1,  2,   2,  1,  3,  // avant
++   4,  5,  6,   6,  5,  7,  // droite
++   8,  9, 10,  10,  9, 11,  // arrière
++  12, 13, 14,  14, 13, 15,  // gauche
++  16, 17, 18,  18, 17, 19,  // haut
++  20, 21, 22,  22, 21, 23,  // bas
 +]);
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <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>
-  <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-indexed.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <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>
 </div>
 
 <p></p>
-<p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> has a <a href="/docs/#api/en/core/BufferGeometry#computeVertexNormals"><code class="notranslate" translate="no">computeVertexNormals</code></a> method for computing normals if you
-are not supplying them. Unfortunately,
-since positions can not be shared if any other part of a vertex is different,
-the results of calling <code class="notranslate" translate="no">computeVertexNormals</code> will generate seams if your
-geometry is supposed to connect to itself like a sphere or a cylinder.</p>
+<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
+ne les fournissez pas. Malheureusement,
+étant donné que les positions ne peuvent pas être partagées si une autre partie d'un sommet est différente,
+les résultats de l'appel à <code class="notranslate" translate="no">computeVertexNormals</code> généreront des coutures si votre
+géométrie est censée se connecter à elle-même comme une sphère ou un cylindre.</p>
 <div class="spread">
   <div>
     <div data-diagram="bufferGeometryCylinder"></div>
   </div>
 </div>
 
-<p>For the cylinder above the normals were created using <code class="notranslate" translate="no">computeVertexNormals</code>.
-If you look closely there is a seam on the cylinder. This is because there
-is no way to share the vertices at the start and end of the cylinder since they
-require different UVs so the function to compute them has no idea those are
-the same vertices to smooth over them. Just a small thing to be aware of.
-The solution is to supply your own normals.</p>
-<p>We can also use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArrays</a> from the start instead of native JavaScript arrays.
-The disadvantage to TypedArrays is you must specify their size up front. Of
-course that's not that large of a burden but with native arrays we can just
-<code class="notranslate" translate="no">push</code> values onto them and look at what size they end up by checking their
-<code class="notranslate" translate="no">length</code> at the end. With TypedArrays there is no push function so we need
-to do our own bookkeeping when adding values to them.</p>
-<p>In this example knowing the length up front is pretty easy since we're using
-a big block of static data to start.</p>
+<p>Pour le cylindre ci-dessus, les normales ont été créées à l'aide de <code class="notranslate" translate="no">computeVertexNormals</code>.
+Si vous regardez attentivement, il y a une couture sur le cylindre. Cela est dû au fait qu'il
+n'y a aucun moyen de partager les sommets au début et à la fin du cylindre, car ils
+nécessitent des UV différents, de sorte que la fonction pour les calculer n'a aucune idée qu'il
+s'agit des mêmes sommets à lisser. Juste une petite chose dont il faut être conscient.
+La solution est de fournir vos propres normales.</p>
+<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.
+L'inconvénient des TypedArrays est que vous devez spécifier leur taille à l'avance. Bien sûr,
+ce n'est pas une si grande contrainte, mais avec les tableaux natifs, nous pouvons simplement
+<code class="notranslate" translate="no">push</code> des valeurs et voir la taille finale en vérifiant leur
+<code class="notranslate" translate="no">length</code> à la fin. Avec les TypedArrays, il n'y a pas de fonction push, nous devons donc
+faire notre propre gestion des données lorsque nous y ajoutons des valeurs.</p>
+<p>Dans cet exemple, connaître la longueur à l'avance est assez facile, car nous utilisons
+un grand bloc de données statiques pour commencer.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const positions = [];
 -const normals = [];
 -const uvs = [];
@@ -303,30 +302,28 @@ geometry.setAttribute(
 +    new THREE.BufferAttribute(uvs, uvNumComponents));
 
 geometry.setIndex([
-   0,  1,  2,   2,  1,  3,  // front
-   4,  5,  6,   6,  5,  7,  // right
-   8,  9, 10,  10,  9, 11,  // back
-  12, 13, 14,  14, 13, 15,  // left
-  16, 17, 18,  18, 17, 19,  // top
-  20, 21, 22,  22, 21, 23,  // bottom
+   0,  1,  2,   2,  1,  3,  // avant
+   4,  5,  6,   6,  5,  7,  // droite
+   8,  9, 10,  10,  9, 11,  // arrière
+  12, 13, 14,  14, 13, 15,  // gauche
+  16, 17, 18,  18, 17, 19,  // haut
+  20, 21, 22,  22, 21, 23,  // bas
 ]);
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <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>
-  <a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-typedarrays.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <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>
 </div>
 
 <p></p>
-<p>A good reason to use typedarrays is if you want to dynamically update any
-part of the vertices.</p>
-<p>I couldn't think of a really good example of dynamically updating the vertices
-so I decided to make a sphere and move each quad in and out from the center. Hopefully
-it's a useful example.</p>
-<p>Here's the code to generate positions and indices for a sphere. The code
-is sharing vertices within a quad but it's not sharing vertices between
-quads because we want to be able to move each quad separately.</p>
-<p>Because I'm lazy I used a small hierarchy of 3 <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> objects to compute
-sphere points. How this works is explained in <a href="optimize-lots-of-objects.html">the article on optimizing lots of objects</a>.</p>
+<p>Une bonne raison d'utiliser des typedarrays est si vous souhaitez mettre à jour dynamiquement une partie
+des sommets.</p>
+<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>
+<p>Voici le code pour générer les positions et les indices d'une sphère. Le code
+partage les sommets au sein d'un quadricône, mais ne partage pas les sommets entre
+les quadricônes, car nous voulons pouvoir déplacer chaque quadricône séparément.</p>
+<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
+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>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeSpherePositions(segmentsAround, segmentsDown) {
   const numVertices = segmentsAround * segmentsDown * 6;
   const numComponents = 3;
@@ -377,16 +374,16 @@ sphere points. How this works is explained in <a href="optimize-lots-of-objects.
   return {positions, indices};
 }
 </pre>
-<p>We can then call it like this</p>
+<p>Nous pouvons ensuite l'appeler comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const segmentsAround = 24;
 const segmentsDown = 16;
 const {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);
 </pre>
-<p>Because positions returned are unit sphere positions so they are exactly the same
-values we need for normals so we can just duplicated them for the normals.</p>
+<p>Comme les positions retournées sont des positions de sphère unitaire, ce sont exactement les mêmes
+valeurs dont nous avons besoin pour les normales, nous pouvons donc simplement les dupliquer pour les normales.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const normals = positions.slice();
 </pre>
-<p>And then we setup the attributes like before</p>
+<p>Et ensuite nous configurons les attributs comme auparavant</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.BufferGeometry();
 const positionNumComponents = 3;
 const normalNumComponents = 3;
@@ -401,10 +398,10 @@ geometry.setAttribute(
     new THREE.BufferAttribute(normals, normalNumComponents));
 geometry.setIndex(indices);
 </pre>
-<p>I've highlighted a few differences. We save a reference to the position attribute.
-We also mark it as dynamic. This is a hint to THREE.js that we're going to be changing
-the contents of the attribute often.</p>
-<p>In our render loop we update the positions based off their normals every frame.</p>
+<p>J'ai mis en évidence quelques différences. Nous sauvegardons une référence à l'attribut de position.
+Nous le marquons également comme dynamique. C'est une indication pour THREE.js que nous allons modifier
+souvent le contenu de l'attribut.</p>
+<p>Dans notre boucle de rendu, nous mettons à jour les positions en fonction de leurs normales à chaque image.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const temp = new THREE.Vector3();
 
 ...
@@ -421,15 +418,15 @@ for (let i = 0; i &lt; positions.length; i += 3) {
 }
 positionAttribute.needsUpdate = true;
 </pre>
-<p>And we set <code class="notranslate" translate="no">positionAttribute.needsUpdate</code> to tell THREE.js to use our changes.</p>
+<p>Et nous réglons <code class="notranslate" translate="no">positionAttribute.needsUpdate</code> à true pour dire à THREE.js d'utiliser nos changements.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-buffergeometry-dynamic.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/custom-buffergeometry-dynamic.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <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>
 </div>
 
 <p></p>
-<p>I hope these were useful examples of how to use <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> directly to
-make your own geometry and how to dynamically update the contents of a
+<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
+créer votre propre géométrie et comment mettre à jour dynamiquement le contenu d'un
 <a href="/docs/#api/en/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>.</p>
 <!-- needed in English only to prevent warning from outdated translations -->
 <p><a href="resources/threejs-geometry.svg"></a></p>

+ 41 - 5
manual/fr/debugging-glsl.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Debugging - GLSL</title>
+    <title>Débogage - GLSL</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Debugging - GLSL">
+    <meta name="twitter:title" content="Three.js – Débogage - GLSL">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,48 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Debugging - GLSL</h1>
+        <h1>Débogage - GLSL</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/debugging-glsl.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Ce site ne vous enseigne pas le GLSL, tout comme il ne vous enseigne pas le JavaScript. Ce sont des sujets très vastes. Si vous souhaitez apprendre le GLSL, consultez
+<a href="https://webglfundamentals.org">ces articles</a> comme point de départ.</p>
+<p>Si vous connaissez déjà le GLSL, voici quelques conseils pour le débogage.</p>
+<p>Lorsque je crée un nouveau shader GLSL et que rien n'apparaît, la première chose que je fais généralement est de modifier le shader de fragment pour qu'il renvoie une couleur unie. Par exemple, tout en bas du shader, je pourrais mettre</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">void main() {
+
+  ...
+
+  gl_FragColor = vec4(1, 0, 0, 1);  // red
+}
+</pre>
+<p>Si je vois l'objet que j'essayais de dessiner, je sais que le problème est lié à mon shader de fragment. Cela peut être n'importe quoi, comme de mauvaises textures, des uniforms non initialisés, des uniforms avec les mauvaises valeurs, mais au moins j'ai une direction où chercher.</p>
+<p>Pour tester certains de ces points, je pourrais commencer par essayer de dessiner certaines des entrées. Par exemple, si j'utilise des normales dans le shader de fragment, je pourrais ajouter</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1);
+</pre>
+<p>Les normales vont de -1 à +1, donc en multipliant par 0,5 et en ajoutant 0,5, nous obtenons des valeurs qui vont de 0,0 à 1,0, ce qui les rend utiles pour les couleurs.</p>
+<p>Essayez ceci avec des choses dont vous savez qu'elles fonctionnent et vous commencerez à avoir une idée de ce à quoi ressemblent les normales <em>normalement</em>. Si vos normales ne semblent pas normales, vous avez une piste où chercher. Si vous manipulez les normales dans le shader de fragment, vous pouvez utiliser la même technique pour dessiner le résultat de cette manipulation.</p>
+<div class="threejs_center"><img src="../resources/images/standard-primitive-normals.jpg" style="width: 650px;"></div>
+
+<p>De même, si nous utilisons des textures, il y aura des coordonnées de texture et nous pouvons les dessiner avec quelque chose comme</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">gl_FragColor = vec4(fract(vUv), 0, 1);
+</pre>
+<p>Le <code class="notranslate" translate="no">fract</code> est là au cas où nous utiliserions des coordonnées de texture qui dépassent la plage 0 à 1. C'est courant si <code class="notranslate" translate="no">texture.repeat</code> est défini sur une valeur supérieure à 1.</p>
+<div class="threejs_center"><img src="../resources/images/standard-primitive-uvs.jpg" style="width: 650px;"></div>
+
+<p>Vous pouvez faire des choses similaires pour toutes les valeurs de votre shader de fragment. Déterminez quelle est leur plage probable, ajoutez du code pour définir <code class="notranslate" translate="no">gl_FragColor</code> avec cette plage mise à l'échelle de 0,0 à 1,0</p>
+<p>Pour vérifier les textures, essayez un <a href="/docs/#api/en/textures/CanvasTexture"><code class="notranslate" translate="no">CanvasTexture</code></a> ou un <a href="/docs/#api/en/textures/DataTexture"><code class="notranslate" translate="no">DataTexture</code></a> dont vous savez qu'il fonctionne.</p>
+<p>Inversement, si après avoir défini <code class="notranslate" translate="no">gl_FragColor</code> sur rouge je ne vois toujours rien, alors j'ai une indication que mon problème pourrait être lié aux choses relatives au shader de vertex. Certaines matrices pourraient être incorrectes ou mes attributs pourraient contenir de mauvaises données ou être mal configurés.</p>
+<p>Je regarderais d'abord les matrices. Je pourrais mettre un point d'arrêt juste après mon appel à <code class="notranslate" translate="no">renderer.render(scene, camera)</code> et commencer ensuite à développer les éléments dans l'inspecteur. La matrice monde et la matrice de projection de la caméra ne sont-elles pas au moins pleines de <code class="notranslate" translate="no">NaN</code>s ? En développant la scène et en regardant ses <code class="notranslate" translate="no">children</code>, je vérifierais que les matrices monde semblent raisonnables (pas de <code class="notranslate" translate="no">NaN</code>s) et que les 4 dernières valeurs de chaque matrice semblent raisonnables pour ma scène. Si j'attends que ma scène fasse 50x50x50 unités et qu'une matrice affiche 552352623.123, il est clair que quelque chose ne va pas.</p>
+<div class="threejs_center"><img src="../resources/images/inspect-matrices.gif"></div>
+
+<p>Tout comme nous l'avons fait pour le shader de fragment, nous pouvons également dessiner les valeurs du shader de vertex en les passant au shader de fragment. Déclarez une variable varying dans les deux et passez la valeur dont vous n'êtes pas sûr qu'elle soit correcte. En fait, si mon shader utilise des normales, je modifierai le shader de fragment pour les afficher comme mentionné ci-dessus, puis je définirai simplement <code class="notranslate" translate="no">vNormal</code> à la valeur que je souhaite afficher, mais mise à l'échelle de sorte que les valeurs aillent de 0,0 à 1,0. Je regarde ensuite les résultats et vois s'ils correspondent à mes attentes.</p>
+<p>Une autre bonne chose à faire est d'utiliser un shader plus simple. Pouvez-vous dessiner vos données avec un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> ? Si oui, essayez-le et assurez-vous qu'il s'affiche comme prévu.</p>
+<p>Sinon, quel est le shader de vertex le plus simple qui vous permettra de visualiser votre géométrie ? Généralement, c'est aussi simple que</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">gl_Position = projection * modelView * vec4(position.xyz, 1);
+</pre>
+<p>Si cela fonctionne, commencez à ajouter vos modifications petit à petit.</p>
+<p>Une autre chose que vous pouvez faire est d'utiliser l'<a href="https://chrome.google.com/webstore/detail/shader-editor/ggeaidddejpbakgafapihjbgdlbbbpob?hl=en">extension Shader Editor pour Chrome</a> ou similaire pour d'autres navigateurs. C'est un excellent moyen de voir comment fonctionnent d'autres shaders. C'est aussi pratique car vous pouvez apporter certaines des modifications suggérées ci-dessus en direct pendant que le code s'exécute.</p>
 
         </div>
       </div>

+ 336 - 5
manual/fr/debugging-javascript.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Debugging JavaScript</title>
+    <title>Débogage JavaScript</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Debugging JavaScript">
+    <meta name="twitter:title" content="Three.js – Débogage JavaScript">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,343 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Debugging JavaScript</h1>
+        <h1>Débogage JavaScript</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/debugging-javascript.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>La majeure partie de cet article ne concerne pas directement THREE.js, mais plutôt le débogage de JavaScript en général. Cela semblait important car de nombreuses personnes qui débutent avec THREE.js commencent également avec JavaScript, j'espère donc que cela pourra les aider à résoudre plus facilement les problèmes qu'ils rencontrent.</p>
+<p>Le débogage est un vaste sujet et je ne peux probablement pas couvrir tout ce qu'il y a à savoir, mais si vous débutez en JavaScript, voici quelques pistes. Je vous suggère fortement de prendre le temps de les apprendre. Elles vous aideront énormément dans votre apprentissage.</p>
+<h2 id="learn-your-browser-s-developer-tools">Apprenez à utiliser les outils de développement de votre navigateur</h2>
+<p>Tous les navigateurs disposent d'outils de développement.
+<a href="https://developers.google.com/web/tools/chrome-devtools/">Chrome</a>,
+<a href="https://developer.mozilla.org/en-US/docs/Tools">Firefox</a>,
+<a href="https://developer.apple.com/safari/tools/">Safari</a>,
+<a href="https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide">Edge</a>.</p>
+<p>Dans Chrome, vous pouvez cliquer sur l'icône <code class="notranslate" translate="no">⋮</code>, choisir Plus d'outils -> Outils de développement pour accéder aux outils de développement. Un raccourci clavier est également affiché.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-chrome.jpg" style="width: 789px;"></div>
+
+<p>Dans Firefox, vous cliquez sur l'icône <code class="notranslate" translate="no">☰</code>, choisissez "Développeur Web", puis "Activer/Désactiver les outils"</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-firefox.jpg" style="width: 786px;"></div>
+
+<p>Dans Safari, vous devez d'abord activer le menu Développement depuis les Préférences avancées de Safari.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-enable-safari.jpg" style="width: 775px;"></div>
+
+<p>Puis, dans le menu Développement, vous pouvez choisir "Afficher/Connecter l'inspecteur web".</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-safari.jpg" style="width: 777px;"></div>
+
+<p>Avec Chrome, vous pouvez également <a href="https://developers.google.com/web/tools/chrome-devtools/remote-debugging/">utiliser Chrome sur votre ordinateur pour déboguer des pages web s'exécutant dans Chrome sur votre téléphone ou tablette Android</a>. De même avec Safari, vous pouvez
+<a href="https://www.google.com/search?q=safari+remote+debugging+ios">utiliser votre ordinateur pour déboguer des pages web s'exécutant dans Safari sur des iPhones et iPads</a>.</p>
+<p>Je suis plus familier avec Chrome, donc ce guide utilisera Chrome comme exemple pour faire référence aux outils, mais la plupart des navigateurs ont des fonctionnalités similaires, il devrait donc être facile d'appliquer ce qui est dit ici à tous les navigateurs.</p>
+<h2 id="turn-off-the-cache">Désactiver le cache</h2>
+<p>Les navigateurs essaient de réutiliser les données qu'ils ont déjà téléchargées. C'est excellent pour les utilisateurs, car si vous visitez un site web une deuxième fois, de nombreux fichiers utilisés pour afficher le site n'auront pas à être téléchargés à nouveau.</p>
+<p>D'autre part, cela peut être problématique pour le développement web. Vous modifiez un fichier sur votre ordinateur, rechargez la page, et vous ne voyez pas les changements car le navigateur utilise la version qu'il a obtenue la dernière fois.</p>
+<p>Une solution pendant le développement web est de désactiver le cache. Ainsi, le navigateur obtiendra toujours les versions les plus récentes de vos fichiers.</p>
+<p>Choisissez d'abord les paramètres dans le menu du coin</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-chrome-settings.jpg" style="width: 778px"></div>
+
+<p>Puis choisissez "Désactiver le cache (lorsque les outils de développement sont ouverts)".</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-chrome-disable-cache.jpg" style="width: 779px"></div>
+
+<h2 id="use-the-javascript-console">Utiliser la console JavaScript</h2>
+<p>Dans tous les outils de développement se trouve une <em>console</em>. Elle affiche les avertissements et les messages d'erreur.</p>
+<p><strong> LISEZ LES MESSAGES !! </strong></p>
+<p>Typiquement, il ne devrait y avoir que 1 ou 2 messages.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-no-errors.jpg" style="width: 779px"></div>
+
+<p>Si vous en voyez d'autres, <strong>LISEZ-LES</strong>. Par exemple :</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-errors.jpg" style="width: 779px"></div>
+
+<p>J'ai mal orthographié "three" en "threee"</p>
+<p>Vous pouvez également afficher vos propres informations dans la console avec <code class="notranslate" translate="no">console.log</code>, comme ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">console.log(someObject.position.x, someObject.position.y, someObject.position.z);
+</pre>
+<p>Encore mieux, si vous affichez un objet, vous pouvez l'inspecter. Par exemple, si nous affichons l'objet scène racine de l'<a href="load-gltf.html">article sur les gLTF</a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">  {
+    const gltfLoader = new GLTFLoader();
+    gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {
+      const root = gltf.scene;
+      scene.add(root);
++      console.log(root);
+</pre>
+<p>Nous pouvons ensuite développer cet objet dans la console JavaScript</p>
+<div class="threejs_center"><img class="border" src="../resources/images/devtools-console-object.gif"></div>
+
+<p>Vous pouvez également utiliser <code class="notranslate" translate="no">console.error</code> qui rapporte le message en rouge et inclut une trace de pile.</p>
+<h2 id="put-data-on-screen">Afficher des données à l'écran</h2>
+<p>Une autre méthode évidente mais souvent négligée est d'ajouter des balises <code class="notranslate" translate="no">&lt;div&gt;</code> ou <code class="notranslate" translate="no">&lt;pre&gt;</code> et d'y insérer des données.</p>
+<p>La manière la plus évidente est de créer des éléments HTML</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
++&lt;div id="debug"&gt;
++  &lt;div&gt;x:&lt;span id="x"&gt;&lt;/span&gt;&lt;/div&gt;
++  &lt;div&gt;y:&lt;span id="y"&gt;&lt;/span&gt;&lt;/div&gt;
++  &lt;div&gt;z:&lt;span id="z"&gt;&lt;/span&gt;&lt;/div&gt;
++&lt;/div&gt;
+</pre>
+<p>Stylez-les pour qu'ils restent au-dessus du canevas. (en supposant que votre canevas remplisse la page)</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;style&gt;
+#debug {
+  position: absolute;
+  left: 1em;
+  top: 1em;
+  padding: 1em;
+  background: rgba(0, 0, 0, 0.8);
+  color: white;
+  font-family: monospace;
+}
+&lt;/style&gt;
+</pre>
+<p>Puis recherchez les éléments et définissez leur contenu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// au moment de l'initialisation
+const xElem = document.querySelector('#x');
+const yElem = document.querySelector('#y');
+const zElem = document.querySelector('#z');
+
+// au moment du rendu ou de la mise à jour
+xElem.textContent = someObject.position.x.toFixed(3);
+yElem.textContent = someObject.position.y.toFixed(3);
+zElem.textContent = someObject.position.z.toFixed(3);
+</pre>
+<p>C'est plus utile pour les valeurs en temps réel</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/debug-js-html-elements.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/debug-js-html-elements.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Une autre façon d'afficher des données à l'écran est de créer un logger à effacement. Je viens d'inventer ce terme, mais de nombreux jeux sur lesquels j'ai travaillé ont utilisé cette solution. L'idée est d'avoir un tampon qui affiche des messages pour une seule image. Toute partie de votre code qui souhaite afficher des données appelle une fonction pour ajouter des données à ce tampon à chaque image. C'est beaucoup moins de travail que de créer un élément par donnée comme ci-dessus.</p>
+<p>Par exemple, modifions le HTML ci-dessus pour qu'il soit juste ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
+&lt;div id="debug"&gt;
+  &lt;pre&gt;&lt;/pre&gt;
+&lt;/div&gt;
+</pre>
+<p>Et créons une classe simple pour gérer ce <em>tampon d'effacement arrière</em>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ClearingLogger {
+  constructor(elem) {
+    this.elem = elem;
+    this.lines = [];
+  }
+  log(...args) {
+    this.lines.push([...args].join(' '));
+  }
+  render() {
+    this.elem.textContent = this.lines.join('\n');
+    this.lines = [];
+  }
+}
+</pre>
+<p>Ensuite, créons un exemple simple qui, chaque fois que nous cliquons avec la souris, crée un maillage qui se déplace dans une direction aléatoire pendant 2 secondes. Nous commencerons avec l'un des exemples de l'article sur <a href="responsive.html">rendre les choses réactives</a></p>
+<p>Voici le code qui ajoute un nouveau <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> chaque fois que nous cliquons avec la souris</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.SphereGeometry();
+const material = new THREE.MeshBasicMaterial({color: 'red'});
+
+const things = [];
+
+function rand(min, max) {
+  if (max === undefined) {
+    max = min;
+    min = 0;
+  }
+  return Math.random() * (max - min) + min;
+}
+
+function createThing() {
+  const mesh = new THREE.Mesh(geometry, material);
+  scene.add(mesh);
+  things.push({
+    mesh,
+    timer: 2,
+    velocity: new THREE.Vector3(rand(-5, 5), rand(-5, 5), rand(-5, 5)),
+  });
+}
+
+canvas.addEventListener('click', createThing);
+</pre>
+<p>Et voici le code qui déplace les maillages que nous avons créés, les enregistre, et les supprime lorsque leur minuterie est écoulée</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const logger = new ClearingLogger(document.querySelector('#debug pre'));
+
+let then = 0;
+function render(now) {
+  now *= 0.001;  // convertir en secondes
+  const deltaTime = now - then;
+  then = now;
+
+  ...
+
+  logger.log('fps:', (1 / deltaTime).toFixed(1));
+  logger.log('num things:', things.length);
+  for (let i = 0; i &lt; things.length;) {
+    const thing = things[i];
+    const mesh = thing.mesh;
+    const pos = mesh.position;
+    logger.log(
+        'timer:', thing.timer.toFixed(3),
+        'pos:', pos.x.toFixed(3), pos.y.toFixed(3), pos.z.toFixed(3));
+    thing.timer -= deltaTime;
+    if (thing.timer &lt;= 0) {
+      // supprimer cet élément. Notez que nous n'avançons pas `i`
+      things.splice(i, 1);
+      scene.remove(mesh);
+    } else {
+      mesh.position.addScaledVector(thing.velocity, deltaTime);
+      ++i;
+    }
+  }
+
+  renderer.render(scene, camera);
+  logger.render();
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Cliquez maintenant plusieurs fois avec la souris dans l'exemple ci-dessous</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/debug-js-clearing-logger.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/debug-js-clearing-logger.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<h2 id="query-parameters">Paramètres de requête</h2>
+<p>Une autre chose à retenir est que les pages web peuvent se voir passer des données soit via des paramètres de requête, soit via l'ancre, parfois appelée la recherche et le hachage.</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">https://domain/path/?query#anchor
+</pre><p>Vous pouvez l'utiliser pour rendre des fonctionnalités optionnelles ou passer des paramètres.</p>
+<p>Par exemple, prenons l'exemple précédent et faisons en sorte que les informations de débogage n'apparaissent que si nous mettons <code class="notranslate" translate="no">?debug=true</code> dans l'URL.</p>
+<p>Nous avons d'abord besoin de code pour analyser la chaîne de requête</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">/**
+  * Renvoie les paramètres de requête sous forme d'objet clé/valeur.
+  * Exemple : Si les paramètres de requête sont
+  *
+  *    abc=123&amp;def=456&amp;name=gman
+  *
+  * Alors `getQuery()` renverra un objet comme
+  *
+  *    {
+  *      abc: '123',
+  *      def: '456',
+  *      name: 'gman',
+  *    }
+  */
+function getQuery() {
+  return Object.fromEntries(new URLSearchParams(window.location.search).entries());
+}
+</pre>
+<p>Nous pourrions ensuite faire en sorte que l'élément de débogage ne s'affiche pas par défaut</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
++&lt;div id="debug" style="display: none;"&gt;
+  &lt;pre&gt;&lt;/pre&gt;
+&lt;/div&gt;
+</pre>
+<p>Ensuite, dans le code, nous lisons les paramètres et choisissons de rendre visible les informations de débogage si et seulement si <code class="notranslate" translate="no">?debug=true</code> est passé en paramètre</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const query = getQuery();
+const debug = query.debug === 'true';
+const logger = debug
+   ? new ClearingLogger(document.querySelector('#debug pre'))
+   : new DummyLogger();
+if (debug) {
+  document.querySelector('#debug').style.display = '';
+}
+</pre>
+<p>Nous avons également créé un <code class="notranslate" translate="no">DummyLogger</code> qui ne fait rien et avons choisi de l'utiliser si <code class="notranslate" translate="no">?debug=true</code> n'a pas été passé en paramètre.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DummyLogger {
+  log() {}
+  render() {}
+}
+</pre>
+<p>Vous pouvez voir si nous utilisons cette URL :</p>
+<p><a target="_blank" href="../examples/debug-js-params.html">debug-js-params.html</a></p>
+<p>il n'y a pas d'informations de débogage, mais si nous utilisons cette URL :</p>
+<p><a target="_blank" href="../examples/debug-js-params.html?debug=true">debug-js-params.html?debug=true</a></p>
+<p>il y a des informations de débogage.</p>
+<p>Plusieurs paramètres peuvent être passés en les séparant par '&amp;', comme dans <code class="notranslate" translate="no">somepage.html?someparam=somevalue&amp;someotherparam=someothervalue</code>. En utilisant des paramètres comme ceci, nous pouvons passer toutes sortes d'options. Peut-être <code class="notranslate" translate="no">speed=0.01</code> pour ralentir notre application afin de faciliter la compréhension de quelque chose, ou <code class="notranslate" translate="no">showHelpers=true</code> pour indiquer s'il faut ajouter des helpers qui affichent les lumières, les ombres ou le frustum de la caméra vus dans d'autres leçons.</p>
+<h2 id="learn-to-use-the-debugger">Apprenez à utiliser le Débogueur</h2>
+<p>Chaque navigateur dispose d'un débogueur où vous pouvez mettre votre programme en pause, l'exécuter pas à pas ligne par ligne et inspecter toutes les variables.</p>
+<p>Vous apprendre à utiliser un débogueur est un sujet trop vaste pour cet article, mais voici quelques liens</p>
+<ul>
+<li><a href="https://developers.google.com/web/tools/chrome-devtools/javascript/">Démarrez avec le débogage JavaScript dans les outils de développement de Chrome</a></li>
+<li><a href="https://javascript.info/debugging-chrome">Débogage dans Chrome</a></li>
+<li><a href="https://hackernoon.com/tips-and-tricks-for-debugging-in-chrome-developer-tools-458ade27c7ab">Conseils et astuces pour le débogage dans les outils de développement de Chrome</a></li>
+</ul>
+<h2 id="check-for-nan-in-the-debugger-or-elsewhere">Vérifiez la présence de <code class="notranslate" translate="no">NaN</code> dans le débogueur ou ailleurs</h2>
+<p><code class="notranslate" translate="no">NaN</code> est l'abréviation de Not A Number (Pas un Nombre). C'est la valeur que JavaScript attribuera lorsque vous faites quelque chose qui n'a pas de sens mathématique.</p>
+<p>Voici un exemple simple :</p>
+<div class="threejs_center"><img class="border" src="../resources/images/nan-banana.png" style="width: 180px;"></div>
+
+<p>Souvent, quand je crée quelque chose et que rien n'apparaît à l'écran, je vérifie certaines valeurs et si je vois <code class="notranslate" translate="no">NaN</code>, j'ai instantanément un point de départ pour chercher.</p>
+<p>À titre d'exemple, lorsque j'ai commencé à créer le chemin pour l'<a href="load-gltf.html">article sur le chargement de fichiers gLTF</a>, j'ai créé une courbe en utilisant la classe <a href="/docs/#api/en/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a> qui crée une courbe 2D.</p>
+<p>J'ai ensuite utilisé cette courbe pour déplacer les voitures comme ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">curve.getPointAt(zeroToOnePointOnCurve, car.position);
+</pre>
+<p>En interne, <code class="notranslate" translate="no">curve.getPointAt</code> appelle la fonction <code class="notranslate" translate="no">set</code> sur l'objet passé comme deuxième argument. Dans ce cas, ce deuxième argument est <code class="notranslate" translate="no">car.position</code> qui est un <a href="/docs/#api/en/math/Vector3"><code class="notranslate" translate="no">Vector3</code></a>. La fonction <code class="notranslate" translate="no">set</code> de <a href="/docs/#api/en/math/Vector3"><code class="notranslate" translate="no">Vector3</code></a> nécessite 3 arguments (x, y et z) mais <a href="/docs/#api/en/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a> est une courbe 2D, elle appelle donc <code class="notranslate" translate="no">car.position.set</code> avec juste x et y.</p>
+<p>Le résultat est que <code class="notranslate" translate="no">car.position.set</code> définit x sur x, y sur y, et z sur <code class="notranslate" translate="no">undefined</code>.</p>
+<p>Un rapide coup d'œil dans le débogueur sur la <code class="notranslate" translate="no">matrixWorld</code> de la voiture a montré un tas de valeurs <code class="notranslate" translate="no">NaN</code>.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/debugging-nan.gif" style="width: 476px;"></div>
+
+<p>Voir que la matrice contenait des <code class="notranslate" translate="no">NaN</code> suggérait que quelque chose comme <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">rotation</code>, <code class="notranslate" translate="no">scale</code> ou une autre fonction qui affecte cette matrice avait de mauvaises données. En remontant à partir de là, il a été facile de trouver le problème.</p>
+<p>En plus de <code class="notranslate" translate="no">NaN</code>, il y a aussi <code class="notranslate" translate="no">Infinity</code> qui est un signe similaire qu'il y a un bug mathématique quelque part.</p>
+<h2 id="look-in-the-code-">Regardez le code !</h2>
+<p>THREE.js est Open Source. N'ayez pas peur de regarder le code ! Vous pouvez regarder à l'intérieur sur <a href="https://github.com/mrdoob/three.js">github</a>. Vous pouvez également regarder à l'intérieur en entrant dans les fonctions du débogueur.</p>
+<h2 id="put-requestanimationframe-at-bottom-of-your-render-function-">Mettez <code class="notranslate" translate="no">requestAnimationFrame</code> en bas de votre fonction de rendu.</h2>
+<p>Je vois souvent ce schéma</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
+   requestAnimationFrame(render);
+
+   // -- faire des choses --
+
+   renderer.render(scene, camera);
+}
+requestAnimationFrame(render);
+</pre>
+<p>Je suggérerais de mettre l'appel à <code class="notranslate" translate="no">requestAnimationFrame</code> en bas, comme ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
+   // -- faire des choses --
+
+   renderer.render(scene, camera);
+
+   requestAnimationFrame(render);
+}
+requestAnimationFrame(render);
+</pre>
+<p>La raison principale est que cela signifie que votre code s'arrêtera si vous avez une erreur. Mettre <code class="notranslate" translate="no">requestAnimationFrame</code> en haut signifie que votre code continuera de s'exécuter même si vous avez une erreur puisque vous avez déjà demandé une autre image. À mon avis, il vaut mieux trouver ces erreurs que les ignorer. Elles pourraient facilement être la raison pour laquelle quelque chose n'apparaît pas comme vous l'attendez, mais à moins que votre code ne s'arrête, vous pourriez même ne pas le remarquer.</p>
+<h2 id="check-your-units-">Vérifiez vos unités !</h2>
+<p>Cela signifie essentiellement savoir, par exemple, quand utiliser des degrés plutôt que des radians. Il est regrettable que THREE.js n'utilise pas uniformément les mêmes unités partout. De mémoire, le champ de vision de la caméra est en degrés. Tous les autres angles sont en radians.</p>
+<p>L'autre point à surveiller est la taille de vos unités mondiales. Jusqu'à récemment, les applications 3D pouvaient choisir la taille d'unité qu'elles voulaient. Une application pouvait choisir 1 unité = 1 cm. Une autre pouvait choisir 1 unité = 1 pied. Il est toujours vrai que vous pouvez choisir les unités que vous voulez pour certaines applications. Cela dit, THREE.js suppose 1 unité = 1 mètre. C'est important pour des choses comme le rendu basé sur la physique qui utilise des mètres pour calculer les effets d'éclairage. C'est également important pour la RA et la RV qui doivent gérer des unités du monde réel, comme l'emplacement de votre téléphone ou des contrôleurs VR.</p>
+<h2 id="making-a-minimal-complete-verifiable-example-for-stack-overflow">Créer un <em>Exemple Minimal, Complet et Vérifiable</em> pour Stack Overflow</h2>
+<p>Si vous décidez de poser une question sur THREE.js, il est presque toujours requis de fournir un MCVE, qui signifie Exemple Minimal, Complet et Vérifiable.</p>
+<p>La partie <strong>Minimale</strong> est importante. Disons que vous avez un problème avec le mouvement le long du chemin dans le dernier exemple de l'<a href="load-gltf.html">article sur le chargement de gLTF</a>. Cet exemple contient de nombreuses parties. En les listant, il y a :</p>
+<ol>
+<li>Beaucoup de HTML</li>
+<li>Du CSS</li>
+<li>Lumières</li>
+<li>Ombres</li>
+<li>Code lil-gui pour manipuler les ombres</li>
+<li>Code pour charger un fichier .GLTF</li>
+<li>Code pour redimensionner le canevas.</li>
+<li>Code pour déplacer les voitures le long des chemins</li>
+</ol>
+<p>C'est assez énorme. Si votre question ne concerne que la partie suivant le chemin, vous pouvez retirer la majeure partie du HTML car vous n'avez besoin que d'une balise <code class="notranslate" translate="no">&lt;canvas&gt;</code> et d'une balise <code class="notranslate" translate="no">&lt;script&gt;</code> pour THREE.js. Vous pouvez retirer le code CSS et le code de redimensionnement. Vous pouvez retirer le code .GLTF car vous ne vous souciez que du chemin. Vous pouvez retirer les lumières et les ombres en utilisant un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>. Vous pouvez certainement retirer le code lil-gui. Le code crée un plan de sol avec une texture. Il serait plus simple d'utiliser un <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>. Enfin, si notre question concerne le déplacement d'objets sur un chemin, nous pourrions simplement utiliser des cubes sur le chemin au lieu de modèles de voitures chargés.</p>
+<p>Voici un exemple plus minimal prenant en compte tout ce qui précède. Il est passé de 271 lignes à 135. Nous pourrions envisager de le réduire encore plus en simplifiant notre chemin. Peut-être qu'un chemin avec 3 ou 4 points fonctionnerait aussi bien que notre chemin avec 21 points.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/debugging-mcve.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/debugging-mcve.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'ai gardé l'<code class="notranslate" translate="no">OrbitController</code> simplement parce qu'il est utile pour que d'autres personnes puissent déplacer la caméra et comprendre ce qui se passe, mais en fonction de votre problème, vous pourriez également être en mesure de le supprimer.</p>
+<p>La meilleure chose à propos de la création d'un MCVE est que nous résolvons souvent notre propre problème. Le processus consistant à supprimer tout ce qui n'est pas nécessaire et à créer le plus petit exemple possible reproduisant le problème nous conduit le plus souvent à notre bug.</p>
+<p>De plus, c'est respectueux du temps de toutes les personnes à qui vous demandez de regarder votre code sur Stack Overflow. En créant l'exemple minimal, vous leur facilitez grandement la tâche de vous aider. Vous apprendrez également au cours du processus.</p>
+<p>Il est également important, lorsque vous allez sur Stack Overflow pour poster votre question, de <strong>mettre votre code <a href="https://stackoverflow.blog/2014/09/16/introducing-runnable-javascript-css-and-html-code-snippets/">dans un extrait (snippet)</a>.</strong> Bien sûr, vous êtes libre d'utiliser JSFiddle ou Codepen ou un site similaire pour tester votre MCVE, mais une fois que vous postez réellement votre question sur Stack Overflow, vous êtes tenu de mettre le code pour reproduire votre problème <strong>dans la question elle-même</strong>. En créant un extrait, vous remplissez cette exigence.</p>
+<p>Notez également que tous les exemples en direct sur ce site devraient fonctionner comme des extraits. Copiez simplement les parties HTML, CSS et JavaScript dans les sections respectives de l'<a href="https://stackoverflow.blog/2014/09/16/introducing-runnable-javascript-css-and-html-code-snippets/">éditeur d'extraits</a>. N'oubliez pas d'essayer de retirer les parties qui ne sont pas pertinentes pour votre problème et d'essayer de rendre votre code le plus minimal possible.</p>
+<p>Suivez ces suggestions et vous aurez beaucoup plus de chances d'obtenir de l'aide pour votre problème.</p>
+<h2 id="use-a-meshbasicmaterial-">Utilisez un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a></h2>
+<p>Étant donné que le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> n'utilise pas de lumières, c'est un moyen d'éliminer les raisons pour lesquelles quelque chose pourrait ne pas s'afficher. Si vos objets s'affichent en utilisant le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> mais pas avec les matériaux que vous utilisiez, alors vous savez que le problème vient probablement des matériaux ou des lumières et non d'une autre partie du code.</p>
+<h2 id="check-your-near-and-far-settings-for-your-camera">Vérifiez les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> de votre caméra</h2>
+<p>Une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> a des paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> qui sont couverts dans l'<a href="cameras.html">article sur les caméras</a>. Assurez-vous qu'ils sont définis pour correspondre à l'espace qui contient vos objets. Vous pourriez même les définir <strong>temporairement</strong> à quelque chose de grand comme <code class="notranslate" translate="no">near</code> = 0.001 et <code class="notranslate" translate="no">far</code> = 1000000. Vous rencontrerez probablement des problèmes de résolution de profondeur, mais vous pourrez au moins voir vos objets à condition qu'ils soient devant la caméra.</p>
+<h2 id="check-your-scene-is-in-front-of-the-camera">Vérifiez que votre scène est devant la caméra</h2>
+<p>Parfois, les choses n'apparaissent pas parce qu'elles ne sont pas devant la caméra. Si votre caméra n'est pas contrôlable, essayez d'ajouter un contrôle de caméra comme l'<code class="notranslate" translate="no">OrbitController</code> afin de pouvoir regarder autour de vous et trouver votre scène. Ou, essayez de cadrer la scène en utilisant du code, comme décrit dans <a href="load-obj.html">cet article</a>. Ce code trouve la taille d'une partie de la scène, puis déplace la caméra et ajuste les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> pour la rendre visible. Vous pouvez ensuite regarder dans le débogueur ou ajouter des messages <code class="notranslate" translate="no">console.log</code> pour afficher la taille et le centre de la scène.</p>
+<h2 id="put-something-in-front-of-the-camera">Mettez quelque chose devant la caméra</h2>
+<p>C'est juste une autre façon de dire que si tout le reste échoue, commencez par quelque chose qui fonctionne, puis ajoutez progressivement des éléments. Si vous obtenez un écran vide, essayez de mettre quelque chose directement devant la caméra. Créez une sphère ou une boîte, donnez-lui un matériau simple comme le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> et assurez-vous que vous pouvez l'afficher à l'écran. Puis commencez à ajouter des éléments petit à petit et à tester. Finalement, vous reproduirez votre bug ou vous le trouverez en chemin.</p>
+<hr>
+<p>Voici quelques conseils pour le débogage de JavaScript. Passons également en revue <a href="debugging-glsl.html">quelques conseils pour le débogage de GLSL</a>.</p>
 
         </div>
       </div>

+ 91 - 0
manual/fr/drawing-lines.html

@@ -0,0 +1,91 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Dessiner des lignes</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Drawing Lines">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Dessiner des lignes</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p>
+            Disons que vous voulez dessiner une ligne ou un cercle, pas un `Mesh` en fil de fer.
+            Nous devons d'abord configurer le renderer, la scène et la caméra (voir la page Créer une scène).
+          </p>
+    
+          <p>Voici le code que nous allons utiliser :</p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const renderer = new THREE.WebGLRenderer();
+renderer.setSize( window.innerWidth, window.innerHeight );
+document.body.appendChild( renderer.domElement );
+
+const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
+camera.position.set( 0, 0, 100 );
+camera.lookAt( 0, 0, 0 );
+
+const scene = new THREE.Scene();
+</pre>
+          <p>Next thing we will do is define a material. For lines we have to use `LineBasicMaterial` or `LineDashedMaterial`.</p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+//créer un LineBasicMaterial bleu
+const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
+</pre>
+    
+          <p>
+            Après le material, nous aurons besoin d'une géométrie avec quelques sommets :
+          </p>
+    
+<pre class="prettyprint notranslate lang-js" translate="no">
+const points = [];
+points.push( new THREE.Vector3( - 10, 0, 0 ) );
+points.push( new THREE.Vector3( 0, 10, 0 ) );
+points.push( new THREE.Vector3( 10, 0, 0 ) );
+
+const geometry = new THREE.BufferGeometry().setFromPoints( points );
+</pre>
+    
+          <p>Notez que les lignes sont tracées entre chaque paire consécutive de sommets, mais pas entre le premier et le dernier (la ligne n'est pas fermée).</p>
+    
+          <p>Maintenant que nous avons des points pour deux lignes et un material, nous pouvons les assembler pour former une ligne.</p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const line = new THREE.Line( geometry, material );
+</pre>
+          <p>Tout ce qui reste est de l'ajouter à la scène et d'appeler `renderer.render()`.</p>
+    
+<pre class="prettyprint notranslate lang-js" translate="no">
+scene.add( line );
+renderer.render( scene, camera );
+</pre>
+    
+          <p>Vous devriez maintenant voir une flèche pointant vers le haut, faite de deux lignes bleues.</p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 93 - 0
manual/fr/faq.html

@@ -0,0 +1,93 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Foire aux questions</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Foire aux questions">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Foire aux questions</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <h2>Quel format de modèle 3D est le mieux supporté ?</h2>
+          <div>
+            <p>
+              Le format recommandé pour l'importation et l'exportation d'assets est glTF (GL Transmission Format). Comme glTF est axé sur la livraison d'assets au moment de l'exécution, il est compact à transmettre et rapide à charger.
+            </p>
+            <p>
+              three.js fournit également des chargeurs pour de nombreux autres formats populaires comme FBX, Collada ou OBJ. Néanmoins, vous devriez toujours essayer d'établir d'abord un workflow basé sur glTF dans vos projets.
+            </p>
+          </div>
+
+          <h2>Pourquoi y a-t-il des balises meta viewport dans les exemples ?</h2>
+          <div>
+            <pre class="prettyprint notranslate lang-js" translate="no">&lt;meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"&gt;</pre>
+
+              <p>Ces balises contrôlent la taille et l'échelle de la fenêtre d'affichage (viewport) pour les navigateurs mobiles (où le contenu de la page peut être rendu à une taille différente de la fenêtre d'affichage visible).</p>
+
+              <p>[link:https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html Safari : Utiliser la fenêtre d'affichage]</p>
+
+              <p>[link:https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag MDN : Utiliser la balise meta viewport]</p>
+          </div>
+
+          <h2>Comment l'échelle de la scène peut-elle être préservée lors du redimensionnement ?</h2>
+          <p>
+            Nous voulons que tous les objets, quelle que soit leur distance par rapport à la caméra, apparaissent de la même taille, même lorsque la fenêtre est redimensionnée.
+
+            L'équation clé pour résoudre cela est cette formule pour la hauteur visible à une distance donnée :
+
+            <pre class="prettyprint notranslate lang-js" translate="no">visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera;</pre>
+            Si nous augmentons la hauteur de la fenêtre d'un certain pourcentage, alors ce que nous voulons, c'est que la hauteur visible à toutes les distances augmente du même pourcentage.
+
+            Cela ne peut pas être fait en changeant la position de la caméra. Au lieu de cela, vous devez changer le champ de vision de la caméra.
+            [link:http://jsfiddle.net/Q4Jpu/ Exemple].
+          </p>
+
+          <h2>Pourquoi une partie de mon objet est-elle invisible ?</h2>
+          <p>
+            Cela pourrait être dû au culling des faces (face culling). Les faces ont une orientation qui décide quel côté est lequel. Et le culling supprime le côté arrière dans des circonstances normales.
+            Pour voir si c'est votre problème, changez le côté du matériau en THREE.DoubleSide.
+            <pre class="prettyprint notranslate lang-js" translate="no">material.side = THREE.DoubleSide</pre>
+          </p>
+
+          <h2>Pourquoi three.js renvoie-t-il parfois des résultats étranges pour des entrées invalides ?</h2>
+          <p>
+            Pour des raisons de performance, three.js ne valide pas les entrées dans la plupart des cas. Il est de la responsabilité de votre application de s'assurer que toutes les entrées sont valides.
+          </p>
+
+          <h2>Puis-je utiliser three.js dans Node.js ?</h2>
+          <p>
+            Parce que three.js est conçu pour le web, il dépend d'APIs de navigateur et du DOM qui n'existent pas toujours dans Node.js. Certains de ces problèmes peuvent être évités en utilisant des shims comme
+            [link:https://github.com/stackgl/headless-gl headless-gl] et [link:https://github.com/rstacruz/jsdom-global jsdom-global], ou en remplaçant des composants comme `TextureLoader`
+            par des alternatives personnalisées. D'autres APIs du DOM peuvent être profondément liées au code qui les utilise, et seront plus difficiles à contourner. Nous accueillons favorablement les pull requests simples et maintenables pour améliorer le support de Node.js, mais recommandons d'ouvrir d'abord un issue pour discuter de vos améliorations.
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 97 - 44
manual/fr/fog.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Le brouillard</title>
+    <title>Brouillard</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,17 +22,25 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Le brouillard</h1>
+        <h1>Brouillard</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js dont le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous devriez commencer par lui. Si, également, vous n'avez pas lu l'article concernant <a href="cameras.html">les caméras</a>, lisez-le avant de poursuivre.</p>
-<p>Le brouillard dans un moteur 3D est généralement un moyen d'atténuer les couleurs de la scène vers une couleur désirée en fonction de la distance par rapport à la caméra. Dans Three.js, vous pouvez ajouter du brouillard en créant un objet <a href="https://threejs.org/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> ou <a href="https://threejs.org/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> et en le définissant sur la propriété <a href="/docs/#api/en/scenes/Scene#fog"><code class="notranslate" translate="no">fog</code></a> de votre scène.</p>
-<p><a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> permet de définir les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> qui sont les distances par rapport à la caméra. Tout ce qui se trouve entre la caméra et <code class="notranslate" translate="no">near</code> n'est pas affecté par le brouillard.
-Ce qui est au-delà de <code class="notranslate" translate="no">far</code> est complètement dans le brouillard. Ce qui se trouve entre les deux, est interpolé entre la couleur du matériau et la couleur du brouillard.</p>
-<p>Il y a aussi <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> qui croît de façon exponentielle en fonction de la distance de la caméra.</p>
-<p>Pour utiliser <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a>, suivez cet exemple :</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le
+premier article est <a href="fundamentals.html">les fondamentaux de three.js</a>. Si
+vous ne l'avez pas encore lu et que vous débutez avec three.js, vous devriez peut-être
+envisager de commencer par là. Si vous n'avez pas lu sur les caméras, vous voudrez peut-être
+commencer par <a href="cameras.html">cet article</a>.</p>
+<p>Le brouillard dans un moteur 3D est généralement une façon de s'estomper vers une couleur spécifique
+en fonction de la distance par rapport à la caméra. Dans three.js, vous ajoutez du brouillard en
+créant un objet <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> ou <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> et en le définissant sur la
+propriété <a href="/docs/#api/en/scenes/Scene#fog"><code class="notranslate" translate="no">fog</code></a> de la scène.</p>
+<p><a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> vous permet de choisir les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> qui sont des distances
+par rapport à la caméra. Tout ce qui est plus proche que <code class="notranslate" translate="no">near</code> n'est pas affecté par le brouillard.
+Tout ce qui est plus loin que <code class="notranslate" translate="no">far</code> prend complètement la couleur du brouillard. Les parties entre
+<code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> s'estompent de leur couleur de matériau à la couleur du brouillard.</p>
+<p>Il y a aussi <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> qui augmente de manière exponentielle avec la distance par rapport à la caméra.</p>
+<p>Pour utiliser l'un ou l'autre type de brouillard, vous en créez un et l'attribuez à la scène comme dans</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 {
   const color = 0xFFFFFF;  // white
@@ -41,7 +49,7 @@ Ce qui est au-delà de <code class="notranslate" translate="no">far</code> est c
   scene.fog = new THREE.Fog(color, near, far);
 }
 </pre>
-<p>Pour utiliser <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a>, suivez cet exemple :</p>
+<p>ou pour <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> ce serait</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 {
   const color = 0xFFFFFF;
@@ -49,7 +57,11 @@ Ce qui est au-delà de <code class="notranslate" translate="no">far</code> est c
   scene.fog = new THREE.FogExp2(color, density);
 }
 </pre>
-<p><a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> est le plus proche de la réalité, mais <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> est le plus souvent utilisé car il permet de choisir un endroit où appliquer le brouillard, afin de vous permettre d'afficher une scène claire jusqu'à une certaine distance, puis de passer à une autre couleur au-delà de cette distance.</p>
+<p><a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> est plus proche de la réalité mais <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> est utilisé
+plus couramment car il vous permet de choisir un endroit où appliquer
+le brouillard afin que vous puissiez décider d'afficher une scène
+claire jusqu'à une certaine distance, puis de s'estomper vers une couleur
+au-delà de cette distance.</p>
 <div class="spread">
   <div>
     <div data-diagram="fog" style="height: 300px;"></div>
@@ -61,21 +73,27 @@ Ce qui est au-delà de <code class="notranslate" translate="no">far</code> est c
   </div>
 </div>
 
-<p>Il est important de noter que le brouillard s'applique aux objets rendus lors du calcul sur chaque pixel de la couleur des objets. Cela signifie que si vous voulez que votre scène s'estompe avec une certaine couleur, vous devez définir le brouillard ainsi que la couleur d'arrière-plan avec la même couleur. La couleur d'arrière-plan est définie à l'aide de la propriété <a href="/docs/#api/en/scenes/Scene#background"><code class="notranslate" translate="no">scene.background</code></a>. Pour choisir une couleur d'arrière-plan, vous lui attachez une <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a>. Comme ceci :</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.background = new THREE.Color('#F00');  // red
+<p>Il est important de noter que le brouillard est appliqué aux <em>choses qui sont rendues</em>.
+Il fait partie du calcul de chaque pixel de la couleur de l'objet.
+Cela signifie que si vous voulez que votre scène s'estompe vers une certaine couleur, vous
+devez définir le brouillard <strong>et</strong> la couleur de fond sur la même couleur.
+La couleur de fond est définie à l'aide de la
+propriété <a href="/docs/#api/en/scenes/Scene#background"><code class="notranslate" translate="no">scene.background</code></a>. Pour choisir une couleur de fond, vous y attachez un <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a>. Par exemple</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.background = new THREE.Color('#F00');  // rouge
 </pre>
 <div class="spread">
   <div>
     <div data-diagram="fogBlueBackgroundRed" style="height: 300px;" class="border"></div>
-    <div class="code">Brouillard bleu, arrière-plan rouge</div>
+    <div class="code">brouillard bleu, fond rouge</div>
   </div>
   <div>
     <div data-diagram="fogBlueBackgroundBlue" style="height: 300px;" class="border"></div>
-    <div class="code">Brouillard bleu, arrière-plan bleu</div>
+    <div class="code">brouillard bleu, fond bleu</div>
   </div>
 </div>
 
-<p>Voici l'un de nos exemples précédents mais avec du brouillard activé. L'unique ajout se fait juste après avoir configuré la scène : nous ajoutons le brouillard et définissons la couleur d'arrière-plan de la scène.</p>
+<p>Voici l'un de nos exemples précédents avec l'ajout de brouillard. La seule addition
+est juste après la mise en place de la scène, nous ajoutons le brouillard et définissons la couleur de fond de la scène</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 
 +{
@@ -86,18 +104,27 @@ Ce qui est au-delà de <code class="notranslate" translate="no">far</code> est c
 +  scene.background = new THREE.Color(color);
 +}
 </pre>
-<p>Dans l'exemple ci-dessous, le <code class="notranslate" translate="no">near</code> de la caméra est à 0,1 et le <code class="notranslate" translate="no">far</code> à 5. La position z de la caméra est à 2. Les cubes mesurent 1 unité de large et à Z = 0. Les réglages du brouillard, <code class="notranslate" translate="no">near</code> = 1 et <code class="notranslate" translate="no">far</code> = 2. Ainsi, les cubes s'estompent juste autour de leur centre.</p>
+<p>Dans l'exemple ci-dessous, <code class="notranslate" translate="no">near</code> de la caméra est de 0.1 et <code class="notranslate" translate="no">far</code> est de 5.
+La caméra est à <code class="notranslate" translate="no">z = 2</code>. Les cubes mesurent 1 unité de large et sont à Z = 0.
+Cela signifie qu'avec un réglage de brouillard de <code class="notranslate" translate="no">near = 1</code> et <code class="notranslate" translate="no">far = 2</code>, les cubes
+s'estomperont juste autour de leur centre.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fog.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée.</a>
+  <a class="threejs_center" href="/manual/examples/fog.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Mettons à jour notre <a href="https://github.com/georgealways/lil-gui">lil-gui</a> pour jouer avec le brouillard. Lil-gui prend un objet et une propriété et crée automatiquement une interface de contrôle pour cette propriété. Nous pourrions simplement le laisser modifier les propriétés <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> du brouillard, mais il est impossible que <code class="notranslate" translate="no">near</code> soit supérieur à <code class="notranslate" translate="no">far</code>. Assurons-nous de cela.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// On utilise cette classe pour passer à lil-gui.
-// Quand lil-gui modifie near ou far :
-//  - near n'est jamais strictement supérieur à far
-//  - far n'est jamais strictement inférieur à near
+<p>Ajoutons une interface pour pouvoir ajuster le brouillard. Encore une fois, nous allons utiliser
+<a href="https://github.com/georgealways/lil-gui">lil-gui</a>. lil-gui prend
+un objet et une propriété et crée automatiquement une interface
+pour ce type de propriété. Nous pourrions simplement le laisser manipuler
+les propriétés <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> du brouillard, mais il est invalide d'avoir
+<code class="notranslate" translate="no">near</code> supérieur à <code class="notranslate" translate="no">far</code>, alors créons un assistant pour que lil-gui
+puisse manipuler une propriété <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>, mais nous nous assurerons que <code class="notranslate" translate="no">near</code>
+est inférieur ou égal à <code class="notranslate" translate="no">far</code> et que <code class="notranslate" translate="no">far</code> est supérieur ou égal à <code class="notulate" translate="no">near</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Nous utilisons cette classe pour la passer à lil-gui
+// ainsi quand elle manipule near ou far
+// near n'est jamais &gt; far et far n'est jamais &lt; near
 class FogGUIHelper {
   constructor(fog) {
     this.fog = fog;
@@ -118,7 +145,7 @@ class FogGUIHelper {
   }
 }
 </pre>
-<p>On peut l'ajouter comme ceci :</p>
+<p>Nous pouvons ensuite l'ajouter comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const near = 1;
   const far = 2;
@@ -131,20 +158,27 @@ class FogGUIHelper {
 +  gui.add(fogGUIHelper, 'far', near, far).listen();
 }
 </pre>
-<p>Les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> définissent les valeurs minimales et maximales pour ajuster le brouillard. Ils sont définis lors de la configuration de la caméra.</p>
-<p>Le <code class="notranslate" translate="no">.listen()</code> à la fin des 2 lignes, dit à lil-gui <em>d'écouter</em>
-les changements. Ainsi, que nous changions <code class="notranslate" translate="no">near</code> ou <code class="notranslate" translate="no">far</code>, lil-gui mettra automatiquement à jour les deux propriétés pour nous.</p>
-<p>Il peut également être agréable de pouvoir changer la couleur du brouillard, mais comme mentionné ci-dessus, nous devons synchroniser la couleur du brouillard et la couleur de l'arrière-plan. Ajoutons donc une autre propriété <em>virtuelle</em> à notre helper qui définira les deux couleurs lorsque lil-gui la manipule.</p>
-<p>lil-gui peut manipuler les couleurs de 4 façons différentes : - Sous la forme d'une chaîne hexadécimale à 6 chiffres (ex : <code class="notranslate" translate="no">#112233</code>); - Sous la forme HSL (ex : <code class="notranslate" translate="no">{h: 60, s: 1, v: }</code>); -
-En tant que tableau RGB (ex : <code class="notranslate" translate="no">[255, 128, 64]</code>); - Ou finalement, comme un tableau RGBA (ex : <code class="notranslate" translate="no">[127, 200, 75, 0.3]</code>).</p>
-<p>Il est plus simple d'utiliser la première solution, la version chaîne hexadécimale, ainsi
+<p>Les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> définissent les valeurs minimum et maximum
+pour l'ajustement du brouillard. Ils sont définis lorsque nous configurons la caméra.</p>
+<p>Le <code class="notranslate" translate="no">.listen()</code> à la fin des 2 dernières lignes indique à lil-gui d'<em>écouter</em>
+les changements. De cette façon, lorsque nous changeons <code class="notranslate" translate="no">near</code> à cause d'une modification de <code class="notranslate" translate="no">far</code>
+ou que nous changeons <code class="notranslate" translate="no">far</code> en réponse à une modification de <code class="notranslate" translate="no">near</code>, lil-gui mettra à jour
+l'interface utilisateur de l'autre propriété pour nous.</p>
+<p>Il pourrait également être agréable de pouvoir changer la couleur du brouillard, mais comme mentionné
+ci-dessus, nous devons synchroniser la couleur du brouillard et la couleur de fond.
+Ajoutons donc une autre propriété <em>virtuelle</em> à notre assistant qui définira les deux couleurs
+lorsque lil-gui la manipulera.</p>
+<p>lil-gui peut manipuler les couleurs de 4 manières : comme une chaîne hexadécimale CSS à 6 chiffres (par exemple : <code class="notranslate" translate="no">#112233</code>). Comme un objet teinte, saturation, valeur (par exemple : <code class="notranslate" translate="no">{h: 60, s: 1, v: }</code>).
+Comme un tableau RGB (par exemple : <code class="notranslate" translate="no">[255, 128, 64]</code>). Ou, comme un tableau RGBA (par exemple : <code class="notranslate" translate="no">[127, 200, 75, 0.3]</code>).</p>
+<p>Le plus simple pour notre objectif est d'utiliser la version chaîne hexadécimale, car de cette façon,
 lil-gui ne manipule qu'une seule valeur. Heureusement, <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a>
-a une méthode pour cela : <a href="/docs/#api/en/math/Color#getHexString"><code class="notranslate" translate="no">getHexString</code></a> qui permet d'obtenir une telle chaîne, il suffit juste d'ajouter un '#' au début.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">/// On utilise cette classe pour passer à lil-gui
-// Quand il manipule near ou far
+dispose d'une méthode <a href="/docs/#api/en/math/Color#getHexString"><code class="notranslate" translate="no">getHexString</code></a> que nous pouvons utiliser pour obtenir facilement une telle chaîne,
+il suffit de faire précéder d'un '#' au début.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Nous utilisons cette classe pour la passer à lil-gui
+// ainsi quand elle manipule near ou far
 // near n'est jamais &gt; far et far n'est jamais &lt; near
-+// Aussi, lorsque lil-gui manipule la couleur, nous allons
-+// mettre à jour les couleurs du brouillard et de l'arrière-plan.
+// Aussi, lorsque lil-gui manipule la couleur, nous allons
+// mettre à jour les couleurs du brouillard et de l'arrière-plan.
 class FogGUIHelper {
 *  constructor(fog, backgroundColor) {
     this.fog = fog;
@@ -173,7 +207,7 @@ class FogGUIHelper {
 +  }
 }
 </pre>
-<p>Ensuite, nous appelons <code class="notranslate" translate="no">gui.addColor</code> pour ajouter une couleur à notre propriété virtuelle :</p>
+<p>Nous appelons ensuite <code class="notranslate" translate="no">gui.addColor</code> pour ajouter une interface utilisateur de couleur pour la propriété virtuelle de notre assistant.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const near = 1;
   const far = 2;
@@ -189,24 +223,43 @@ class FogGUIHelper {
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog-gui.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fog-gui.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée.</a>
+  <a class="threejs_center" href="/manual/examples/fog-gui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Vous pouvez voir qu'un réglage <code class="notranslate" translate="no">near</code> à 1.9 et <code class="notranslate" translate="no">far</code> à 2.0 donne une transition très nette entre non embué et complètement dans le brouillard. <code class="notranslate" translate="no">near</code> = 1.1 et <code class="notranslate" translate="no">far</code> = 2.9 devrait être la meilleure configuration étant donné que nos cubes tournent à 2 unités de la caméra.</p>
-<p>Une dernière chose ! Il existe une propriété <a href="/docs/#api/en/materials/Material#fog">fog</a> pour savoir si les objets rendus avec ce matériau sont affectés ou non par le brouillard. La valeur par défaut est <code class="notranslate" translate="no">true</code> pour la plupart des matériaux. Voici deux exemples illustrant cette volonté de désactiver le brouillard : imaginez que vous créez un simulateur de véhicule 3D avec une vue depuis le siège du conducteur (cockpit). Vous ne voulez pas qu'il y ait de brouillard à l'intérieur du véhicule. Prenons un second exemple : une maison avec un épais brouillard à l'extérieur. Disons que pour commencer, le brouillard est réglé pour commencer à 2 mètres (near = 2) et être total à 4 mètres (far = 4). Les pièces et la maison faisant plus de 4 mètres, il vous faudra donc définir les matériaux utilisés à l'intérieur de la maison pour qu'il n'y ait pas de brouillard, sinon, cela donnerait l'aspect non désiré suivant :</p>
+<p>Vous pouvez voir que régler <code class="notranslate" translate="no">near</code> à environ 1.9 et <code class="notranslate" translate="no">far</code> à 2.0 donne
+une transition très nette entre non-brouillardé et complètement brouillardé.
+tandis que <code class="notranslate" translate="no">near</code> = 1.1 et <code class="notranslate" translate="no">far</code> = 2.9 devraient être à peu près
+les plus lisses étant donné que nos cubes tournent à 2 unités de distance de la caméra.</p>
+<p>Une dernière chose, il existe une propriété booléenne <a href="/docs/#api/en/materials/Material#fog"><code class="notranslate" translate="no">fog</code></a>
+sur un matériau indiquant si les objets rendus
+avec ce matériau sont affectés par le brouillard. Elle est par défaut à <code class="notranslate" translate="no">true</code>
+pour la plupart des matériaux. À titre d'exemple de pourquoi vous pourriez vouloir
+désactiver le brouillard, imaginez que vous créez un simulateur de véhicule 3D
+avec une vue depuis le siège du conducteur ou le cockpit.
+Vous voudrez probablement désactiver le brouillard pour tout ce qui se trouve à l'intérieur du véhicule lorsque
+vous regardez depuis l'intérieur du véhicule.</p>
+<p>Un meilleur exemple pourrait être une maison
+et un brouillard épais à l'extérieur. Disons que le brouillard est configuré pour commencer
+à 2 mètres de distance (<code class="notranslate" translate="no">near</code> = 2) et complètement brouillardé à 4 mètres (<code class="notranslate" translate="no">far</code> = 4).
+Les pièces sont plus longues que 2 mètres et la maison est probablement plus longue
+que 4 mètres, vous devez donc régler les matériaux de l'intérieur
+de la maison pour ne pas appliquer de brouillard, sinon, en vous tenant à l'intérieur
+de la maison et en regardant dehors le mur au fond de la pièce, cela aura l'air
+d'être dans le brouillard.</p>
 <div class="spread">
   <div>
     <div data-diagram="fogHouseAll" style="height: 300px;" class="border"></div>
-    <div class="code">fog à true sur tous les objets.</div>
+    <div class="code">brouillard : vrai, tout</div>
   </div>
 </div>
 
-<p>Remarquez que les murs et le plafond au fond de la pièce sont dans le brouillard. En désactivant le brouillard sur les matériaux de la maison, on résout ce problème.</p>
+<p>Remarquez que les murs et le plafond au fond de la pièce sont affectés par le brouillard.
+En désactivant le brouillard sur les matériaux de la maison, nous pouvons corriger ce problème.</p>
 <div class="spread">
   <div>
     <div data-diagram="fogHouseInsideNoFog" style="height: 300px;" class="border"></div>
-    <div class="code">fog à true uniquement sur les matériaux extérieurs de la maison.</div>
+    <div class="code">brouillard : vrai, seulement les matériaux extérieurs</div>
   </div>
 </div>
 
@@ -223,4 +276,4 @@ class FogGUIHelper {
 
 
 
-</body></html>
+</body></html>

+ 272 - 259
manual/fr/fundamentals.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>principes de base</title>
+    <title>Principes fondamentaux</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – principes de base">
+    <meta name="twitter:title" content="Three.js – Principes fondamentaux">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,112 +22,111 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>principes de base</h1>
+        <h1>Principes fondamentaux</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Ceci est le premier article d'une série consacrée à Three.js.
-<a href="https://threejs.org">Three.js</a> est une bibliothèque 3D qui a pour objectif
-de rendre aussi facile que possible l'inclusion de contenu 3D dans une page web.</p>
-<p>Three.js est souvent confondu avec WebGL puisque la plupart du temps, mais
-pas toujours, elle exploite WebGL pour dessiner en 3D.
-<a href="https://webglfundamentals.org">WebGL est un système très bas niveau qui ne dessine que des points, des lignes et des triangles</a>.
-Faire quelque chose d'exploitable avec WebGL requiert une certaine quantité de code
-et c'est là que Three.js intervient. Elle prend en charge des choses
-telles que les scènes, lumières, ombres, matériaux, textures, mathématiques 3D, en bref,
-tout ce que vous avez à écrire par vous même si vous aviez à utiliser WebGL directement.</p>
-<p>Ces tutoriels supposent que JavaScript vous est connu et, pour grande partie,
-se conforment au style ES6. <a href="prerequisites.html">Consultez ici une brève liste des choses que vous êtes
-déjà censés connaître</a>.</p>
-<p>La plupart des navigateurs qui supportent three.js se mettent à jour automatiquement
-donc la plupart des utilisateurs devraient être capables d'exécuter ce code.
-Si vous souhaitez exécuter ce code sur un très vieux navigateur, nous vous recommandons
-un transpileur tel que <a href="https://babeljs.io">Babel</a>.
-Bien sûr, les utilisateurs exécutant de très vieux navigateurs ont probablement
-des machines incapables de faire tourner Three.js.</p>
-<p>Lors de l'apprentissage de la plupart des langages de programmation,
-la première tâche que les gens font est de faire afficher à l'ordinateur
-<code class="notranslate" translate="no">"Hello World!"</code>. Pour la programmation 3D, l'équivalent est de faire afficher
-un cube en 3D. Donc, nous commencerons par "Hello Cube!".</p>
-<p>Avant de débuter, nous allons tenter de vous donner un idée de la structure
-d'une application Three.js. Elle requiert de créer un ensemble d'objets
-et de les connecter. Voici un diagramme qui représente une application
-Three.js de petite taille:</p>
+          <p>Ceci est le premier article d'une série d'articles sur three.js.
+<a href="https://threejs.org">Three.js</a> est une bibliothèque 3D qui essaie de rendre
+aussi facile que possible l'affichage de contenu 3D sur une page web.</p>
+<p>Three.js est souvent confondu avec WebGL car la plupart du temps,
+mais pas toujours, three.js utilise WebGL pour dessiner en 3D.
+<a href="https://webglfundamentals.org">WebGL est un système de très bas niveau qui ne dessine que des points, des lignes et des triangles</a>.
+Pour faire quoi que ce soit d'utile avec WebGL, cela nécessite généralement beaucoup de
+code et c'est là que three.js intervient. Il gère des choses
+comme les scènes, les lumières, les ombres, les matériaux, les textures, les mathématiques 3D,
+toutes choses que vous auriez à écrire vous-même si vous utilisiez WebGL directement.</p>
+<p>Ces tutoriels supposent que vous connaissez déjà JavaScript et, pour la
+plupart, ils utiliseront le style ES6. <a href="prerequisites.html">Voir ici pour une
+liste concise des choses que vous êtes censé déjà connaître</a>.
+La plupart des navigateurs qui supportent three.js sont mis à jour automatiquement, donc la plupart des utilisateurs
+devraient pouvoir exécuter ce code. Si vous souhaitez faire fonctionner ce code
+sur de très vieux navigateurs, penchez-vous sur un transpiler comme <a href="https://babeljs.io">Babel</a>.
+Bien sûr, les utilisateurs qui exécutent de très vieux navigateurs ont probablement des machines
+qui ne peuvent pas exécuter three.js.</p>
+<p>Lors de l'apprentissage de la plupart des langages de programmation, la première chose que les gens
+font est de faire afficher <code class="notranslate" translate="no">"Hello World!"</code> par l'ordinateur. Pour la 3D,
+l'une des premières choses les plus courantes à faire est de créer un cube 3D.
+Alors commençons par "Hello Cube !"</p>
+<p>Avant de commencer, essayons de vous donner une idée de la structure
+d'une application three.js. Une application three.js vous demande de créer un tas
+d'objets et de les connecter ensemble. Voici un diagramme qui représente
+une petite application three.js</p>
 <div class="threejs_center"><img src="../resources/images/threejs-structure.svg" style="width: 768px;"></div>
 
-<p>Voici ce qui est à remarquer dans le diagramme ci-dessus :</p>
+<p>Points à noter concernant le diagramme ci-dessus.</p>
 <ul>
-<li><p>Il y a un <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a>. C'est sans doute l'objet principal de Three.js. Vous passez
-une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> et une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> à un <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a> et il effectue le rendu (dessine) de la
-partie de la scène 3D qui est à l'intérieur de l'espace visible (en réalité une pyramide tronquée ou <em>frustum</em>)
-de la caméra dans une image 2D affichée dans un canevas (<em>canvas</em>).</p>
+<li><p>Il y a un <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a>. C'est sans doute l'objet principal de three.js. Vous passez une
+<a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> et une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> à un <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a> et il rend (dessine) la partie
+de la scène 3D qui se trouve à l'intérieur du <em>frustum</em> de la caméra en tant qu'image 2D sur un
+canevas.</p>
 </li>
-<li><p>Il y a un <a href="scenegraph.html">graphe de scène</a> qui est une structure arborescente,
-constituée de divers objets tel qu'un objet <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a>, de multiple maillages (<a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>),
-des lumières (<a href="/docs/#api/en/lights/Light"><code class="notranslate" translate="no">Light</code></a>), des groupes (<a href="/docs/#api/en/objects/Group"><code class="notranslate" translate="no">Group</code></a>), des objets 3D <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et des objets <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a>.
-Un objet <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> définit la racine d'un graphe de scène et contient des propriétés telles que
-la couleur d'arrière plan et le brouillard. L'ensemble de ces objets définissent une structure
-hiérarchique de type parent/enfant, arborescente, et indique où les objets apparaissent et
-comment ils sont orientés. Les enfants sont positionnés et orientés par rapport à leur parent.
-Par exemple, les roues d'une voiture sont les enfants du châssis impliquant que si l'on déplace
-ou oriente la voiture, les roues suivront automatiquement son déplacement. Plus de
-détails sont donnés dans <a href="scenegraph.html">l'article sur les graphes de scène</a>.</p>
-<p>Il est à noter sur que ce diagramme <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> est partiellement placé dans le graphe de scène.
-Cela permet d'attirer l'attention qu'en Three.js, contrairement aux autres objets, une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> ne doit
-pas forcément faire partie du graphe de scène pour être opérationnelle. Une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a>, de la même
-façon que les autres objets, enfant d'un autre objet, se déplace et s'oriente par rapport à son
-objet parent. A la fin de <a href="scenegraph.html">l'article sur les graphes de scène</a>, l'inclusion
-de multiples objets <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> dans un unique graphe de scène est donné en exemple.</p>
+<li><p>Il y a un <a href="scenegraph.html">graphe de scène (scenegraph)</a> qui est une structure arborescente,
+composée de divers objets comme un objet <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a>, plusieurs objets
+<a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, des objets <a href="/docs/#api/en/lights/Light"><code class="notranslate" translate="no">Light</code></a>, <a href="/docs/#api/en/objects/Group"><code class="notranslate" translate="no">Group</code></a>, <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>, et des objets <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a>. Un
+objet <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> définit la racine du graphe de scène et contient des propriétés
+comme la couleur de fond et le brouillard. Ces objets définissent une structure arborescente
+hiérarchique parent/enfant et représentent où les objets apparaissent et comment ils sont
+orientés. Les enfants sont positionnés et orientés par rapport à leur parent. Par
+exemple, les roues d'une voiture pourraient être les enfants de la voiture de sorte que déplacer et
+orienter l'objet voiture déplace automatiquement les roues. Vous pouvez en savoir plus
+à ce sujet dans <a href="scenegraph.html">l'article sur les graphes de scène</a>.</p>
+<p>Notez dans le diagramme que la <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> est à moitié dedans et à moitié dehors du graphe de scène. Cela représente
+qu'en three.js, contrairement aux autres objets, une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> n'a pas besoin
+d'être dans le graphe de scène pour fonctionner. Tout comme les autres objets, une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a>, en tant
+qu'enfant d'un autre objet, se déplacera et s'orientera par rapport à son objet parent.
+Il y a un exemple de mise en place de plusieurs objets <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> dans un graphe de scène à
+la fin de <a href="scenegraph.html">l'article sur les graphes de scène</a>.</p>
 </li>
-<li><p>Les objets de type <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> représentent une géométrie (<code class="notranslate" translate="no">Geometry</code>) liée à un matériau (<a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>)
-spécifique. Les objets <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> et <code class="notranslate" translate="no">Geometry</code> peuvent être liés à plusieurs objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>
-simultanément. Par exemple, pour dessiner deux cubes bleus à des positions différentes, nous
-pouvons soit utiliser deux objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> pour spécifier les positions et orientations de
-chaque cube; soit nous pouvons utiliser seulement une géométrie unique (<code class="notranslate" translate="no">Geometry</code>) pour décrire les
-données spatiales du cube et un matériau unique (<a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>) pour spécifier la couleur bleue.
-Les deux objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> peuvent ainsi référencer les mêmes objets <code class="notranslate" translate="no">Geometry</code> et <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>.</p>
+<li><p>Les objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> représentent le dessin d'une <code class="notranslate" translate="no">Geometry</code> spécifique avec un
+ <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> spécifique.</p>
+ <p>Les objets <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> et les objets <code class="notranslate" translate="no">Geometry</code> peuvent être utilisés par
+ plusieurs objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>. Par exemple, pour dessiner deux cubes bleus à différents
+ endroits, nous aurions besoin de deux objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> pour représenter la position et
+ l'orientation de chaque cube. Nous n'aurions besoin que d'une seule <code class="notranslate" translate="no">Geometry</code> pour stocker les
+ données de sommet d'un cube et nous n'aurions besoin que d'un seul <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> pour spécifier la couleur
+ bleue. Les deux objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> pourraient référencer le même objet <code class="notranslate" translate="no">Geometry</code> et le
+ même objet <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>.</p>
 </li>
-<li><p>Les objets <code class="notranslate" translate="no">Geometry</code> représentent les données associées aux sommets d'une géométrie telle qu'une
-sphère, un cube, un avion, un chien, un chat, un humain, un arbre, un bâtiment, etc...
-Three.js fournit plusieurs types intégrés de <a href="primitives.html">primitives géométriques</a>.
-Vous pouvez aussi <a href="custom-buffergeometry.html">créer vos propres géométries</a> ou
-<a href="load-obj.html">charger des géométries à partir d'un fichier</a>.</p>
+<li><p>Les objets <code class="notranslate" translate="no">Geometry</code> représentent les données de sommet d'une pièce de géométrie
+ comme une sphère, un cube, un plan, un chien, un chat, un humain, un arbre, un bâtiment, etc...
+ Three.js fournit de nombreux types de
+ <a href="primitives.html">primitives de géométrie intégrées</a>. Vous pouvez également
+ <a href="custom-buffergeometry.html">créer une géométrie personnalisée</a> ainsi que
+ <a href="load-obj.html">charger de la géométrie à partir de fichiers</a>.</p>
 </li>
-<li><p>Les objets <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> représentent les
-<a href="materials.html">propriétés de surface utilisées pour dessiner la géométrie</a>
-telles que la couleur à utiliser ou le pouvoir réfléchissant (brillance). Un matériau (<a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>)
-peut aussi se référer à un ou plusieurs objets <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a> dont l'utilité est, par exemple, de plaquer
-une image sur la surface d'une géométrie.</p>
+<li><p>Les objets <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> représentent
+<a href="materials.html">les propriétés de surface utilisées pour dessiner la géométrie</a>
+y compris des choses comme la couleur à utiliser et à quel point elle est brillante. Un <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> peut
+également référencer un ou plusieurs objets <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a> qui peuvent être utilisés, par exemple,
+pour envelopper une image sur la surface d'une géométrie.</p>
 </li>
-<li><p>Les objets <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a> représentent généralement des images soit <a href="textures.html">chargées de fichiers image</a>,
-soit <a href="canvas-textures.html">générées par le biais d'un canevas</a> ou
-<a href="rendertargets.html">résultant du rendu d'une autre scène</a>.</p>
+<li><p>Les objets <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a> représentent généralement des images soit <a href="textures.html">chargées à partir de fichiers image</a>,
+<a href="canvas-textures.html">générées à partir d'un canevas</a>, soit <a href="rendertargets.html">rendues à partir d'une autre scène</a>.</p>
 </li>
-<li><p>Les objets <a href="/docs/#api/en/lights/Light"><code class="notranslate" translate="no">Light</code></a> représentent <a href="lights.html">différentes sortes de lumière</a>.</p>
+<li><p>Les objets <a href="/docs/#api/en/lights/Light"><code class="notranslate" translate="no">Light</code></a> représentent <a href="lights.html">différents types de lumières</a>.</p>
 </li>
 </ul>
-<p>Maintenant que tout cela a été défini, nous allons présenter un exemple de type <em>"Hello Cube"</em> utilisant un
-nombre minimum d'éléments Three.js :</p>
+<p>Étant donné tout cela, nous allons créer la configuration *« Hello Cube »* la plus simple
+qui ressemble à ceci</p>
 <div class="threejs_center"><img src="../resources/images/threejs-1cube-no-light-scene.svg" style="width: 500px;"></div>
 
-<p>Tout d'abord, chargeons Three.js :</p>
+<p>Tout d'abord, chargeons three.js</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="module"&gt;
 import * as THREE from 'three';
 &lt;/script&gt;
 </pre>
-<p>Il est important d'écrire <code class="notranslate" translate="no">type="module"</code> dans la balise script.
-Cela nous autorise l'utilisation du mot-clé <code class="notranslate" translate="no">import</code> pour charger Three.js.
-Il y a d'autres manières de le réaliser, mais depuis la version 106 (r106),
-l'utilisation des modules est recommandée. Ils ont l'avantage de pouvoir
-facilement importer les autres modules dont ils ont besoin. Cela nous
-épargne d'avoir à charger à la main les scripts supplémentaires dont ils dépendent.</p>
-<p>Ensuite, nous avons besoin d'une balise <code class="notranslate" translate="no">&lt;canvas&gt;</code> :</p>
+<p>Il est important de mettre <code class="notranslate" translate="no">type="module"</code> dans la balise script. Cela nous permet
+d'utiliser le mot-clé <code class="notranslate" translate="no">import</code> pour charger three.js. À partir de r147, c'est la
+seule façon de charger properly three.js. Les modules ont l'avantage de pouvoir facilement
+importer d'autres modules dont ils ont besoin. Cela nous évite d'avoir à
+charger manuellement les scripts supplémentaires dont ils dépendent.</p>
+<p>Ensuite, nous avons besoin d'une balise <code class="notranslate" translate="no">&lt;canvas&gt;</code>, donc...</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
   &lt;canvas id="c"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
 </pre>
-<p>Nous allons demander à Three.js de dessiner dans ce canevas donc nous devons le rechercher
-dans le document html :</p>
+<p>Nous allons demander à three.js de dessiner dans ce canevas, nous devons donc le rechercher.</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="module"&gt;
 import * as THREE from 'three';
 
@@ -137,99 +136,102 @@ import * as THREE from 'three';
 +  ...
 &lt;/script&gt;
 </pre>
-<p>Après la recherche du canevas, nous créons un <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a>. Le <em>renderer</em>
-a pour mission de charger les données fournies et d'en effectuer le rendu
-dans le canevas.</p>
-<p>Notez qu'il y a quelques détails ésotériques ici. Si vous ne passez pas un
-canevas à Three.js, il va en créer un pour vous mais vous aurez à l'ajouter
-au document. Où l'ajouter peut dépendre du contexte d'utilisation et vous aurez
-à modifier votre code en conséquence. Passer un canevas à Three.js nous apparaît donc
-plus flexible. Nous pouvons mettre le canevas n'importe où et le code le retrouvera.
-Dans le cas contraire, nous aurons à coder où insérer le canevas, ce qui amènera
-probablement à changer le code si le contexte d'utilisation change.</p>
-<p>Ensuite, nous avons besoin d'une caméra. Nous créons une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>.</p>
+<p>Après avoir trouvé le canevas, nous créons un <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a>. Le renderer
+est la chose responsable de prendre toutes les données que vous fournissez
+et de les rendre sur le canevas.</p>
+<p>Notez qu'il y a quelques détails ésotériques ici. Si vous ne passez pas de canevas
+à three.js, il en créera un pour vous, mais vous devrez ensuite l'ajouter
+à votre document. L'endroit où l'ajouter peut changer en fonction de votre cas d'utilisation
+et vous devrez changer votre code. Je trouve que passer un canevas
+à three.js est un peu plus flexible. Je peux placer le canevas n'importe où
+et le code le trouvera, alors que si j'avais du code pour insérer le canevas
+dans le document, je devrais probablement changer ce code si mon cas d'utilisation changeait.</p>
+<p>Ensuite, nous avons besoin d'une caméra. Nous allons créer une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
-const aspect = 2;  // valeur par défaut du canevas
+const aspect = 2;  // the canvas default
 const near = 0.1;
 const far = 5;
 const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 </pre>
-<p><code class="notranslate" translate="no">fov</code> est le raccourci pour <code class="notranslate" translate="no">field of view</code> ou champ de vision.
-Dans ce cas, 75 degrés d'ouverture verticale. Il est à noter que
-la plupart des angles dans Three.js sont exprimés en radians à l'exception
-de la caméra perspective.</p>
-<p><code class="notranslate" translate="no">aspect</code> est le ratio d'affichage dans le canevas. Cela sera détaillé
-<a href="responsive.html">dans un autre article</a>. Toutefois, par défaut,
-un canevas est de taille 300x150 pixels ce qui lui confère un ratio de 300/150 ou 2.</p>
-<p><code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> délimitent la portion de l'espace devant la caméra dont
-le rendu est effectué. Tout ce qui est avant ou après est découpé (<em>clipped</em>),
-donc non dessiné.</p>
-<p>Ces 4 paramètres définissent une pyramide tronquée ou <em>"frustum"</em>.
-En d'autres termes, il s'agit d'une autre forme 3D à l'instar des sphères,
-cubes et prismes.</p>
+<p><code class="notranslate" translate="no">fov</code> est l'abréviation de <code class="notranslate" translate="no">field of view</code> (champ de vision). Dans ce cas, 75 degrés dans la dimension verticale.
+Notez que la plupart des angles en three.js sont en radians, mais pour une raison quelconque, la caméra perspective prend des degrés.</p>
+<p><code class="notranslate" translate="no">aspect</code> est le rapport d'aspect (display aspect) du canevas. Nous aborderons les détails
+<a href="responsive.html">dans un autre article</a> mais par défaut, un canevas est
+de 300x150 pixels, ce qui donne un rapport d'aspect de 300/150, soit 2.</p>
+<p><code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> représentent l'espace devant la caméra
+qui sera rendu. Tout ce qui se trouve avant cette plage ou après cette plage
+sera écrêté (non dessiné).</p>
+<p>Ces quatre paramètres définissent un *« frustum »*.</p>
+<p>Un *frustum* est le nom d'une forme 3D qui ressemble à une pyramide dont la pointe est tranchée.</p>
+<p>En d'autres termes, considérez le mot "frustum" comme une autre forme 3D comme une sphère,
+un cube, un prisme, un frustum.</p>
 <p><img src="../resources/frustum-3d.svg" width="500" class="threejs_center"></p>
-<p>La hauteur des plans <em>near</em> et <em>far</em> est déterminée
-par le champ de vision. La largeur de ces plans est déterminée par le champ de vision et le ratio.</p>
-<p>Tout ce qui est dans le <em>frustum</em> est dessiné. Ce qui est à l'extérieur ne l'est pas.</p>
-<p>La caméra regarde par défaut suivant l'axe -Z, +Y pointant le haut.
-Nous mettons notre cube à l'origine donc nous devons déplacer la caméra de l'origine légèrement vers son arrière
-pour voir quelque chose.</p>
+<p>La hauteur des plans near et far est déterminée par le champ de vision.
+La largeur des deux plans est déterminée par le champ de vision et l'aspect.</p>
+<p>Tout ce qui se trouve à l'intérieur du frustum défini sera dessiné. Tout ce qui se trouve à l'extérieur
+ne le sera pas.</p>
+<p>La caméra est orientée par défaut vers l'axe -Z avec +Y vers le haut. Nous allons placer notre cube
+à l'origine, nous devons donc reculer légèrement la caméra par rapport à l'origine
+afin de voir quelque chose.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.position.z = 2;
 </pre>
-<p>Voici ce que nous voudrions voir :</p>
+<p>Voici ce que nous visons.</p>
 <p><img src="../resources/scene-down.svg" width="500" class="threejs_center"></p>
-<p>Dans le schéma ci-dessus, nous pouvons voir que notre caméra est placée
-en <code class="notranslate" translate="no">z = 2</code>. Elle regarde le long de la direction -Z.
-Notre <em>frustum</em> démarre à 0.1 unités de la caméra et s'étend jusqu'à 5 unités devant elle.
-Comme le schéma est vu du haut, le champ de vision est affecté par le ratio du canvas.
-Celui-ci est deux fois plus large que haut donc le champ de vision horizontal
-est plus grand que les 75 degrés spécifiés pour le champ de vision vertical.</p>
-<p>Ensuite, nous créons une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a>. Dans Three.js, une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> est la racine
-du graphe de scène. Tout ce que nous voulons que Three.js dessine doit être ajouté
-à la scène. Cela sera davantage détaillé dans <a href="scenegraph.html">un futur article sur le fonctionnement des scènes</a>.</p>
+<p>Dans le diagramme ci-dessus, nous pouvons voir que notre caméra est à <code class="notranslate" translate="no">z = 2</code>. Elle regarde
+vers l'axe -Z. Notre frustum commence à 0.1 unité de l'avant de la caméra
+et va jusqu'à 5 unités devant la caméra. Parce que dans ce diagramme nous regardons vers le bas,
+le champ de vision est affecté par l'aspect. Notre canevas est deux fois plus large
+qu'il n'est haut, donc sur la largeur du canevas, le champ de vision sera beaucoup plus large que
+nos 75 degrés spécifiés, qui correspondent au champ de vision vertical.</p>
+<p>Ensuite, nous créons une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a>. Une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> dans three.js est la racine d'une forme de graphe de scène.
+Tout ce que vous voulez que three.js dessine doit être ajouté à la scène. Nous allons
+couvrir plus de détails sur <a href="scenegraph.html">le fonctionnement des scènes dans un futur article</a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 </pre>
-<p>Ensuite nous créons une géométrie de type <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> qui contient les données pour un parallélépipède.
-Quasiment tout ce que nous souhaitons afficher avec Three.js nécessite une
-géométrie qui définit les sommets qui composent nos objets 3D.</p>
+<p>Ensuite, nous créons une <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> qui contient les données pour une boîte.
+Presque tout ce que nous voulons afficher dans Three.js nécessite une géométrie qui définit
+les sommets qui composent notre objet 3D.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const boxWidth = 1;
 const boxHeight = 1;
 const boxDepth = 1;
 const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
 </pre>
-<p>Puis nous créons un matériau basique et fixons sa couleur, qui peut être spécifiée au format hexadécimal (6 chiffres) du standard CSS.</p>
+<p>Nous créons ensuite un matériau de base et définissons sa couleur. Les couleurs peuvent
+être spécifiées en utilisant les valeurs hexadécimales à 6 chiffres de style CSS standard.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshBasicMaterial({color: 0x44aa88});
 </pre>
-<p>Nous créons ensuite un maillage (<a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>). Dans Three.js, il représente la combinaison
-d'une <code class="notranslate" translate="no">Geometry</code> (forme de l'objet) et d'un matériau (<a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> - aspect
-d'un objet, brillant ou plat, quelle couleur, quelle texture appliquer, etc.)
-ainsi que la position, l'orientation et l'échelle de l'objet dans la scène.</p>
+<p>Nous créons ensuite un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>. Un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> en three.js représente la combinaison
+de trois choses</p>
+<ol>
+<li>Une <code class="notranslate" translate="no">Geometry</code> (la forme de l'objet)</li>
+<li>Un <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a> (comment dessiner l'objet, brillant ou plat, quelle couleur, quelle(s) texture(s) appliquer. Etc.)</li>
+<li>La position, l'orientation et l'échelle de cet objet dans la scène par rapport à son parent. Dans le code ci-dessous, ce parent est la scène.</li>
+</ol>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cube = new THREE.Mesh(geometry, material);
 </pre>
-<p>Finalement, nous ajoutons le maillage à la scène.</p>
+<p>Et enfin, nous ajoutons ce maillage à la scène</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.add(cube);
 </pre>
-<p>Nous pouvons, maintenant, effectuer le rendu de la scène en appelant la fonction <em>render</em> du <em>renderer</em>
-et en lui passant la scène et la caméra.</p>
+<p>Nous pouvons ensuite rendre la scène en appelant la fonction de rendu du renderer
+et en lui passant la scène et la caméra</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">renderer.render(scene, camera);
 </pre>
-<p>Voici un exemple fonctionnel :</p>
+<p>Voici un exemple fonctionnel</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fundamentals.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fundamentals.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/fundamentals.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Il est difficile de déterminer s'il s'agit d'un cube 3D puisque nous
-l'observons suivant l'axe -Z sur lequel le cube est lui même aligné.
-Nous n'en voyons donc qu'une face.</p>
-<p>Animons notre cube en le faisant tourner et cela fera clairement
-apparaître qu'il est dessiné en 3D. Pour l'animation, nous effectuerons son rendu
-dans une boucle de rendu en utilisant
+<p>Il est un peu difficile de voir qu'il s'agit d'un cube 3D puisque nous le visualisons
+directement le long de l'axe -Z et que le cube lui-même est aligné sur les axes,
+donc nous ne voyons qu'une seule face.</p>
+<p>Animons-le en rotation et, espérons-le, cela montrera
+clairement qu'il est dessiné en 3D. Pour l'animer, nous allons le rendre dans une boucle de rendu en utilisant
 <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"><code class="notranslate" translate="no">requestAnimationFrame</code></a>.</p>
-<p>Voici notre boucle :</p>
+<p>Voici notre boucle</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
-  time *= 0.001;  // convertis le temps en secondes
+  time *= 0.001;  // convert time to seconds
 
   cube.rotation.x = time;
   cube.rotation.y = time;
@@ -240,66 +242,63 @@ dans une boucle de rendu en utilisant
 }
 requestAnimationFrame(render);
 </pre>
-<p><code class="notranslate" translate="no">requestAnimationFrame</code> est une requête auprès du navigateur dans le cas où vous
-voulez animer quelque chose. Nous lui passons une fonction à appeler.
-Dans notre cas, c'est la fonction <code class="notranslate" translate="no">render</code>. Le navigateur appellera cette fonction
-et, si nous mettons à jour l'affichage de la page, le navigateur refera le rendu
-de la page. Dans notre cas, nous appelons la fonction <code class="notranslate" translate="no">renderer.render</code> de Three.js
-qui dessinera notre scène.</p>
-<p><code class="notranslate" translate="no">requestAnimationFrame</code> passe à notre fonction le temps depuis lequel la page est chargée.
-Il est mesuré en millisecondes. Il est parfois plus facile de travailler
-avec des secondes. C'est pourquoi, nous l'avons converti.</p>
-<p>A présent, nous appliquons sur le cube des rotations le long des axes X et Y en fonction du temps écoulé.
-Les angles de rotation sont exprimés en <a href="https://en.wikipedia.org/wiki/Radian">radians</a>.
-Sachant que 2 PI radians fait faire un tour complet, notre cube effectuera une rotation complète
-sur chaque axe en, à peu près, 6,28 secondes.</p>
-<p>Nous effectuons alors le rendu de la scène et
-demandons une autre image pour l'animation afin de poursuivre notre boucle.</p>
-<p>A l'extérieur de la boucle, nous appelons <code class="notranslate" translate="no">requestAnimationFrame</code> une première fois pour activer la boucle.</p>
+<p><code class="notranslate" translate="no">requestAnimationFrame</code> est une requête au navigateur indiquant que vous souhaitez animer quelque chose.
+Vous lui passez une fonction à appeler. Dans notre cas, cette fonction est <code class="notranslate" translate="no">render</code>. Le navigateur
+appellera votre fonction et si vous mettez à jour quoi que ce soit lié à l'affichage de la
+page, le navigateur re-rendrera la page. Dans notre cas, nous appelons la fonction
+<code class="notranslate" translate="no">renderer.render</code> de three, qui dessinera notre scène.</p>
+<p><code class="notranslate" translate="no">requestAnimationFrame</code> passe le temps écoulé depuis le chargement de la page
+à notre fonction. Ce temps est exprimé en millisecondes. Je trouve beaucoup
+plus facile de travailler avec des secondes, donc ici nous convertissons cela en secondes.</p>
+<p>Nous définissons ensuite les rotations X et Y du cube à l'heure actuelle. Ces rotations
+sont en <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. Il y a 2 pi radians
+dans un cercle, donc notre cube devrait faire un tour sur chaque axe en environ 6,28
+secondes.</p>
+<p>Nous rendons ensuite la scène et demandons une autre frame d'animation pour continuer
+notre boucle.</p>
+<p>En dehors de la boucle, nous appelons <code class="notranslate" translate="no">requestAnimationFrame</code> une seule fois pour démarrer la boucle.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fundamentals-with-animation.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fundamentals-with-animation.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/fundamentals-with-animation.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>C'est un peu mieux mais il est toujours difficile de percevoir la 3D.
-Ce qui aiderait serait d'ajouter de la lumière.
-Three.js propose plusieurs type de lumière que nous détaillerons dans
-<a href="lights.html">un futur article</a>. Pour le moment, créons une lumière directionnelle.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
-  const color = 0xFFFFFF;
-  const intensity = 3;
-  const light = new THREE.DirectionalLight(color, intensity);
-  light.position.set(-1, 2, 4);
-  scene.add(light);
-}
+<p>C'est un peu mieux, mais il est toujours difficile de voir le 3D. Ce qui aiderait,
+c'est d'ajouter un peu d'éclairage, alors ajoutons une lumière. Il existe de nombreux types de lumières dans
+three.js que nous aborderons dans <a href="lights.html">un futur article</a>. Pour l'instant, créons une lumière directionnelle.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
+const intensity = 3;
+const light = new THREE.DirectionalLight(color, intensity);
+light.position.set(-1, 2, 4);
+scene.add(light);
 </pre>
-<p>Les lumières directionnelles ont une position et une cible. Les deux sont par défaut en 0, 0, 0.
-Dans notre cas, nous positionnons la lumière en -1, 2, 4, de manière à ce qu'elle soit légèrement
-sur la gauche, au dessus et derrière notre caméra. La cible est toujours 0, 0, 0 donc elle va
-éclairer vers l'origine.</p>
-<p>Nous avons également besoin de changer le matériau car <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> ne s'applique pas aux
-lumières. Nous le remplaçons par un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> qui est affecté par les sources lumineuses.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({color: 0x44aa88});  // cyan
-+const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // cyan
+<p>Les lumières directionnelles ont une position et une cible. Les deux sont par défaut à 0, 0, 0. Dans notre
+cas, nous définissons la position de la lumière à -1, 2, 4, de sorte qu'elle est légèrement sur la gauche,
+au-dessus et derrière notre caméra. La cible est toujours 0, 0, 0, elle brillera donc
+vers l'origine.</p>
+<p>Nous devons également changer le matériau. Le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> n'est pas affecté par
+les lumières. Changeons-le pour un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> qui est affecté par les lumières.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({color: 0x44aa88});  // greenish blue
++const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // greenish blue
 </pre>
-<p>Voici à présent la nouvelle structure de notre programme :</p>
+<p>Voici la structure de notre nouveau programme</p>
 <div class="threejs_center"><img src="../resources/images/threejs-1cube-with-directionallight.svg" style="width: 500px;"></div>
 
-<p>Et voici son fonctionnement :</p>
+<p>Et le voici en fonctionnement.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fundamentals-with-light.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fundamentals-with-light.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/fundamentals-with-light.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Cela devrait à présent apparaître très clairement en 3D.</p>
-<p>Pour le sport, ajoutons 2 cubes supplémentaires.</p>
-<p>Nous partageons la même géométrie pour chaque cube mais un matériau différent par cube
-afin qu'ils aient une couleur différente.</p>
-<p>Tout d'abord, nous définissons une fonction qui crée un nouveau matériau
-avec la couleur spécifiée. Ensuite, elle créé un maillage à partir de la
-géométrie spécifiée, l'ajoute à la scène et change sa position en X.</p>
+<p>Maintenant, il devrait être assez clairement en 3D.</p>
+<p>Juste pour le plaisir, ajoutons 2 cubes de plus.</p>
+<p>Nous utiliserons la même géométrie pour chaque cube mais créerons un matériau différent
+afin que chaque cube puisse avoir une couleur différente.</p>
+<p>Tout d'abord, nous allons créer une fonction qui crée un nouveau matériau
+avec la couleur spécifiée. Ensuite, elle crée un maillage en utilisant
+la géométrie spécifiée et l'ajoute à la scène et
+définit sa position en X.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
   const material = new THREE.MeshPhongMaterial({color});
 
@@ -311,18 +310,18 @@ géométrie spécifiée, l'ajoute à la scène et change sa position en X.</p>
   return cube;
 }
 </pre>
-<p>Ensuite, nous l'appellons à 3 reprises avec 3 différentes couleurs
-et positions en X, puis nous conservons ces instances de <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> dans un tableau.</p>
+<p>Ensuite, nous l'appellerons 3 fois avec 3 couleurs et positions X différentes
+en stockant les instances <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> dans un tableau.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
   makeInstance(geometry, 0x44aa88,  0),
   makeInstance(geometry, 0x8844aa, -2),
   makeInstance(geometry, 0xaa8844,  2),
 ];
 </pre>
-<p>Enfin, nous faisons tourner ces 3 cubes dans notre fonction de rendu.
-Nous calculons une rotation légèrement différente pour chacun d'eux.</p>
+<p>Enfin, nous allons faire tourner les 3 cubes dans notre fonction de rendu. Nous
+calculons une rotation légèrement différente pour chacun.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
-  time *= 0.001;  // conversion du temps en secondes
+  time *= 0.001;  // convert time to seconds
 
   cubes.forEach((cube, ndx) =&gt; {
     const speed = 1 + ndx * .1;
@@ -333,34 +332,34 @@ Nous calculons une rotation légèrement différente pour chacun d'eux.</p>
 
   ...
 </pre>
-<p>et voi le résultat.</p>
+<p>et voici le résultat.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fundamentals-3-cubes.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/fundamentals-3-cubes.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/fundamentals-3-cubes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Si nous le comparons au schéma précédent, nous constatons qu'il
-est conforme à nos attentes. Les cubes en X = -2 et X = +2
-sont partiellement en dehors du <em>frustum</em>. Ils sont, de plus,
-exagérément déformés puisque le champ de vision dépeint au travers du
-canevas est extrême.</p>
-<p>A présent, notre programme est schématisé par la figure suivante :</p>
+<p>Si vous le comparez au diagramme vu de dessus ci-dessus, vous pouvez voir
+qu'il correspond à nos attentes.</p>
+<p>Avec les cubes à X = -2 et X = +2, ils sont partiellement en dehors de notre frustum.</p>
+<p>Ils sont également quelque peu exagérément déformés car le champ de vision
+à travers le canevas est si extrême.</p>
+<p>Notre programme a maintenant cette structure</p>
 <div class="threejs_center"><img src="../resources/images/threejs-3cubes-scene.svg" style="width: 610px;"></div>
 
-<p>Comme nous pouvons le constater, nous avons 3 objets de type <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, chacun référençant
-la même <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>. Chaque <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> référence également un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> unique
-de sorte que chaque cube possède une couleur différente.</p>
-<p>Nous espérons que cette courte introduction vous aide à débuter.
-<a href="responsive.html">La prochaine étape consiste à  rendre notre code réactif et donc adaptable à de multiples situations</a>.</p>
-<div class="threejs_bottombar">
-<h3>es6 modules, Three.js et structure de dossiers</h3>
-<p>Depuis la version 106 (r106), l'utilisation de Three.js privilégie les <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">modules es6</a>.</p>
+<p>Comme vous pouvez le voir, nous avons 3 objets <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, chacun référençant la même <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>.
+Chaque <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> référence un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> unique afin que chaque cube puisse avoir
+une couleur différente.</p>
+<p>J'espère que cette courte introduction vous aidera à démarrer. <a href="responsive.html">Ensuite, nous verrons
+comment rendre notre code réactif afin qu'il soit adaptable à plusieurs situations</a>.</p>
+<div id="es6" class="threejs_bottombar">
+<h3>modules es6, three.js et structure de dossiers</h3>
+<p>À partir de la version r147, la manière préférée d'utiliser three.js est via les <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">modules es6</a> et les cartes d'importation (import maps).</p>
 <p>
-Ils sont chargés via le mot-clé <code class="notranslate" translate="no">import</code> dans un script ou en ligne
-par le biais d'une balise <code class="notranslate" translate="no">&lt;script type="module"&gt;</code>. Voici un exemple d'utilisation avec les deux :
+Les modules es6 peuvent être chargés via le mot-clé <code class="notranslate" translate="no">import</code> dans un script
+ou en ligne via une balise <code class="notranslate" translate="no">&lt;script type="module"&gt;</code>. Voici un exemple
 </p>
-<pre class="prettyprint">&lt;script type="module"&gt;
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="module"&gt;
 import * as THREE from 'three';
 
 ...
@@ -368,61 +367,75 @@ import * as THREE from 'three';
 &lt;/script&gt;
 </pre>
 <p>
-Les chemins doivent être absolus ou relatifs. Un chemin relatif commencent toujours par
-<code class="notranslate" translate="no">./</code> ou par <code class="notranslate" translate="no">../</code>,
-ce qui est différent des autres balises telles que <code class="notranslate" translate="no">&lt;img&gt;</code> et <code class="notranslate" translate="no">&lt;a&gt;</code>.
+Notez le spécificateur <code class="notranslate" translate="no">'three'</code> ici. Si vous le laissez tel quel, il produira probablement une erreur. Une *carte d'importation* doit être utilisée pour indiquer au navigateur où trouver three.js
 </p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="importmap"&gt;
+{
+  "imports": {
+    "three": "./path/to/three.module.js"
+  }
+}
+&lt;/script&gt;
+</pre>
 <p>
-Les références à un même script ne seront chargées qu'une seule fois à partir du
-moment où leur chemin absolu est exactement identique. Pour Three.js, cela veut
-dire qu'il est nécessaire de mettre toutes les bibliothèques d'exemples dans une
-structure correcte pour les dossiers :
+Notez que le spécificateur de chemin ne peut commencer qu'avec <code class="notranslate" translate="no">./</code> ou <code class="notranslate" translate="no">../</code>.
 </p>
-<pre class="dos">unDossier
- |
- ├-build
- | |
- | +-three.module.js
- |
- +-examples
-   |
-   +-jsm
-     |
-     +-controls
-     | |
-     | +-OrbitControls.js
-     | +-TrackballControls.js
-     | +-...
-     |
-     +-loaders
-     | |
-     | +-GLTFLoader.js
-     | +-...
-     |
-     ...
+<p>
+Pour importer des extensions (addons) comme <a href="https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js"><code class="notranslate" translate="no">OrbitControls.js</code></a>, utilisez ce qui suit
+</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
 </pre>
 <p>
-La raison nécessitant cette structuration des dossiers est parce que les scripts
-des exemples tels que <a href="https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js"><code class="notranslate" translate="no">OrbitControls.js</code></a>
-ont des chemins relatifs tels que :
+N'oubliez pas d'ajouter les extensions (addons) à la carte d'importation comme ceci
 </p>
-<pre class="prettyprint">import * as THREE from '../../../build/three.module.js';
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="importmap"&gt;
+{
+  "imports": {
+    "three": "./path/to/three.module.js",
+    "three/addons/": "./different/path/to/examples/jsm/"
+  }
+}
+&lt;/script&gt;
 </pre>
 <p>
-Utiliser la même structure permet de s'assurer, lors de l'importation de
-Three.js et d'une autre bibliothèque d'exemple, qu'ils référenceront le même fichier
-three.module.js.
+Vous pouvez également utiliser un CDN
 </p>
-<pre class="prettyprint">import * as THREE from './someFolder/build/three.module.js';
-import {OrbitControls} from './someFolder/addons/controls/OrbitControls.js';
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="importmap"&gt;
+{
+  "imports": {
+    "three": "https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/build/three.module.js",
+    "three/addons/": "https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/examples/jsm/"
+  }
+}
+&lt;/script&gt;
 </pre>
-<p>Cela est valable aussi lors de l'utilisation d'un CDN. Assurez vous que vos chemins versThis includes when using a CDN. Be  <code class="notranslate" translate="no">three.modules.js</code> terminent par
-<code class="notranslate" translate="no">/build/three.modules.js</code>. Par exemple :</p>
-<pre class="prettyprint">import * as THREE from 'https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/<b>build/three.module.js</b>';
-import {OrbitControls} from 'https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/addons/controls/OrbitControls.js';
+<p>
+En conclusion, la manière recommandée d'utiliser three.js est
+</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">
+&lt;script type="importmap"&gt;
+{
+  "imports": {
+    "three": "./path/to/three.module.js",
+    "three/addons/": "./different/path/to/examples/jsm/"
+  }
+}
+&lt;/script&gt;
+
+&lt;script type="module"&gt;
+import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+
+...
+
+&lt;/script&gt;
 </pre>
 </div>
 
+<!-- needed in English only to prevent warning from outdated translations -->
+<p><a href="geometry.html"></a>
+<a href="Geometry"></a></p>
+
         </div>
       </div>
     </div>
@@ -433,4 +446,4 @@ import {OrbitControls} from 'https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/
 
 
 
-</body></html>
+</body></html>

+ 1719 - 5
manual/fr/game.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Making a Game</title>
+    <title>Créer un jeu</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Making a Game">
+    <meta name="twitter:title" content="Three.js – Créer un jeu">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,13 +22,1727 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Making a Game</h1>
+        <h1>Créer un jeu</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/game.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Beaucoup de gens veulent écrire des jeux en utilisant three.js. Cet article
+vous donnera, je l'espère, quelques idées sur la façon de commencer.</p>
+<p>Au moment où j'écris cet article, il s'agit probablement de l'article le plus long de ce site.
+Il est possible que le code ici soit massivement sur-conçu, mais à mesure que j'écrivais chaque nouvelle fonctionnalité, je rencontrais un problème qui nécessitait une solution à laquelle je suis habitué depuis d'autres jeux que j'ai écrits.
+En d'autres termes, chaque nouvelle solution semblait importante, je vais donc essayer de montrer pourquoi.
+Bien sûr, plus votre jeu est petit, moins vous pourriez avoir besoin de certaines des solutions présentées ici, mais il s'agit d'un jeu assez petit et pourtant, avec les complexités des personnages 3D, beaucoup de choses demandent plus d'organisation qu'elles ne le feraient avec des personnages 2D.</p>
+<p>Par exemple, si vous créez PacMan en 2D, lorsque PacMan tourne dans un coin,
+cela se produit instantanément à 90 degrés. Il n'y a pas d'étape intermédiaire.
+Mais dans un jeu 3D, nous avons souvent besoin que le personnage pivote sur plusieurs images.
+Ce simple changement peut ajouter beaucoup de complexité et nécessiter des solutions différentes.</p>
+<p>La majorité du code ici ne sera pas vraiment three.js et
+c'est important à noter, <strong>three.js n'est pas un moteur de jeu</strong>.
+Three.js est une bibliothèque 3D. Elle fournit un <a href="scenegraph.html">graphe de scène</a>
+et des fonctionnalités pour afficher les objets 3D ajoutés à ce graphe de scène,
+mais elle ne fournit pas toutes les autres choses nécessaires pour créer un jeu.
+Pas de collisions, pas de physique, pas de systèmes d'entrée, pas de recherche de chemin, etc., etc...
+Donc, nous devrons fournir ces choses nous-mêmes.</p>
+<p>J'ai fini par écrire pas mal de code pour créer cette simple chose <em>inachevée</em>
+ressemblant à un jeu, et encore une fois, il est certainement possible que j'aie sur-conçu
+et qu'il existe des solutions plus simples, mais j'ai l'impression de ne pas avoir écrit
+assez de code et j'espère pouvoir expliquer ce qui, à mon avis, manque.</p>
+<p>Beaucoup des idées ici sont fortement influencées par <a href="https://unity.com">Unity</a>.
+Si vous n'êtes pas familier avec Unity, cela n'a probablement pas d'importance.
+Je n'en parle que parce que des dizaines de milliers de jeux ont été publiés en utilisant
+ces idées.</p>
+<p>Commençons par les parties three.js. Nous devons charger des modèles pour notre jeu.</p>
+<p>Sur <a href="https://opengameart.org">opengameart.org</a> j'ai trouvé ce <a href="https://opengameart.org/content/lowpoly-animated-knight">modèle de chevalier
+animé</a> par <a href="https://opengameart.org/users/quaternius">quaternius</a></p>
+<div class="threejs_center"><img src="../resources/images/knight.jpg" style="width: 375px;"></div>
 
+<p><a href="https://opengameart.org/users/quaternius">quaternius</a> a également créé <a href="https://opengameart.org/content/lowpoly-animated-farm-animal-pack">ces animaux animés</a>.</p>
+<div class="threejs_center"><img src="../resources/images/animals.jpg" style="width: 606px;"></div>
+
+<p>Ceux-ci semblent être de bons modèles pour commencer, donc la première chose à
+faire est de les charger.</p>
+<p>Nous avons abordé <a href="load-gltf.html">le chargement de fichiers glTF auparavant</a>.
+La différence cette fois est que nous devons charger plusieurs modèles et
+nous ne pouvons pas démarrer le jeu tant que tous les modèles ne sont pas chargés.</p>
+<p>Heureusement, three.js fournit le <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> juste à cette fin.
+Nous créons un <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> et le passons aux autres chargeurs. Le
+<a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> fournit à la fois les propriétés <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a> et
+<a href="/docs/#api/en/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a> auxquelles nous pouvons attacher des callbacks.
+Le callback <a href="/docs/#api/en/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a> sera appelé lorsque
+tous les fichiers auront été chargés. Le callback <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a>
+est appelé après l'arrivée de chaque fichier individuel pour nous donner une chance de montrer
+la progression du chargement.</p>
+<p>En partant du code de <a href="load-gltf.html">chargement d'un fichier glTF</a>, j'ai supprimé tout
+le code lié au cadrage de la scène et ajouté ce code pour charger tous les modèles.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const manager = new THREE.LoadingManager();
+manager.onLoad = init;
+const models = {
+  pig:    { url: 'resources/models/animals/Pig.gltf' },
+  cow:    { url: 'resources/models/animals/Cow.gltf' },
+  llama:  { url: 'resources/models/animals/Llama.gltf' },
+  pug:    { url: 'resources/models/animals/Pug.gltf' },
+  sheep:  { url: 'resources/models/animals/Sheep.gltf' },
+  zebra:  { url: 'resources/models/animals/Zebra.gltf' },
+  horse:  { url: 'resources/models/animals/Horse.gltf' },
+  knight: { url: 'resources/models/knight/KnightCharacter.gltf' },
+};
+{
+  const gltfLoader = new GLTFLoader(manager);
+  for (const model of Object.values(models)) {
+    gltfLoader.load(model.url, (gltf) =&gt; {
+      model.gltf = gltf;
+    });
+  }
+}
+
+function init() {
+  // TBD
+}
+</pre>
+<p>Ce code chargera tous les modèles ci-dessus et le <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> appellera
+<code class="notranslate" translate="no">init</code> une fois terminé. Nous utiliserons l'objet <code class="notranslate" translate="no">models</code> plus tard pour accéder aux
+modèles chargés, de sorte que le callback du <a href="/docs/#examples/loaders/GLTFLoader"><code class="notranslate" translate="no">GLTFLoader</code></a> pour chaque modèle individuel attache
+les données chargées aux informations de ce modèle.</p>
+<p>Tous les modèles avec toutes leurs animations font actuellement environ 6,6 Mo. C'est un
+téléchargement assez important. En supposant que votre serveur prenne en charge la compression (ce qui est le cas du serveur sur lequel ce site fonctionne), il peut les compresser à environ 1,4 Mo. C'est
+nettement mieux que 6,6 Mo, mais ce n'est toujours pas une petite quantité de données. Il serait
+probablement bon d'ajouter une barre de progression pour que l'utilisateur ait une idée du temps qu'il
+lui reste à attendre.</p>
+<p>Alors, ajoutons un callback <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a>. Il sera
+appelé avec 3 arguments : l'<code class="notranslate" translate="no">url</code> du dernier objet chargé, puis le nombre
+d'éléments chargés jusqu'à présent, ainsi que le nombre total d'éléments.</p>
+<p>Mettons en place du code HTML pour une barre de chargement</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="loading"&gt;
++    &lt;div&gt;
++      &lt;div&gt;...chargement...&lt;/div&gt;
++      &lt;div class="progress"&gt;&lt;div id="progressbar"&gt;&lt;/div&gt;&lt;/div&gt;
++    &lt;/div&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>Nous allons rechercher la div <code class="notranslate" translate="no">#progressbar</code> et nous pourrons définir la largeur de 0 % à 100 %
+pour montrer notre progression. Tout ce que nous avons à faire est de définir cela dans notre callback.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const manager = new THREE.LoadingManager();
+manager.onLoad = init;
+
++const progressbarElem = document.querySelector('#progressbar');
++manager.onProgress = (url, itemsLoaded, itemsTotal) =&gt; {
++  progressbarElem.style.width = `${itemsLoaded / itemsTotal * 100 | 0}%`;
++};
+</pre>
+<p>Nous avons déjà configuré <code class="notranslate" translate="no">init</code> pour être appelé lorsque tous les modèles sont chargés, nous pouvons donc
+désactiver la barre de progression en masquant l'élément <code class="notranslate" translate="no">#loading</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
++  // masquer la barre de chargement
++  const loadingElem = document.querySelector('#loading');
++  loadingElem.style.display = 'none';
+}
+</pre>
+<p>Voici un tas de CSS pour styler la barre. Le CSS rend la <code class="notranslate" translate="no">#loading</code> <code class="notranslate" translate="no">&lt;div&gt;</code>
+de la taille totale de la page et centre ses enfants. Le CSS crée une zone <code class="notranslate" translate="no">.progress</code>
+pour contenir la barre de progression. Le CSS donne également à la barre de progression
+une animation CSS de rayures diagonales.</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#loading {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  font-size: xx-large;
+  font-family: sans-serif;
+}
+#loading&gt;div&gt;div {
+  padding: 2px;
+}
+.progress {
+  width: 50vw;
+  border: 1px solid black;
+}
+#progressbar {
+  width: 0;
+  transition: width ease-out .5s;
+  height: 1em;
+  background-color: #888;
+  background-image: linear-gradient(
+    -45deg,
+    rgba(255, 255, 255, .5) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, .5) 50%,
+    rgba(255, 255, 255, .5) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-size: 50px 50px;
+  animation: progressanim 2s linear infinite;
+}
+
+@keyframes progressanim {
+  0% {
+    background-position: 50px 50px;
+  }
+  100% {
+    background-position: 0 0;
+  }
+}
+</pre>
+<p>Maintenant que nous avons une barre de progression, occupons-nous des modèles. Ces modèles
+ont des animations et nous voulons pouvoir y accéder.
+Les animations sont stockées dans un tableau par défaut, mais nous aimerions pouvoir y accéder
+facilement par leur nom. Configurons donc une propriété <code class="notranslate" translate="no">animations</code> pour
+chaque modèle afin de faire cela. Notez bien sûr que cela signifie que les animations doivent avoir des noms uniques.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function prepModelsAndAnimations() {
++  Object.values(models).forEach(model =&gt; {
++    const animsByName = {};
++    model.gltf.animations.forEach((clip) =&gt; {
++      animsByName[clip.name] = clip;
++    });
++    model.animations = animsByName;
++  });
++}
+
+function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
++  prepModelsAndAnimations();
+}
+</pre>
+<p>Affichons les modèles animés.</p>
+<p>Contrairement à l'<a href="load-gltf.html">exemple précédent de chargement d'un fichier glTF</a>,
+cette fois-ci, nous voulons probablement pouvoir afficher plus d'une instance
+de chaque modèle. Pour ce faire, au lieu d'ajouter
+directement la scène glTF chargée, comme nous l'avons fait dans <a href="load-gltf.html">l'article sur le chargement d'un glTF</a>,
+nous voulons plutôt cloner la scène et, en particulier, nous voulons la cloner
+pour les personnages animés avec skinning. Heureusement, il existe une fonction utilitaire,
+<code class="notranslate" translate="no">SkeletonUtils.clone</code>, que nous pouvons utiliser pour cela. Donc, nous devons d'abord inclure
+les utilitaires.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
++import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';
+</pre>
+<p>Ensuite, nous pouvons cloner les modèles que nous venons de charger</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
+  prepModelsAndAnimations();
+
++  Object.values(models).forEach((model, ndx) =&gt; {
++    const clonedScene = SkeletonUtils.clone(model.gltf.scene);
++    const root = new THREE.Object3D();
++    root.add(clonedScene);
++    scene.add(root);
++    root.position.x = (ndx - 3) * 3;
++  });
+}
+</pre>
+<p>Ci-dessus, pour chaque modèle, nous clonons la <code class="notranslate" translate="no">gltf.scene</code> que nous avons chargée et
+nous en faisons l'enfant d'un nouveau <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Nous devons l'attacher à un autre objet,
+car lorsque nous jouons des animations, l'animation appliquera des positions animées aux nœuds
+de la scène chargée, ce qui signifie que nous n'aurons pas le contrôle sur ces positions.</p>
+<p>Pour jouer les animations, chaque modèle que nous clonons a besoin d'un <a href="/docs/#api/en/animation/AnimationMixer"><code class="notranslate" translate="no">AnimationMixer</code></a>.
+Un <a href="/docs/#api/en/animation/AnimationMixer"><code class="notranslate" translate="no">AnimationMixer</code></a> contient 1 ou plusieurs <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a>s. Une
+<a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a> référence un <a href="/docs/#api/en/animation/AnimationClip"><code class="notranslate" translate="no">AnimationClip</code></a>. Les <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a>s
+ont toutes sortes de paramètres pour jouer, puis enchaîner avec une autre
+action ou faire un crossfade entre les actions. Prenons simplement le premier
+<a href="/docs/#api/en/animation/AnimationClip"><code class="notranslate" translate="no">AnimationClip</code></a> et créons une action pour celui-ci. La valeur par défaut est qu'une
+action joue son clip en boucle indéfiniment.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const mixers = [];
+
+function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
+  prepModelsAndAnimations();
+
+  Object.values(models).forEach((model, ndx) =&gt; {
+    const clonedScene = SkeletonUtils.clone(model.gltf.scene);
+    const root = new THREE.Object3D();
+    root.add(clonedScene);
+    scene.add(root);
+    root.position.x = (ndx - 3) * 3;
+
++    const mixer = new THREE.AnimationMixer(clonedScene);
++    const firstClip = Object.values(model.animations)[0];
++    const action = mixer.clipAction(firstClip);
++    action.play();
++    mixers.push(mixer);
+  });
+}
+</pre>
+<p>Nous avons appelé <a href="/docs/#api/en/animation/AnimationAction#play"><code class="notranslate" translate="no">play</code></a> pour démarrer l'action et stocké
+tous les <code class="notranslate" translate="no">AnimationMixers</code> dans un tableau appelé <code class="notranslate" translate="no">mixers</code>. Enfin,
+nous devons mettre à jour chaque <a href="/docs/#api/en/animation/AnimationMixer"><code class="notranslate" translate="no">AnimationMixer</code></a> dans notre boucle de rendu en calculant
+le temps écoulé depuis la dernière image et en le passant à <a href="/docs/#api/en/animation/AnimationMixer.update"><code class="notranslate" translate="no">AnimationMixer.update</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+let then = 0;
+function render(now) {
++  now *= 0.001;  // convertir en secondes
++  const deltaTime = now - then;
++  then = now;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
++  for (const mixer of mixers) {
++    mixer.update(deltaTime);
++  }
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Et avec cela, chaque modèle devrait être chargé et jouer sa première animation.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/game-load-models.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/game-load-models.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Faisons en sorte que nous puissions vérifier toutes les animations.
+Nous ajouterons tous les clips en tant qu'actions, puis nous n'en activerons qu'un
+seul à la fois.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const mixers = [];
++const mixerInfos = [];
+
+function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
+  prepModelsAndAnimations();
+
+  Object.values(models).forEach((model, ndx) =&gt; {
+    const clonedScene = SkeletonUtils.clone(model.gltf.scene);
+    const root = new THREE.Object3D();
+    root.add(clonedScene);
+    scene.add(root);
+    root.position.x = (ndx - 3) * 3;
+
+    const mixer = new THREE.AnimationMixer(clonedScene);
+-    const firstClip = Object.values(model.animations)[0];
+-    const action = mixer.clipAction(firstClip);
+-    action.play();
+-    mixers.push(mixer);
++    const actions = Object.values(model.animations).map((clip) =&gt; {
++      return mixer.clipAction(clip);
++    });
++    const mixerInfo = {
++      mixer,
++      actions,
++      actionNdx: -1,
++    };
++    mixerInfos.push(mixerInfo);
++    playNextAction(mixerInfo);
+  });
+}
+
++function playNextAction(mixerInfo) {
++  const {actions, actionNdx} = mixerInfo;
++  const nextActionNdx = (actionNdx + 1) % actions.length;
++  mixerInfo.actionNdx = nextActionNdx;
++  actions.forEach((action, ndx) =&gt; {
++    const enabled = ndx === nextActionNdx;
++    action.enabled = enabled;
++    if (enabled) {
++      action.play();
++    }
++  });
++}
+</pre>
+<p>Le code ci-dessus crée un tableau de <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a>s,
+une pour chaque <a href="/docs/#api/en/animation/AnimationClip"><code class="notranslate" translate="no">AnimationClip</code></a>. Il crée un tableau d'objets, <code class="notranslate" translate="no">mixerInfos</code>,
+avec des références au <a href="/docs/#api/en/animation/AnimationMixer"><code class="notranslate" translate="no">AnimationMixer</code></a> et à toutes les <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a>s
+pour chaque modèle. Il appelle ensuite <code class="notranslate" translate="no">playNextAction</code> qui définit la propriété <code class="notranslate" translate="no">enabled</code> à
+l'exception d'une seule action pour ce mixeur.</p>
+<p>Nous devons mettre à jour la boucle de rendu pour le nouveau tableau</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-for (const mixer of mixers) {
++for (const {mixer} of mixerInfos) {
+  mixer.update(deltaTime);
+}
+</pre>
+<p>Faisons en sorte qu'en appuyant sur une touche de 1 à 8, l'animation suivante soit jouée
+pour chaque modèle</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">window.addEventListener('keydown', (e) =&gt; {
+  const mixerInfo = mixerInfos[e.keyCode - 49];
+  if (!mixerInfo) {
+    return;
+  }
+  playNextAction(mixerInfo);
+});
+</pre>
+<p>Maintenant, vous devriez pouvoir cliquer sur l'exemple, puis appuyer sur les touches 1 à 8
+pour faire défiler chaque modèle à travers ses animations disponibles.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/game-check-animations.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/game-check-animations.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>On peut donc dire que c'est la somme totale de la partie three.js de cet article.
+Nous avons abordé le chargement de plusieurs fichiers, le clonage de modèles skinnés,
+et la lecture d'animations sur ceux-ci. Dans un vrai jeu, vous auriez beaucoup plus
+de manipulations à faire sur les objets <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a>.</p>
+<p>Commençons à créer une infrastructure de jeu</p>
+<p>Un modèle courant pour créer un jeu moderne est d'utiliser un
+<a href="https://www.google.com/search?q=entity+component+system">Entity Component System</a>.
+Dans un Entity Component System, un objet dans un jeu est appelé une <em>entité</em> qui
+se compose d'un ensemble de <em>composants</em>. Vous construisez des entités en décidant quels composants
+leur attacher. Alors, créons un Entity Component System.</p>
+<p>Nous appellerons nos entités <code class="notranslate" translate="no">GameObject</code>. C'est effectivement juste une collection
+de composants et un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> de three.js.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function removeArrayElement(array, element) {
+  const ndx = array.indexOf(element);
+  if (ndx &gt;= 0) {
+    array.splice(ndx, 1);
+  }
+}
+
+class GameObject {
+  constructor(parent, name) {
+    this.name = name;
+    this.components = [];
+    this.transform = new THREE.Object3D();
+    parent.add(this.transform);
+  }
+  addComponent(ComponentType, ...args) {
+    const component = new ComponentType(this, ...args);
+    this.components.push(component);
+    return component;
+  }
+  removeComponent(component) {
+    removeArrayElement(this.components, component);
+  }
+  getComponent(ComponentType) {
+    return this.components.find(c =&gt; c instanceof ComponentType);
+  }
+  update() {
+    for (const component of this.components) {
+      component.update();
+    }
+  }
+}
+</pre>
+<p>L'appel de <code class="notranslate" translate="no">GameObject.update</code> appelle la fonction <code class="notranslate" translate="no">update</code> sur tous les composants.</p>
+<p>J'ai inclus un nom uniquement pour faciliter le débogage, de sorte que si j'examine un <code class="notranslate" translate="no">GameObject</code>
+dans le débogueur, je puisse voir un nom pour l'aider à l'identifier.</p>
+<p>Quelques choses qui pourraient sembler un peu étranges :</p>
+<p><code class="notranslate" translate="no">GameObject.addComponent</code> est utilisé pour créer des composants. Que ce
+soit une bonne ou une mauvaise idée, je ne suis pas sûr. Ma pensée était qu'il n'a aucun sens
+pour un composant d'exister en dehors d'un gameobject, alors j'ai pensé
+qu'il pourrait être bon que la création d'un composant ajoute automatiquement ce composant
+au gameobject et passe le gameobject au constructeur du composant.
+En d'autres termes, pour ajouter un composant, vous faites ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gameObject = new GameObject(scene, 'foo');
+gameObject.addComponent(TypeOfComponent);
+</pre>
+<p>Si je ne le faisais pas de cette façon, vous feriez plutôt quelque chose comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gameObject = new GameObject(scene, 'foo');
+const component = new TypeOfComponent(gameObject);
+gameObject.addComponent(component);
+</pre>
+<p>Est-ce mieux que la première méthode soit plus courte et plus automatisée, ou est-ce pire
+parce que cela sort de l'ordinaire ? Je ne sais pas.</p>
+<p><code class="notranslate" translate="no">GameObject.getComponent</code> recherche les composants par type. Cela
+implique que vous ne pouvez pas avoir 2 composants du même
+type sur un seul objet de jeu, ou du moins si vous en avez, vous ne pouvez
+rechercher que le premier sans ajouter une autre API.</p>
+<p>Il est courant qu'un composant en recherche un autre, et lors de cette recherche, ils
+doivent correspondre par type, sinon vous pourriez obtenir le mauvais. Nous pourrions à la place
+donner un nom à chaque composant et vous pourriez les rechercher par leur nom. Ce serait
+plus flexible car vous pourriez avoir plus d'un composant du même type, mais ce
+serait aussi plus fastidieux. Encore une fois, je ne suis pas sûr de ce qui est le mieux.</p>
+<p>Passons aux composants eux-mêmes. Voici leur classe de base.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Base pour tous les composants
+class Component {
+  constructor(gameObject) {
+    this.gameObject = gameObject;
+  }
+  update() {
+  }
+}
+</pre>
+<p>Les composants ont-ils besoin d'une classe de base ? JavaScript n'est pas comme la plupart des langages strictement
+typés, donc en pratique, nous pourrions ne pas avoir de classe de base et laisser
+chaque composant faire ce qu'il veut dans son constructeur, sachant que le premier
+argument est toujours le gameobject du composant. S'il ne se soucie pas du gameobject, il
+ne le stockerait pas. J'ai un peu l'impression que cette base commune est bonne cependant.
+Cela signifie que si vous avez une référence à un
+composant, vous savez que vous pouvez toujours trouver son gameobject parent, et à partir de son
+parent, vous pouvez facilement rechercher d'autres composants ainsi que regarder sa
+transformation.</p>
+<p>Pour gérer les gameobjects, nous avons probablement besoin d'une sorte de gestionnaire de gameobjects. Vous
+pourriez penser que nous pourrions simplement garder un tableau de gameobjects, mais dans un vrai jeu, les
+composants d'un gameobject pourraient ajouter et supprimer d'autres gameobjects pendant l'exécution.
+Par exemple, un gameobject d'arme pourrait ajouter un gameobject de balle chaque fois que l'arme
+tire. Un gameobject de monstre pourrait se supprimer s'il a été tué. Nous
+aurions alors un problème : nous pourrions avoir du code comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (const gameObject of globalArrayOfGameObjects) {
+  gameObject.update();
+}
+</pre>
+<p>La boucle ci-dessus échouerait ou ferait des choses inattendues si
+des gameobjects étaient ajoutés ou supprimés de <code class="notranslate" translate="no">globalArrayOfGameObjects</code>
+au milieu de la boucle dans la fonction <code class="notranslate" translate="no">update</code> d'un composant.</p>
+<p>Pour essayer de prévenir ce problème, nous avons besoin de quelque chose d'un peu plus sûr.
+Voici une tentative.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class SafeArray {
+  constructor() {
+    this.array = [];
+    this.addQueue = [];
+    this.removeQueue = new Set();
+  }
+  get isEmpty() {
+    return this.addQueue.length + this.array.length &gt; 0;
+  }
+  add(element) {
+    this.addQueue.push(element);
+  }
+  remove(element) {
+    this.removeQueue.add(element);
+  }
+  forEach(fn) {
+    this._addQueued();
+    this._removeQueued();
+    for (const element of this.array) {
+      if (this.removeQueue.has(element)) {
+        continue;
+      }
+      fn(element);
+    }
+    this._removeQueued();
+  }
+  _addQueued() {
+    if (this.addQueue.length) {
+      this.array.splice(this.array.length, 0, ...this.addQueue);
+      this.addQueue = [];
+    }
+  }
+  _removeQueued() {
+    if (this.removeQueue.size) {
+      this.array = this.array.filter(element =&gt; !this.removeQueue.has(element));
+      this.removeQueue.clear();
+    }
+  }
+}
+</pre>
+<p>La classe ci-dessus vous permet d'ajouter ou de supprimer des éléments du <code class="notranslate" translate="no">SafeArray</code>
+sans altérer le tableau lui-même pendant qu'il est parcouru. Au lieu
+de cela, les nouveaux éléments sont ajoutés à <code class="notranslate" translate="no">addQueue</code> et les éléments supprimés
+à <code class="notranslate" translate="no">removeQueue</code>, puis ajoutés ou supprimés en dehors de la boucle.</p>
+<p>En utilisant cela, voici notre classe pour gérer les gameobjects.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class GameObjectManager {
+  constructor() {
+    this.gameObjects = new SafeArray();
+  }
+  createGameObject(parent, name) {
+    const gameObject = new GameObject(parent, name);
+    this.gameObjects.add(gameObject);
+    return gameObject;
+  }
+  removeGameObject(gameObject) {
+    this.gameObjects.remove(gameObject);
+  }
+  update() {
+    this.gameObjects.forEach(gameObject =&gt; gameObject.update());
+  }
+}
+</pre>
+<p>Avec tout cela, créons maintenant notre premier composant. Ce composant
+gérera simplement un objet three.js skinné comme ceux que nous venons de créer.
+Pour rester simple, il n'aura qu'une seule méthode, <code class="notranslate" translate="no">setAnimation</code>, qui
+prend le nom de l'animation à jouer et la lance.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class SkinInstance extends Component {
+  constructor(gameObject, model) {
+    super(gameObject);
+    this.model = model;
+    this.animRoot = SkeletonUtils.clone(this.model.gltf.scene);
+    this.mixer = new THREE.AnimationMixer(this.animRoot);
+    gameObject.transform.add(this.animRoot);
+    this.actions = {};
+  }
+  setAnimation(animName) {
+    const clip = this.model.animations[animName];
+    // turn off all current actions
+    for (const action of Object.values(this.actions)) {
+      action.enabled = false;
+    }
+    // get or create existing action for clip
+    const action = this.mixer.clipAction(clip);
+    action.enabled = true;
+    action.reset();
+    action.play();
+    this.actions[animName] = action;
+  }
+  update() {
+    this.mixer.update(globals.deltaTime);
+  }
+}
+</pre>
+<p>Vous pouvez voir qu'il s'agit essentiellement du code que nous avions auparavant qui clone la scène que nous avons chargée,
+puis configure un <a href="/docs/#api/en/animation/AnimationMixer"><code class="notranslate" translate="no">AnimationMixer</code></a>. <code class="notranslate" translate="no">setAnimation</code> ajoute une <a href="/docs/#api/en/animation/AnimationAction"><code class="notranslate" translate="no">AnimationAction</code></a> pour un
+<a href="/docs/#api/en/animation/AnimationClip"><code class="notranslate" translate="no">AnimationClip</code></a> particulier s'il n'existe pas déjà, et désactive toutes
+les actions existantes.</p>
+<p>Le code référence <code class="notranslate" translate="no">globals.deltaTime</code>. Créons un objet globals</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const globals = {
+  time: 0,
+  deltaTime: 0,
+};
+</pre>
+<p>Et mettons-le à jour dans la boucle de rendu</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">let then = 0;
+function render(now) {
+  // convertir en secondes
+  globals.time = now * 0.001;
+  // s'assurer que le temps delta n'est pas trop grand.
+  globals.deltaTime = Math.min(globals.time - then, 1 / 20);
+  then = globals.time;
+</pre>
+<p>La vérification ci-dessus pour s'assurer que <code class="notranslate" translate="no">deltaTime</code> ne dépasse pas 1/20ème
+de seconde est due au fait que, sinon, nous obtiendrions une valeur énorme pour <code class="notranslate" translate="no">deltaTime</code>
+si nous masquions l'onglet. Nous pourrions le masquer pendant des secondes ou des minutes, et ensuite,
+lorsque notre onglet serait ramené au premier plan, <code class="notranslate" translate="no">deltaTime</code> serait énorme
+et pourrait téléporter des personnages à travers notre monde de jeu si nous avions du code comme</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">position += velocity * deltaTime;
+</pre>
+<p>En limitant le maximum <code class="notranslate" translate="no">deltaTime</code>, ce problème est évité.</p>
+<p>Créons maintenant un composant pour le joueur.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const kForward = new THREE.Vector3(0, 0, 1);
+const globals = {
+  time: 0,
+  deltaTime: 0,
++  moveSpeed: 16,
+};
+
+class Player extends Component {
+  constructor(gameObject) {
+    super(gameObject);
+    const model = models.knight;
+    this.skinInstance = gameObject.addComponent(SkinInstance, model);
+    this.skinInstance.setAnimation('Run');
++    this.turnSpeed = globals.moveSpeed / 4;
+  }
++  update() {
++    const {deltaTime, moveSpeed} = globals;
++    const {transform} = this.gameObject;
++    const delta = (inputManager.keys.left.down  ?  1 : 0) +
++                  (inputManager.keys.right.down ? -1 : 0);
++    transform.rotation.y += this.turnSpeed * delta * deltaTime;
++    transform.translateOnAxis(kForward, moveSpeed * deltaTime);
++  }
+}
+</pre>
+<p>Le code ci-dessus utilise <a href="/docs/#api/en/core/Object3D.transformOnAxis"><code class="notranslate" translate="no">Object3D.transformOnAxis</code></a> pour faire avancer le joueur.
+<a href="/docs/#api/en/core/Object3D.transformOnAxis"><code class="notranslate" translate="no">Object3D.transformOnAxis</code></a> fonctionne dans l'espace local, il ne fonctionne donc que
+si l'objet en question est à la racine de la scène, pas s'il est un enfant de quelque chose d'autre <a class="footnote" href="#parented" id="parented-backref">1</a></p>
+<p>Nous avons également ajouté une vitesse de déplacement globale (<code class="notranslate" translate="no">moveSpeed</code>) et basé une vitesse de rotation (<code class="notranslate" translate="no">turnSpeed</code>) sur la vitesse de déplacement.
+La vitesse de rotation est basée sur la vitesse de déplacement pour essayer de s'assurer qu'un personnage
+peut tourner assez brusquement pour atteindre sa cible. Si <code class="notranslate" translate="no">turnSpeed</code> est trop faible,
+un personnage tournera en rond autour de sa cible sans jamais l'atteindre.
+Je n'ai pas pris la peine de faire les calculs pour déterminer la vitesse de rotation requise
+pour une vitesse de déplacement donnée. J'ai juste deviné.</p>
+<p>Le code jusqu'à présent fonctionnerait, mais si le joueur sort de l'écran, il n'y a
+aucun moyen de savoir où il se trouve. Faisons en sorte que s'il est hors écran
+pendant plus d'un certain temps, il soit téléporté à l'origine.
+Nous pouvons le faire en utilisant la classe <a href="/docs/#api/en/math/Frustum"><code class="notranslate" translate="no">Frustum</code></a> de three.js pour vérifier si un point
+est à l'intérieur du frustum de vue de la caméra.</p>
+<p>Nous devons construire un frustum à partir de la caméra. Nous pourrions le faire dans le composant Player,
+mais d'autres objets pourraient vouloir l'utiliser également, alors ajoutons un autre
+gameobject avec un composant pour gérer un frustum.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class CameraInfo extends Component {
+  constructor(gameObject) {
+    super(gameObject);
+    this.projScreenMatrix = new THREE.Matrix4();
+    this.frustum = new THREE.Frustum();
+  }
+  update() {
+    const {camera} = globals;
+    this.projScreenMatrix.multiplyMatrices(
+        camera.projectionMatrix,
+        camera.matrixWorldInverse);
+    this.frustum.setFromProjectionMatrix(this.projScreenMatrix);
+  }
+}
+</pre>
+<p>Configurons ensuite un autre gameobject au moment de l'initialisation.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
+  prepModelsAndAnimations();
+
++  {
++    const gameObject = gameObjectManager.createGameObject(camera, 'camera');
++    globals.cameraInfo = gameObject.addComponent(CameraInfo);
++  }
+
+  {
+    const gameObject = gameObjectManager.createGameObject(scene, 'player');
+    gameObject.addComponent(Player);
+  }
+}
+</pre>
+<p>et maintenant nous pouvons l'utiliser dans le composant <code class="notranslate" translate="no">Player</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Player extends Component {
+  constructor(gameObject) {
+    super(gameObject);
+    const model = models.knight;
+    this.skinInstance = gameObject.addComponent(SkinInstance, model);
+    this.skinInstance.setAnimation('Run');
+    this.turnSpeed = globals.moveSpeed / 4;
++    this.offscreenTimer = 0;
++    this.maxTimeOffScreen = 3;
+  }
+  update() {
+-    const {deltaTime, moveSpeed} = globals;
++    const {deltaTime, moveSpeed, cameraInfo} = globals;
+    const {transform} = this.gameObject;
+    const delta = (inputManager.keys.left.down  ?  1 : 0) +
+                  (inputManager.keys.right.down ? -1 : 0);
+    transform.rotation.y += this.turnSpeed * delta * deltaTime;
+    transform.translateOnAxis(kForward, moveSpeed * deltaTime);
+
++    const {frustum} = cameraInfo;
++    if (frustum.containsPoint(transform.position)) {
++      this.offscreenTimer = 0;
++    } else {
++      this.offscreenTimer += deltaTime;
++      if (this.offscreenTimer &gt;= this.maxTimeOffScreen) {
++        transform.position.set(0, 0, 0);
++      }
++    }
+  }
+}
+</pre>
+<p>Une dernière chose avant d'essayer, ajoutons le support des écrans tactiles
+pour mobile. Tout d'abord, ajoutons un peu de code HTML pour le toucher</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="ui"&gt;
++    &lt;div id="left"&gt;&lt;img src="../resources/images/left.svg"&gt;&lt;/div&gt;
++    &lt;div style="flex: 0 0 40px;"&gt;&lt;/div&gt;
++    &lt;div id="right"&gt;&lt;img src="../resources/images/right.svg"&gt;&lt;/div&gt;
++  &lt;/div&gt;
+  &lt;div id="loading"&gt;
+    &lt;div&gt;
+      &lt;div&gt;...chargement...&lt;/div&gt;
+      &lt;div class="progress"&gt;&lt;div id="progressbar"&gt;&lt;/div&gt;&lt;/div&gt;
+    &lt;/div&gt;
+  &lt;/div&gt;
++  &lt;div id="labels"&gt;&lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>et un peu de CSS pour le styler</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#ui {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-items: center;
+  align-content: stretch;
+}
+#ui&gt;div {
+  display: flex;
+  align-items: flex-end;
+  flex: 1 1 auto;
+}
+.bright {
+  filter: brightness(2);
+}
+#left {
+  justify-content: flex-end;
+}
+#right {
+  justify-content: flex-start;
+}
+#ui img {
+  padding: 10px;
+  width: 80px;
+  height: 80px;
+  display: block;
+}
+#labels {
+  position: absolute;  /* nous permet de nous positionner à l'intérieur du conteneur */
+  left: 0;             /* fait que notre position est en haut à gauche du conteneur */
+  top: 0;
+  color: white;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  pointer-events: none;
+}
+#labels&gt;div {
+  position: absolute;  /* nous permet de les positionner à l'intérieur du conteneur */
+  left: 0;             /* fait que leur position par défaut est en haut à gauche du conteneur */
+  top: 0;
+  font-size: large;
+  font-family: monospace;
+  user-select: none;   /* n'autorise pas la sélection du texte */
+  text-shadow:         /* crée un contour noir */
+    -1px -1px 0 #000,
+     0   -1px 0 #000,
+     1px -1px 0 #000,
+     1px  0   0 #000,
+     1px  1px 0 #000,
+     0    1px 0 #000,
+    -1px  1px 0 #000,
+    -1px  0   0 #000;
+}
+</pre>
+<p>L'idée ici est d'avoir une div, <code class="notranslate" translate="no">#ui</code>, qui
+couvre toute la page. À l'intérieur, il y aura 2 divs, <code class="notranslate" translate="no">#left</code> et <code class="notranslate" translate="no">#right</code>,
+chacune occupant près de la moitié de la largeur de la page et toute la hauteur de l'écran.
+Entre les deux, il y a un séparateur de 40px. Si l'utilisateur glisse son doigt
+sur le côté gauche ou droit, nous devons mettre à jour <code class="notranslate" translate="no">keys.left</code> et <code class="notranslate" translate="no">keys.right</code>
+dans l'<code class="notranslate" translate="no">InputManager</code>. Cela rend tout l'écran sensible au toucher,
+ce qui semble mieux que de simples petites flèches.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class InputManager {
+  constructor() {
+    this.keys = {};
+    const keyMap = new Map();
+
+    const setKey = (keyName, pressed) =&gt; {
+      const keyState = this.keys[keyName];
+      keyState.justPressed = pressed &amp;&amp; !keyState.down;
+      keyState.down = pressed;
+    };
+
+    const addKey = (keyCode, name) =&gt; {
+      this.keys[name] = { down: false, justPressed: false };
+      keyMap.set(keyCode, name);
+    };
+
+    const setKeyFromKeyCode = (keyCode, pressed) =&gt; {
+      const keyName = keyMap.get(keyCode);
+      if (!keyName) {
+        return;
+      }
+      setKey(keyName, pressed);
+    };
+
+    addKey(37, 'left');
+    addKey(39, 'right');
+    addKey(38, 'up');
+    addKey(40, 'down');
+    addKey(90, 'a');
+    addKey(88, 'b');
+
+    window.addEventListener('keydown', (e) =&gt; {
+      setKeyFromKeyCode(e.keyCode, true);
+    });
+    window.addEventListener('keyup', (e) =&gt; {
+      setKeyFromKeyCode(e.keyCode, false);
+    });
+
++    const sides = [
++      { elem: document.querySelector('#left'),  key: 'left'  },
++      { elem: document.querySelector('#right'), key: 'right' },
++    ];
++
++    const clearKeys = () =&gt; {
++      for (const {key} of sides) {
++          setKey(key, false);
++      }
++    };
++
++    const handleMouseMove = (e) =&gt; {
++      e.preventDefault();
++      // ceci est nécessaire car nous appelons preventDefault();
++      // nous avons également donné au canvas un tabindex afin qu'il puisse
++      // obtenir le focus
++      canvas.focus();
++      window.addEventListener('pointermove', handleMouseMove);
++      window.addEventListener('pointerup', handleMouseUp);
++
++      for (const {elem, key} of sides) {
++        let pressed = false;
++        const rect = elem.getBoundingClientRect();
++        const x = e.clientX;
++        const y = e.clientY;
++        const inRect = x &gt;= rect.left &amp;&amp; x &lt; rect.right &amp;&amp;
++                       y &gt;= rect.top &amp;&amp; y &lt; rect.bottom;
++        if (inRect) {
++          pressed = true;
++        }
++        setKey(key, pressed);
++      }
++    };
++
++    function handleMouseUp() {
++      clearKeys();
++      window.removeEventListener('pointermove', handleMouseMove, {passive: false});
++      window.removeEventListener('pointerup', handleMouseUp);
++    }
++
++    const uiElem = document.querySelector('#ui');
++    uiElem.addEventListener('pointerdown', handleMouseMove, {passive: false});
++
++    uiElem.addEventListener('touchstart', (e) =&gt; {
++      // empêcher le défilement
++      e.preventDefault();
++    }, {passive: false});
+  }
+  update() {
+    for (const keyState of Object.values(this.keys)) {
+      if (keyState.justPressed) {
+        keyState.justPressed = false;
+      }
+    }
+  }
+}
+</pre>
+<p>Et maintenant, nous devrions pouvoir contrôler le personnage avec les touches curseur gauche et droite
+ou avec nos doigts sur un écran tactile.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/game-player-input.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/game-player-input.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Idéalement, nous ferions quelque chose d'autre si le joueur sortait de l'écran, comme déplacer
+la caméra ou peut-être considérer que hors écran = mort, mais cet article va déjà être
+trop long, donc pour l'instant, se téléporter au milieu était la chose la plus simple.</p>
+<p>Ajoutons quelques animaux. Nous pouvons commencer de manière similaire au <code class="notranslate" translate="no">Player</code> en créant
+un composant <code class="notranslate" translate="no">Animal</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Animal extends Component {
+  constructor(gameObject, model) {
+    super(gameObject);
+    const skinInstance = gameObject.addComponent(SkinInstance, model);
+    skinInstance.mixer.timeScale = globals.moveSpeed / 4;
+    skinInstance.setAnimation('Idle');
+  }
+}
+</pre>
+<p>Le code ci-dessus définit le <a href="/docs/#api/en/animation/AnimationMixer.timeScale"><code class="notranslate" translate="no">AnimationMixer.timeScale</code></a> pour régler la vitesse de lecture
+des animations par rapport à la vitesse de déplacement. De cette façon, si nous
+ajustons la vitesse de déplacement, l'animation accélérera ou ralentira également.</p>
+<p>Pour commencer, nous pourrions configurer un animal de chaque type</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
+  // masquer la barre de chargement
+  const loadingElem = document.querySelector('#loading');
+  loadingElem.style.display = 'none';
+
+  prepModelsAndAnimations();
+  {
+    const gameObject = gameObjectManager.createGameObject(camera, 'camera');
+    globals.cameraInfo = gameObject.addComponent(CameraInfo);
+  }
+
+  {
+    const gameObject = gameObjectManager.createGameObject(scene, 'player');
+    globals.player = gameObject.addComponent(Player);
+    globals.congaLine = [gameObject];
+  }
+
++  const animalModelNames = [
++    'pig',
++    'cow',
++    'llama',
++    'pug',
++    'sheep',
++    'zebra',
++    'horse',
++  ];
++  animalModelNames.forEach((name, ndx) =&gt; {
++    const gameObject = gameObjectManager.createGameObject(scene, name);
++    gameObject.addComponent(Animal, models[name]);
++    gameObject.transform.position.x = (ndx + 1) * 5;
++  });
+}
+</pre>
+<p>Et cela nous donnerait des animaux debout à l'écran, mais nous voulons qu'ils fassent quelque chose.</p>
+<p>Faisons en sorte qu'ils suivent le joueur en file indienne, mais seulement si le joueur s'approche suffisamment.
+Pour cela, nous avons besoin de plusieurs états.</p>
+<ul>
+<li><p>Inactif :</p>
+<p>L'animal attend que le joueur s'approche</p>
+</li>
+<li><p>Attendre la fin de la ligne :</p>
+<p>L'animal a été "tagué" par le joueur, mais doit maintenant attendre que l'animal
+au bout de la ligne arrive pour pouvoir rejoindre la fin de la ligne.</p>
+</li>
+<li><p>Aller au dernier :</p>
+<p>L'animal doit marcher jusqu'à l'endroit où se trouvait l'animal qu'il suit,
+tout en enregistrant un historique de la position actuelle de l'animal qu'il suit.</p>
+</li>
+<li><p>Suivre</p>
+<p>L'animal doit continuer à enregistrer un historique de la position de l'animal qu'il suit
+tout en se déplaçant vers l'endroit où se trouvait cet animal auparavant.</p>
+</li>
+</ul>
+<p>Il existe de nombreuses façons de gérer différents états comme ceux-ci. Une méthode courante consiste à utiliser
+une <a href="https://www.google.com/search?q=finite+state+machine">machine à états finis</a> (Finite State Machine) et
+à construire une classe pour nous aider à gérer l'état.</p>
+<p>Alors, faisons cela.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class FiniteStateMachine {
+  constructor(states, initialState) {
+    this.states = states;
+    this.transition(initialState);
+  }
+  get state() {
+    return this.currentState;
+  }
+  transition(state) {
+    const oldState = this.states[this.currentState];
+    if (oldState &amp;&amp; oldState.exit) {
+      oldState.exit.call(this);
+    }
+    this.currentState = state;
+    const newState = this.states[state];
+    if (newState.enter) {
+      newState.enter.call(this);
+    }
+  }
+  update() {
+    const state = this.states[this.currentState];
+    if (state.update) {
+      state.update.call(this);
+    }
+  }
+}
+</pre>
+<p>Voici une classe simple. Nous lui passons un objet contenant un ensemble d'états.
+Chaque état a 3 fonctions optionnelles : <code class="notranslate" translate="no">enter</code>, <code class="notranslate" translate="no">update</code> et <code class="notranslate" translate="no">exit</code>.
+Pour changer d'état, nous appelons <code class="notranslate" translate="no">FiniteStateMachine.transition</code> et lui passons
+le nom du nouvel état. Si l'état actuel a une fonction <code class="notranslate" translate="no">exit</code>,
+elle est appelée. Ensuite, si le nouvel état a une fonction <code class="notranslate" translate="no">enter</code>,
+elle est appelée. Enfin, à chaque image, <code class="notranslate" translate="no">FiniteStateMachine.update</code> appelle la fonction <code class="notranslate" translate="no">update</code>
+de l'état actuel.</p>
+<p>Utilisons-le pour gérer les états des animaux.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Retourne vrai si obj1 et obj2 sont proches
+function isClose(obj1, obj1Radius, obj2, obj2Radius) {
+  const minDist = obj1Radius + obj2Radius;
+  const dist = obj1.position.distanceTo(obj2.position);
+  return dist &lt; minDist;
+}
+
+// maintient v entre -min et +min
+function minMagnitude(v, min) {
+  return Math.abs(v) &gt; min
+      ? min * Math.sign(v)
+      : v;
+}
+
+const aimTowardAndGetDistance = function() {
+  const delta = new THREE.Vector3();
+
+  return function aimTowardAndGetDistance(source, targetPos, maxTurn) {
+    delta.subVectors(targetPos, source.position);
+    // calculer la direction dans laquelle nous voulons faire face
+    const targetRot = Math.atan2(delta.x, delta.z) + Math.PI * 1.5;
+    // tourner dans la direction la plus courte
+    const deltaRot = (targetRot - source.rotation.y + Math.PI * 1.5) % (Math.PI * 2) - Math.PI;
+    // s'assurer que nous ne tournons pas plus vite que maxTurn
+    const deltaRotation = minMagnitude(deltaRot, maxTurn);
+    // maintenir la rotation entre 0 et Math.PI * 2
+    source.rotation.y = THREE.MathUtils.euclideanModulo(
+        source.rotation.y + deltaRotation, Math.PI * 2);
+    // retourner la distance à la cible
+    return delta.length();
+  };
+}();
+
+class Animal extends Component {
+  constructor(gameObject, model) {
+    super(gameObject);
++    const hitRadius = model.size / 2;
+    const skinInstance = gameObject.addComponent(SkinInstance, model);
+    skinInstance.mixer.timeScale = globals.moveSpeed / 4;
++    const transform = gameObject.transform;
++    const playerTransform = globals.player.gameObject.transform;
++    const maxTurnSpeed = Math.PI * (globals.moveSpeed / 4);
++    const targetHistory = [];
++    let targetNdx = 0;
++
++    function addHistory() {
++      const targetGO = globals.congaLine[targetNdx];
++      const newTargetPos = new THREE.Vector3();
++      newTargetPos.copy(targetGO.transform.position);
++      targetHistory.push(newTargetPos);
++    }
++
++    this.fsm = new FiniteStateMachine({
++      idle: {
++        enter: () =&gt; {
++          skinInstance.setAnimation('Idle');
++        },
++        update: () =&gt; {
++          // vérifier si le joueur est proche
++          if (isClose(transform, hitRadius, playerTransform, globals.playerRadius)) {
++            this.fsm.transition('waitForEnd');
++          }
++        },
++      },
++      waitForEnd: {
++        enter: () =&gt; {
++          skinInstance.setAnimation('Jump');
++        },
++        update: () =&gt; {
++          // obtenir le gameObject à la fin de la file indienne
++          const lastGO = globals.congaLine[globals.congaLine.length - 1];
++          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;
++          const targetPos = lastGO.transform.position;
++          aimTowardAndGetDistance(transform, targetPos, deltaTurnSpeed);
++          // vérifier si le dernier élément de la file indienne est proche
++          if (isClose(transform, hitRadius, lastGO.transform, globals.playerRadius)) {
++            this.fsm.transition('goToLast');
++          }
++        },
++      },
++      goToLast: {
++        enter: () =&gt; {
++          // se souvenir de qui nous suivons
++          targetNdx = globals.congaLine.length - 1;
++          // nous ajouter à la file indienne
++          globals.congaLine.push(gameObject);
++          skinInstance.setAnimation('Walk');
++        },
++        update: () =&gt; {
++          addHistory();
++          // marcher jusqu'au point le plus ancien de l'historique
++          const targetPos = targetHistory[0];
++          const maxVelocity = globals.moveSpeed * globals.deltaTime;
++          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;
++          const distance = aimTowardAndGetDistance(transform, targetPos, deltaTurnSpeed);
++          const velocity = distance;
++          transform.translateOnAxis(kForward, Math.min(velocity, maxVelocity));
++          if (distance &lt;= maxVelocity) {
++            this.fsm.transition('follow');
++          }
++        },
++      },
++      follow: {
++        update: () =&gt; {
++          addHistory();
++          // supprimer l'historique le plus ancien et nous placer simplement là.
++          const targetPos = targetHistory.shift();
++          transform.position.copy(targetPos);
++          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;
++          aimTowardAndGetDistance(transform, targetHistory[0], deltaTurnSpeed);
++        },
++      },
++    }, 'idle');
++  }
++  update() {
++    this.fsm.update();
++  }
+}
+</pre>
+<p>C'était un gros morceau de code, mais il fait ce qui a été décrit ci-dessus.
+J'espère que si vous parcourez chaque état, ce sera clair.</p>
+<p>Quelques choses que nous devons ajouter. Nous devons faire en sorte que le joueur s'ajoute
+aux variables globales afin que les animaux puissent le trouver, et nous devons commencer la
+file indienne avec le <code class="notranslate" translate="no">GameObject</code> du joueur.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
+
+  ...
+
+  {
+    const gameObject = gameObjectManager.createGameObject(scene, 'player');
++    globals.player = gameObject.addComponent(Player);
++    globals.congaLine = [gameObject];
+  }
+
+}
+</pre>
+<p>Nous devons également calculer une taille pour chaque modèle</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function prepModelsAndAnimations() {
++  const box = new THREE.Box3();
++  const size = new THREE.Vector3();
+  Object.values(models).forEach(model =&gt; {
++    box.setFromObject(model.gltf.scene);
++    box.getSize(size);
++    model.size = size.length();
+    const animsByName = {};
+    model.gltf.animations.forEach((clip) =&gt; {
+      animsByName[clip.name] = clip;
+      // Devrait vraiment être corrigé dans le fichier .blend
+      if (clip.name === 'Walk') {
+        clip.duration /= 2;
+      }
+    });
+    model.animations = animsByName;
+  });
+}
+</pre>
+<p>Et nous avons besoin que le joueur enregistre sa taille</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Player extends Component {
+  constructor(gameObject) {
+    super(gameObject);
+    const model = models.knight;
++    globals.playerRadius = model.size / 2;
+</pre>
+<p>En y pensant maintenant, il aurait probablement été plus judicieux
+que les animaux ciblent simplement la tête de la file indienne
+au lieu du joueur spécifiquement. Peut-être que je reviendrai dessus
+et modifierai cela plus tard.</p>
+<p>Lorsque j'ai commencé cela, j'ai utilisé un seul rayon pour tous les animaux,
+mais bien sûr, ce n'était pas bon, car le carlin est beaucoup plus petit que le cheval.
+J'ai donc ajouté les différentes tailles, mais je voulais pouvoir visualiser
+les choses. Pour ce faire, j'ai créé un composant <code class="notranslate" translate="no">StatusDisplayHelper</code>.</p>
+<p>J'utilise un <a href="/docs/#api/en/helpers/PolarGridHelper"><code class="notranslate" translate="no">PolarGridHelper</code></a> pour dessiner un cercle autour de chaque personnage,
+et il utilise des éléments html pour permettre à chaque personnage d'afficher un certain statut en utilisant
+les techniques couvertes dans <a href="align-html-elements-to-3d.html">l'article sur l'alignement des éléments html en 3D</a>.</p>
+<p>Nous devons d'abord ajouter du code HTML pour héberger ces éléments</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
+  &lt;div id="ui"&gt;
+    &lt;div id="left"&gt;&lt;img src="../resources/images/left.svg"&gt;&lt;/div&gt;
+    &lt;div style="flex: 0 0 40px;"&gt;&lt;/div&gt;
+    &lt;div id="right"&gt;&lt;img src="../resources/images/right.svg"&gt;&lt;/div&gt;
+  &lt;/div&gt;
+  &lt;div id="loading"&gt;
+    &lt;div&gt;
+      &lt;div&gt;...chargement...&lt;/div&gt;
+      &lt;div class="progress"&gt;&lt;div id="progressbar"&gt;&lt;/div&gt;&lt;/div&gt;
+    &lt;/div&gt;
+  &lt;/div&gt;
++  &lt;div id="labels"&gt;&lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>Et ajouter du CSS pour eux</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#ui {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-items: center;
+  align-content: stretch;
+}
+#ui&gt;div {
+  display: flex;
+  align-items: flex-end;
+  flex: 1 1 auto;
+}
+.bright {
+  filter: brightness(2);
+}
+#left {
+  justify-content: flex-end;
+}
+#right {
+  justify-content: flex-start;
+}
+#ui img {
+  padding: 10px;
+  width: 80px;
+  height: 80px;
+  display: block;
+}
+#labels {
+  position: absolute;  /* nous permet de nous positionner à l'intérieur du conteneur */
+  left: 0;             /* fait que notre position est en haut à gauche du conteneur */
+  top: 0;
+  color: white;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  pointer-events: none;
+}
+#labels&gt;div {
+  position: absolute;  /* nous permet de les positionner à l'intérieur du conteneur */
+  left: 0;             /* fait que leur position par défaut est en haut à gauche du conteneur */
+  top: 0;
+  font-size: large;
+  font-family: monospace;
+  user-select: none;   /* n'autorise pas la sélection du texte */
+  text-shadow:         /* crée un contour noir */
+    -1px -1px 0 #000,
+     0   -1px 0 #000,
+     1px -1px 0 #000,
+     1px  0   0 #000,
+     1px  1px 0 #000,
+     0    1px 0 #000,
+    -1px  1px 0 #000,
+    -1px  0   0 #000;
+}
+</pre>
+<p>Voici ensuite le composant</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const labelContainerElem = document.querySelector('#labels');
+
+class StateDisplayHelper extends Component {
+  constructor(gameObject, size) {
+    super(gameObject);
+    this.elem = document.createElement('div');
+    labelContainerElem.appendChild(this.elem);
+    this.pos = new THREE.Vector3();
+
+    this.helper = new THREE.PolarGridHelper(size / 2, 1, 1, 16);
+    gameObject.transform.add(this.helper);
+  }
+  setState(s) {
+    this.elem.textContent = s;
+  }
+  setColor(cssColor) {
+    this.elem.style.color = cssColor;
+    this.helper.material.color.set(cssColor);
+  }
+  update() {
+    const {pos} = this;
+    const {transform} = this.gameObject;
+    const {canvas} = globals;
+    pos.copy(transform.position);
+
+    // obtenir les coordonnées d'écran normalisées de cette position
+    // x et y seront dans la plage -1 à +1, avec x = -1 étant
+    // à gauche et y = -1 étant en bas
+    pos.project(globals.camera);
+
+    // convertir la position normalisée en coordonnées CSS
+    const x = (pos.x *  .5 + .5) * canvas.clientWidth;
+    const y = (pos.y * -.5 + .5) * canvas.clientHeight;
+
+    // déplacer l'élément à cette position
+    this.elem.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
+  }
+}
+</pre>
+<p>Et nous pouvons ensuite les ajouter aux animaux comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Animal extends Component {
+  constructor(gameObject, model) {
+    super(gameObject);
++    this.helper = gameObject.addComponent(StateDisplayHelper, model.size);
+
+     ...
+
+  }
+  update() {
+    this.fsm.update();
++    const dir = THREE.MathUtils.radToDeg(this.gameObject.transform.rotation.y);
++    this.helper.setState(`${this.fsm.state}:${dir.toFixed(0)}`);
+  }
+}
+</pre>
+<p>Pendant que nous y sommes, faisons en sorte que nous puissions les activer/désactiver en utilisant lil-gui, comme
+nous l'avons fait ailleurs.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
+import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';
++import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const gui = new GUI();
++gui.add(globals, 'debug').onChange(showHideDebugInfo);
++showHideDebugInfo();
+
+const labelContainerElem = document.querySelector('#labels');
++function showHideDebugInfo() {
++  labelContainerElem.style.display = globals.debug ? '' : 'none';
++}
++showHideDebugInfo();
+
+class StateDisplayHelper extends Component {
+
+  ...
+
+  update() {
++    this.helper.visible = globals.debug;
++    if (!globals.debug) {
++      return;
++    }
+
+    ...
+  }
+}
+</pre>
+<p>Et avec cela, nous obtenons une sorte de début de jeu.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/game-conga-line.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/game-conga-line.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>À l'origine, j'avais l'intention de créer un <a href="https://www.google.com/search?q=snake+game">jeu de serpent</a>
+où, à mesure que vous ajoutez des animaux à votre ligne, cela devient plus difficile car vous devez éviter
+de les heurter. J'aurais également placé des obstacles dans la scène et peut-être une clôture ou une sorte
+de barrière autour du périmètre.</p>
+<p>Malheureusement, les animaux sont longs et minces. Vu d'en haut, voici le zèbre.</p>
+<div class="threejs_center"><img src="../resources/images/zebra.png" style="width: 113px;"></div>
+
+<p>Le code jusqu'à présent utilise des collisions circulaires, ce qui signifie que si nous avions des obstacles comme une clôture,
+cela serait considéré comme une collision</p>
+<div class="threejs_center"><img src="../resources/images/zebra-collisions.svg" style="width: 400px;"></div>
+
+<p>Ce n'est pas bon. Même d'animal à animal, nous aurions le même problème.</p>
+<p>J'ai pensé à écrire un système de collision rectangle à rectangle en 2D, mais j'ai
+rapidement réalisé que cela pourrait vraiment représenter beaucoup de code. Vérifier si 2 boîtes
+orientées arbitrairement se chevauchent n'est pas trop de code, et pour notre jeu avec seulement quelques
+objets, cela pourrait fonctionner, mais en y regardant de plus près, après quelques objets, vous
+commencez rapidement à avoir besoin d'optimiser la vérification des collisions. Tout d'abord, vous pourriez parcourir tous
+les objets qui peuvent potentiellement entrer en collision les uns avec les autres et vérifier leurs
+sphères englobantes, leurs cercles englobants ou leurs boîtes englobantes alignées sur les axes. Une fois que vous
+savez quels objets <em>pourraient</em> entrer en collision, vous devez faire plus de travail pour vérifier s'ils
+entrent <em>réellement</em> en collision. Souvent, même la vérification des sphères englobantes est trop
+de travail, et vous avez besoin d'une sorte de meilleure structure spatiale pour les objets afin de pouvoir
+vérifier plus rapidement uniquement les objets potentiellement proches les uns des autres.</p>
+<p>Ensuite, une fois que vous avez écrit le code pour vérifier si 2 objets entrent en collision, vous voulez généralement
+créer un système de collision plutôt que de demander manuellement "est-ce que je collisionne avec ces
+objets". Un système de collision émet des événements ou appelle des callbacks en relation avec
+les collisions. L'avantage est qu'il peut vérifier toutes les collisions en une seule fois, de sorte qu'aucun
+objet n'est vérifié plus d'une fois, alors que si vous appelez manuellement une fonction
+"est-ce que je collisionne", les objets sont souvent vérifiés plus d'une fois, ce qui fait perdre du temps.</p>
+<p>Créer ce système de collision ne représenterait probablement pas plus de 100 à 300 lignes de code
+pour vérifier uniquement les rectangles orientés arbitrairement, mais c'est toujours beaucoup plus de code,
+il a donc semblé préférable de l'omettre.</p>
+<p>Une autre solution aurait été d'essayer de trouver d'autres personnages qui sont majoritairement
+circulaires vus du dessus. D'autres personnages humanoïdes par exemple, au lieu
+d'animaux, auquel cas la vérification circulaire pourrait fonctionner d'animal à animal.
+Cela ne fonctionnerait pas d'animal à clôture ; eh bien, nous devrions ajouter une vérification de cercle à rectangle. J'ai
+pensé à faire de la clôture une clôture de buissons ou de poteaux, quelque chose de circulaire,
+mais alors il me faudrait probablement 120 à 200 d'entre eux pour entourer la zone de jeu,
+ce qui entraînerait les problèmes d'optimisation mentionnés ci-dessus.</p>
+<p>Ce sont des raisons pour lesquelles de nombreux jeux utilisent une solution existante. Souvent, ces solutions
+font partie d'une bibliothèque de physique. La bibliothèque de physique a besoin de savoir si les objets
+entrent en collision les uns avec les autres, donc en plus de fournir la physique, elles peuvent également être utilisées
+pour détecter les collisions.</p>
+<p>Si vous cherchez une solution, certains exemples three.js utilisent
+<a href="https://github.com/kripken/ammo.js/">ammo.js</a>, cela pourrait donc être une option.</p>
+<p>Une autre solution aurait pu être de placer les obstacles sur une grille
+et d'essayer de faire en sorte que chaque animal et le joueur n'aient qu'à regarder
+la grille. Bien que cela serait performant, j'ai estimé qu'il valait mieux laisser
+cela comme un exercice pour le lecteur 😜</p>
+<p>Une chose de plus, de nombreux systèmes de jeu ont ce qu'on appelle des <a href="https://www.google.com/search?q=coroutines"><em>coroutines</em></a>.
+Les coroutines sont des routines qui peuvent se mettre en pause pendant l'exécution et reprendre plus tard.</p>
+<p>Faisons en sorte que le personnage principal émette des notes de musique comme s'il dirigeait
+la ligne en chantant. Il existe de nombreuses façons de mettre cela en œuvre, mais pour l'instant,
+faisons-le en utilisant des coroutines.</p>
+<p>Tout d'abord, voici une classe pour gérer les coroutines</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function* waitSeconds(duration) {
+  while (duration &gt; 0) {
+    duration -= globals.deltaTime;
+    yield;
+  }
+}
+
+class CoroutineRunner {
+  constructor() {
+    this.generatorStacks = [];
+    this.addQueue = [];
+    this.removeQueue = new Set();
+  }
+  isBusy() {
+    return this.addQueue.length + this.generatorStacks.length &gt; 0;
+  }
+  add(generator, delay = 0) {
+    const genStack = [generator];
+    if (delay) {
+      genStack.push(waitSeconds(delay));
+    }
+    this.addQueue.push(genStack);
+  }
+  remove(generator) {
+    this.removeQueue.add(generator);
+  }
+  update() {
+    this._addQueued();
+    this._removeQueued();
+    for (const genStack of this.generatorStacks) {
+      const main = genStack[0];
+      // Gérer si une coroutine en supprime une autre
+      if (this.removeQueue.has(main)) {
+        continue;
+      }
+      while (genStack.length) {
+        const topGen = genStack[genStack.length - 1];
+        const {value, done} = topGen.next();
+        if (done) {
+          if (genStack.length === 1) {
+            this.removeQueue.add(topGen);
+            break;
+          }
+          genStack.pop();
+        } else if (value) {
+          genStack.push(value);
+        } else {
+          break;
+        }
+      }
+    }
+    this._removeQueued();
+  }
+  _addQueued() {
+    if (this.addQueue.length) {
+      this.generatorStacks.splice(this.generatorStacks.length, 0, ...this.addQueue);
+      this.addQueue = [];
+    }
+  }
+  _removeQueued() {
+    if (this.removeQueue.size) {
+      this.generatorStacks = this.generatorStacks.filter(genStack =&gt; !this.removeQueue.has(genStack[0]));
+      this.removeQueue.clear();
+    }
+  }
+}
+</pre>
+<p>Il fait des choses similaires à <code class="notranslate" translate="no">SafeArray</code> pour s'assurer qu'il est sûr d'ajouter ou de supprimer
+des coroutines pendant que d'autres coroutines s'exécutent. Il gère également les coroutines imbriquées.</p>
+<p>Pour créer une coroutine, vous créez une <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*">fonction génératrice JavaScript</a>.
+Une fonction génératrice est précédée du mot-clé <code class="notranslate" translate="no">function*</code> (l'astérisque est important !)</p>
+<p>Les fonctions génératrices peuvent <code class="notranslate" translate="no">yield</code> (céder). Par exemple</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function* countOTo9() {
+  for (let i = 0; i &lt; 10; ++i) {
+    console.log(i);
+    yield;
+  }
+}
+</pre>
+<p>Si nous ajoutions cette fonction au <code class="notranslate" translate="no">CoroutineRunner</code> ci-dessus, elle imprimerait
+chaque nombre, de 0 à 9, une fois par image, ou plutôt une fois par appel de <code class="notranslate" translate="no">runner.update</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const runner = new CoroutineRunner();
+runner.add(count0To9);
+while(runner.isBusy()) {
+  runner.update();
+}
+</pre>
+<p>Les coroutines sont supprimées automatiquement lorsqu'elles sont terminées.</p>
+<p>Pour supprimer une coroutine prématurément, avant qu'elle n'atteigne la fin, vous devez conserver
+une référence à son générateur comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gen = count0To9();
+runner.add(gen);
+
+// plus tard
+
+runner.remove(gen);
+</pre>
+<p>En tout cas, dans le joueur, utilisons une coroutine pour émettre une note toutes les demi-secondes à 1 seconde.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Player extends Component {
+  constructor(gameObject) {
+
+    ...
+
++    this.runner = new CoroutineRunner();
++
++    function* emitNotes() {
++      for (;;) {
++        yield waitSeconds(rand(0.5, 1));
++        const noteGO = gameObjectManager.createGameObject(scene, 'note');
++        noteGO.transform.position.copy(gameObject.transform.position);
++        noteGO.transform.position.y += 5;
++        noteGO.addComponent(Note);
++      }
++    }
++
++    this.runner.add(emitNotes());
+  }
+  update() {
++    this.runner.update();
+
+  ...
+
+  }
+}
+
+function rand(min, max) {
+  if (max === undefined) {
+    max = min;
+    min = 0;
+  }
+  return Math.random() * (max - min) + min;
+}
+</pre>
+<p>Vous pouvez voir que nous créons un <code class="notranslate" translate="no">CoroutineRunner</code> et ajoutons une coroutine <code class="notranslate" translate="no">emitNotes</code>.
+Cette fonction s'exécutera indéfiniment, attendant 0,5 à 1 seconde, puis créant un objet de jeu
+avec un composant <code class="notranslate" translate="no">Note</code>.</p>
+<p>Pour le composant <code class="notranslate" translate="no">Note</code>, créons d'abord une texture avec une note dessus et,
+au lieu de charger une image de note, créons-en une à l'aide d'un canvas, comme nous l'avons vu dans <a href="canvas-textures.html">l'article sur les textures de canvas</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeTextTexture(str) {
+  const ctx = document.createElement('canvas').getContext('2d');
+  ctx.canvas.width = 64;
+  ctx.canvas.height = 64;
+  ctx.font = '60px sans-serif';
+  ctx.textAlign = 'center';
+  ctx.textBaseline = 'middle';
+  ctx.fillStyle = '#FFF';
+  ctx.fillText(str, ctx.canvas.width / 2, ctx.canvas.height / 2);
+  return new THREE.CanvasTexture(ctx.canvas);
+}
+const noteTexture = makeTextTexture('♪');
+</pre>
+<p>La texture que nous créons ci-dessus est blanche, ce qui signifie que lorsque nous l'utilisons,
+nous pouvons définir la couleur du matériau et obtenir une note de n'importe quelle couleur.</p>
+<p>Maintenant que nous avons une texture de note, voici le composant <code class="notranslate" translate="no">Note</code>.
+Il utilise <a href="/docs/#api/en/materials/SpriteMaterial"><code class="notranslate" translate="no">SpriteMaterial</code></a> et un <a href="/docs/#api/en/objects/Sprite"><code class="notranslate" translate="no">Sprite</code></a>, comme nous l'avons vu dans
+<a href="billboards.html">l'article sur les billboards</a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Note extends Component {
+  constructor(gameObject) {
+    super(gameObject);
+    const {transform} = gameObject;
+    const noteMaterial = new THREE.SpriteMaterial({
+      color: new THREE.Color().setHSL(rand(1), 1, 0.5),
+      map: noteTexture,
+      side: THREE.DoubleSide,
+      transparent: true,
+    });
+    const note = new THREE.Sprite(noteMaterial);
+    note.scale.setScalar(3);
+    transform.add(note);
+    this.runner = new CoroutineRunner();
+    const direction = new THREE.Vector3(rand(-0.2, 0.2), 1, rand(-0.2, 0.2));
+
+    function* moveAndRemove() {
+      for (let i = 0; i &lt; 60; ++i) {
+        transform.translateOnAxis(direction, globals.deltaTime * 10);
+        noteMaterial.opacity = 1 - (i / 60);
+        yield;
+      }
+      transform.parent.remove(transform);
+      gameObjectManager.removeGameObject(gameObject);
+    }
+
+    this.runner.add(moveAndRemove());
+  }
+  update() {
+    this.runner.update();
+  }
+}
+</pre>
+<p>Tout ce qu'il fait est de configurer un <a href="/docs/#api/en/objects/Sprite"><code class="notranslate" translate="no">Sprite</code></a>, puis de choisir une vitesse aléatoire et de déplacer
+la transformation à cette vitesse pendant 60 images, tout en estompant la note
+en définissant l'<a href="/docs/#api/en/materials/Material#opacity"><code class="notranslate" translate="no">opacity</code></a> du matériau.
+Après la boucle, il supprime la transformation
+de la scène et la note elle-même des gameobjects actifs.</p>
+<p>Une dernière chose, ajoutons quelques animaux supplémentaires.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function init() {
+
+   ...
+
+  const animalModelNames = [
+    'pig',
+    'cow',
+    'llama',
+    'pug',
+    'sheep',
+    'zebra',
+    'horse',
+  ];
++  const base = new THREE.Object3D();
++  const offset = new THREE.Object3D();
++  base.add(offset);
++
++  // positionner les animaux en spirale.
++  const numAnimals = 28;
++  const arc = 10;
++  const b = 10 / (2 * Math.PI);
++  let r = 10;
++  let phi = r / b;
++  for (let i = 0; i &lt; numAnimals; ++i) {
++    const name = animalModelNames[rand(animalModelNames.length) | 0];
+    const gameObject = gameObjectManager.createGameObject(scene, name);
+    gameObject.addComponent(Animal, models[name]);
++    base.rotation.y = phi;
++    offset.position.x = r;
++    offset.updateWorldMatrix(true, false);
++    offset.getWorldPosition(gameObject.transform.position);
++    phi += arc / r;
++    r = b * phi;
+  }
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/game-conga-line-w-notes.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/game-conga-line-w-notes.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Vous pourriez vous demander, pourquoi ne pas utiliser <code class="notranslate" translate="no">setTimeout</code> ? Le problème avec <code class="notranslate" translate="no">setTimeout</code>
+est qu'il n'est pas lié à l'horloge du jeu. Par exemple, ci-dessus, nous avons défini le temps
+maximum autorisé à s'écouler entre les images à 1/20ème de seconde.
+Notre système de coroutine respectera cette limite, mais <code class="notranslate" translate="no">setTimeout</code> ne le ferait pas.</p>
+<p>Bien sûr, nous aurions pu créer un simple minuteur nous-mêmes</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class Player ... {
+  update() {
+    this.noteTimer -= globals.deltaTime;
+    if (this.noteTimer &lt;= 0) {
+      // réinitialiser le minuteur
+      this.noteTimer = rand(0.5, 1);
+      // créer un gameobject avec un composant de note
+    }
+  }
+</pre>
+<p>Et pour ce cas particulier, cela aurait peut-être été mieux, mais à mesure que vous ajoutez
+de plus en plus de choses, vous aurez de plus en plus de variables ajoutées à vos classes,
+alors qu'avec les coroutines, vous pouvez souvent simplement <em>lancer et oublier</em>.</p>
+<p>Étant donné les états simples de nos animaux, nous aurions également pu les implémenter
+avec une coroutine sous la forme de</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// pseudo-code !
+function* animalCoroutine() {
+   setAnimation('Idle');
+   while(playerIsTooFar()) {
+     yield;
+   }
+   const target = endOfLine;
+   setAnimation('Jump');
+   while(targetIsTooFar()) {
+     aimAt(target);
+     yield;
+   }
+   setAnimation('Walk')
+   while(notAtOldestPositionOfTarget()) {
+     addHistory();
+     aimAt(target);
+     yield;
+   }
+   for(;;) {
+     addHistory();
+     const pos = history.unshift();
+     transform.position.copy(pos);
+     aimAt(history[0]);
+     yield;
+   }
+}
+</pre>
+<p>Cela aurait fonctionné, mais bien sûr, dès que nos états n'auraient pas été si linéaires,
+nous aurions dû passer à une <code class="notranslate" translate="no">FiniteStateMachine</code>.</p>
+<p>Il ne m'était pas non plus clair si les coroutines devaient s'exécuter indépendamment de leurs
+composants. Nous aurions pu créer un <code class="notranslate" translate="no">CoroutineRunner</code> global et y placer toutes
+les coroutines. Cela rendrait leur nettoyage plus difficile. En l'état actuel,
+si le gameobject est supprimé, tous ses composants sont supprimés et
+donc les CoroutineRunner créés ne sont plus appelés, et tout sera collecté par le
+ramasse-miettes. Si nous avions un CoroutineRunner global, il incomberait alors
+à chaque composant de supprimer toute coroutine qu'il aurait ajoutée, ou bien un autre
+mécanisme d'enregistrement des coroutines auprès d'un composant ou d'un gameobject particulier
+serait nécessaire afin que la suppression de l'un supprime les autres.</p>
+<p>Il y a beaucoup d'autres problèmes qu'un
+moteur de jeu normal gérerait. En l'état actuel, il n'y a pas d'ordre dans la façon dont
+les gameobjects ou leurs composants sont exécutés. Ils sont simplement exécutés dans l'ordre
+d'ajout. De nombreux systèmes de jeu ajoutent une priorité pour que l'ordre puisse être défini ou modifié.</p>
+<p>Un autre problème que nous avons rencontré est que le composant <code class="notranslate" translate="no">Note</code> supprime la transformation de son gameobject de la scène.
+Cela semble être quelque chose qui devrait se produire dans <code class="notranslate" translate="no">GameObject</code> puisque c'est <code class="notranslate" translate="no">GameObject</code>
+qui a ajouté la transformation en premier lieu. Peut-être que <code class="notranslate" translate="no">GameObject</code> devrait avoir
+une méthode <code class="notranslate" translate="no">dispose</code> qui est appelée par <code class="notranslate" translate="no">GameObjectManager.removeGameObject</code> ?</p>
+<p>Encore un autre problème est la façon dont nous appelons manuellement <code class="notranslate" translate="no">gameObjectManager.update</code> et <code class="notranslate" translate="no">inputManager.update</code>.
+Peut-être qu'il devrait y avoir un <code class="notranslate" translate="no">SystemManager</code> auquel ces services globaux pourraient s'ajouter,
+et chaque service aurait sa fonction <code class="notranslate" translate="no">update</code> appelée. De cette façon, si nous ajoutions un nouveau
+service comme <code class="notranslate" translate="no">CollisionManager</code>, nous pourrions simplement l'ajouter au gestionnaire de système sans
+avoir à modifier la boucle de rendu.</p>
+<p>Je vous laisse le soin de régler ce genre de problèmes.
+J'espère que cet article vous a donné quelques idées pour votre propre moteur de jeu.</p>
+<p>Peut-être devrais-je promouvoir un game jam. Si vous cliquez sur les boutons <em>jsfiddle</em> ou <em>codepen</em>
+au-dessus du dernier exemple, ils s'ouvriront sur ces sites prêts à être modifiés. Ajoutez des fonctionnalités,
+changez le jeu pour qu'un carlin mène un groupe de chevaliers. Utilisez l'animation de roulade du chevalier
+comme boule de bowling et faites un jeu de bowling avec des animaux. Faites une course de relais avec des animaux.
+Si vous créez un jeu sympa, postez un lien dans les commentaires ci-dessous.</p>
+<div class="footnotes">
+[<a id="parented">1</a>] : techniquement, cela fonctionnerait toujours si aucun des parents n'a de translation, de rotation ou d'échelle <a href="#parented-backref">§</a>.
+</div>
         </div>
       </div>
     </div>

+ 100 - 0
manual/fr/how-to-create-vr-content.html

@@ -0,0 +1,100 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Comment créer du contenu VR</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Comment créer du contenu VR">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Comment créer du contenu VR</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <p>
+            Ce guide fournit un bref aperçu des composants de base d'une application VR basée sur le web réalisée avec three.js.
+          </p>
+
+          <h2>Flux de travail</h2>
+
+          <p>
+            Tout d'abord, vous devez inclure [link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/webxr/VRButton.js VRButton.js]
+            dans votre projet.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import { VRButton } from 'three/addons/webxr/VRButton.js';
+</pre>
+
+          <p>
+            *VRButton.createButton()* fait deux choses importantes : il crée un bouton qui indique la compatibilité VR. De plus, il initialise une session VR si l'utilisateur active le bouton. La seule chose que vous avez à faire est d'ajouter la ligne de code suivante à votre application.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+document.body.appendChild( VRButton.createButton( renderer ) );
+</pre>
+
+          <p>
+            Ensuite, vous devez indiquer à votre instance de `WebGLRenderer` d'activer le rendu XR.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+renderer.xr.enabled = true;
+</pre>
+
+          <p>
+            Enfin, vous devez ajuster votre boucle d'animation car nous ne pouvons pas utiliser notre fonction bien connue *window.requestAnimationFrame()*. Pour les projets VR, nous utilisons `renderer.setAnimationLoop()`. Le code minimal ressemble à ceci :
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+renderer.setAnimationLoop( function () {
+
+  renderer.render( scene, camera );
+
+} );
+</pre>
+
+          <h2>Étapes suivantes</h2>
+
+          <p>
+            Jetez un œil à l'un des exemples officiels de WebVR pour voir ce flux de travail en action.<br /><br />
+
+            [example:webxr_xr_ballshooter WebXR / XR / tireur de balles]<br />
+            [example:webxr_xr_cubes WebXR / XR / cubes]<br />
+            [example:webxr_xr_dragging WebXR / XR / glisser-déposer]<br />
+            [example:webxr_xr_paint WebXR / XR / peinture]<br />
+            [example:webxr_xr_sculpt WebXR / XR / sculpture]<br />
+            [example:webxr_vr_panorama_depth WebXR / VR / panorama-profondeur]<br />
+            [example:webxr_vr_panorama WebXR / VR / panorama]<br />
+            [example:webxr_vr_rollercoaster WebXR / VR / montagnes russes]<br />
+            [example:webxr_vr_sandbox WebXR / VR / bac à sable]<br />
+            [example:webxr_vr_video WebXR / VR / vidéo]
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 169 - 0
manual/fr/how-to-dispose-of-objects.html

@@ -0,0 +1,169 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Comment se débarrasser des objets</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Comment se débarrasser des objets">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Comment se débarrasser des objets</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <p>
+            Un aspect important pour améliorer les performances et éviter les fuites de mémoire dans votre application est la libération des entités de la librairie inutilisées.
+            Chaque fois que vous créez une instance d'un type *three.js*, vous allouez une certaine quantité de mémoire. Cependant, *three.js* crée pour des objets spécifiques
+            comme les géométries ou les matériaux des entités liées à WebGL comme des tampons (buffers) ou des programmes de shaders qui sont nécessaires au rendu. Il est important de
+            souligner que ces objets ne sont pas libérés automatiquement. Au lieu de cela, l'application doit utiliser une API spéciale afin de libérer de telles ressources.
+            Ce guide donne un bref aperçu de la manière dont cette API est utilisée et des objets pertinents dans ce contexte.
+          </p>
+
+          <h2>Géométries</h2>
+
+          <p>
+            Une géométrie représente généralement les informations de vertex définies comme une collection d'attributs. *three.js* crée en interne un objet de type [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer WebGLBuffer]
+            pour chaque attribut. Ces entités ne sont supprimées que si vous appelez `BufferGeometry.dispose()`. Si une géométrie devient obsolète dans votre application,
+            exécutez la méthode pour libérer toutes les ressources associées.
+          </p>
+
+          <h2>Matériaux</h2>
+
+          <p>
+            Un matériau définit la manière dont les objets sont rendus. *three.js* utilise les informations d'une définition de matériau afin de construire un programme de shader pour le rendu.
+            Les programmes de shader ne peuvent être supprimés que si le matériau respectif est libéré. Pour des raisons de performance, *three.js* essaie de réutiliser les
+            programmes de shader existants si possible. Ainsi, un programme de shader n'est supprimé que si tous les matériaux associés sont libérés. Vous pouvez indiquer la libération d'un matériau en
+            exécutant `Material.dispose()`.
+          </p>
+
+          <h2>Textures</h2>
+
+          <p>
+            La libération d'un matériau n'a aucun effet sur les textures. Elles sont gérées séparément car une seule texture peut être utilisée par plusieurs matériaux en même temps.
+            Chaque fois que vous créez une instance de `Texture`, three.js crée en interne une instance de [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture].
+            Comme pour les tampons (buffers), cet objet ne peut être supprimé qu'en appelant `Texture.dispose()`.
+          </p>
+
+          <p>
+            Si vous utilisez un `ImageBitmap` comme source de données de la texture, vous devez appeler [link:https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap/close ImageBitmap.close]() au niveau de l'application pour libérer toutes les ressources côté CPU.
+            Un appel automatique de `ImageBitmap.close()` dans `Texture.dispose()` n'est pas possible, car l'image bitmap devient inutilisable, et le moteur n'a aucun moyen de savoir si l'image bitmap est utilisée ailleurs.
+          </p>
+
+          <h2>Cibles de rendu</h2>
+
+          <p>
+            Les objets de type `WebGLRenderTarget` allouent non seulement une instance de [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture] mais aussi
+            des [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer WebGLFramebuffer] et des [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer WebGLRenderbuffer]
+            pour réaliser des destinations de rendu personnalisées. Ces objets ne sont désalloués qu'en exécutant `WebGLRenderTarget.dispose()`.
+          </p>
+
+          <h2>Mesh skinné</h2>
+
+          <p>
+            Les meshes skinnés représentent leur hiérarchie d'os comme des squelettes. Si vous n'avez plus besoin d'un mesh skinné, envisagez d'appeler `Skeleton.dispose()` sur le squelette pour libérer les ressources internes.
+            Gardez à l'esprit que les squelettes peuvent être partagés entre plusieurs meshes skinnés, n'appelez donc `dispose()` que si le squelette n'est pas utilisé par d'autres meshes skinnés actifs.
+          </p>
+
+          <h2>Divers</h2>
+
+          <p>
+            Il existe d'autres classes dans le répertoire d'exemples, comme les contrôles ou les passes de post-traitement, qui fournissent des méthodes `dispose()` afin de supprimer les écouteurs d'événements internes
+            ou les cibles de rendu. En général, il est recommandé de vérifier l'API ou la documentation d'une classe et de rechercher `dispose()`. Si présent, vous devriez l'utiliser lors du nettoyage.
+          </p>
+
+          <h2>FAQ</h2>
+
+          <h3>Pourquoi *three.js* ne peut-il pas libérer les objets automatiquement ?</h3>
+
+          <p>
+            Cette question a été posée de nombreuses fois par la communauté, il est donc important de clarifier ce point. Le fait est que *three.js* ne connaît pas la durée de vie ou la portée
+            des entités créées par l'utilisateur, comme les géométries ou les matériaux. C'est la responsabilité de l'application. Par exemple, même si un matériau n'est actuellement pas utilisé pour le rendu,
+            il pourrait être nécessaire pour la prochaine image. Donc, si l'application décide qu'un certain objet peut être supprimé, elle doit en informer le moteur en appelant la méthode
+            `dispose()` respective.
+          </p>
+
+          <h3>La suppression d'un mesh de la scène libère-t-elle également sa géométrie et son matériau ?</h3>
+
+          <p>
+            Non, vous devez explicitement libérer la géométrie et le matériau via *dispose()*. Gardez à l'esprit que les géométries et les matériaux peuvent être partagés entre des objets 3D comme les meshes.
+          </p>
+
+          <h3>*three.js* fournit-il des informations sur la quantité d'objets mis en cache ?</h3>
+
+          <p>
+            Oui. Il est possible d'évaluer `renderer.info`, une propriété spéciale du renderer avec une série d'informations statistiques sur la mémoire de la carte graphique
+            et le processus de rendu. Entre autres choses, elle vous indique combien de textures, de géométries et de programmes de shader sont stockés en interne. Si vous remarquez des problèmes de performance
+            dans votre application, c'est une bonne idée de déboguer cette propriété afin d'identifier facilement une fuite de mémoire.
+          </p>
+
+          <h3>Que se passe-t-il lorsque vous appelez `dispose()` sur une texture mais que l'image n'est pas encore chargée ?</h3>
+
+          <p>
+            Les ressources internes d'une texture ne sont allouées que si l'image est entièrement chargée. Si vous libérez une texture avant que l'image ne soit chargée,
+            rien ne se passe. Aucune ressource n'a été allouée, il n'y a donc pas besoin de nettoyage.
+          </p>
+
+          <h3>Que se passe-t-il si j'appelle `dispose()` puis utilise l'objet respectif ultérieurement ?</h3>
+
+          <p>
+            Cela dépend. Pour les géométries, les matériaux, les textures, les cibles de rendu et les passes de post-traitement, les ressources internes supprimées peuvent être recréées par le moteur.
+            Aucune erreur d'exécution ne se produira donc, mais vous pourriez remarquer un impact négatif sur les performances pour l'image actuelle, surtout lorsque les programmes de shader doivent être compilés.
+
+            Les contrôles et les renderers sont une exception. Les instances de ces classes ne peuvent pas être utilisées après que `dispose()` a été appelée. Vous devez créer de nouvelles instances dans ce cas.
+          </p>
+
+          <h3>Comment gérer les objets *three.js* dans mon application ? Quand savoir comment libérer les choses ?</h3>
+
+          <p>
+            En général, il n'y a pas de recommandation définitive pour cela. Cela dépend fortement du cas d'utilisation spécifique pour savoir quand appeler `dispose()` est approprié. Il est important de souligner que
+            il n'est pas toujours nécessaire de libérer les objets en permanence. Un bon exemple est un jeu composé de plusieurs niveaux. Un bon moment pour la libération des objets est lors du changement de niveau.
+            L'application pourrait parcourir l'ancienne scène et libérer tous les matériaux, géométries et textures obsolètes. Comme mentionné dans la section précédente, cela ne produit pas
+            d'erreur d'exécution si vous libérez un objet qui est en fait toujours utilisé. Le pire qui puisse arriver est une chute de performance pour une seule image.
+          </p>
+
+          <h3>Pourquoi `renderer.info.memory` rapporte toujours des géométries et des textures après avoir parcouru la scène et libéré toutes les textures et géométries accessibles ?</h3>
+
+          <p>
+            Dans certains cas, certaines textures et géométries utilisées en interne par Three.js
+            ne sont pas accessibles lors de la traversée du graphe de scène afin d'être libérées.
+            Il est prévu que `renderer.info.memory` les signale toujours même après un nettoyage complet de la scène.
+            Cependant, elles ne fuient pas, mais sont réutilisées lors des cycles consécutifs de nettoyage/repopulation de la scène.
+
+            Ces cas peuvent être liés à l'utilisation de `material.envMap`, `scene.background`, `scene.environment`,
+            ou d'autres contextes qui nécessitent que le moteur crée des textures ou des géométries pour un usage interne.
+          </p>
+
+          <h2>Exemples illustrant l'utilisation de dispose()</h2>
+
+          <p>
+            [example:webgl_test_memory WebGL / test / mémoire]<br />
+            [example:webgl_test_memory2 WebGL / test / mémoire2]<br />
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 275 - 0
manual/fr/how-to-update-things.html

@@ -0,0 +1,275 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Comment mettre à jour les éléments</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Comment mettre à jour les éléments">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Comment mettre à jour les éléments</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <div>
+            <p>Par défaut, tous les objets mettent automatiquement à jour leurs matrices s'ils ont été ajoutés à la scène avec</p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const object = new THREE.Object3D();
+scene.add( object );
+</pre>
+            ou s'ils sont l'enfant d'un autre objet qui a été ajouté à la scène :
+<pre class="prettyprint notranslate lang-js" translate="no">
+const object1 = new THREE.Object3D();
+const object2 = new THREE.Object3D();
+
+object1.add( object2 );
+scene.add( object1 ); //object1 et object2 mettront automatiquement à jour leurs matrices
+</pre>
+          </div>
+
+          <p>Cependant, si vous savez que l'objet sera statique, vous pouvez désactiver cela et mettre à jour la matrice de transformation manuellement uniquement lorsque nécessaire.</p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+object.matrixAutoUpdate = false;
+object.updateMatrix();
+</pre>
+
+          <h2>BufferGeometry</h2>
+          <div>
+            <p>
+              Les BufferGeometries stockent des informations (telles que les positions des sommets, les indices des faces, les normales, les couleurs,
+              les UV et tout attribut personnalisé) dans des tampons d'attributs - c'est-à-dire des
+              [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays tableaux typés].
+              Cela les rend généralement plus rapides que les Geometries standard, au prix d'être un peu plus difficiles à
+              utiliser.
+            </p>
+            <p>
+              En ce qui concerne la mise à jour des BufferGeometries, la chose la plus importante à comprendre est que
+              vous ne pouvez pas redimensionner les tampons (c'est très coûteux, c'est fondamentalement l'équivalent de la création d'une nouvelle géométrie).
+              Vous pouvez cependant mettre à jour le contenu des tampons.
+            </p>
+            <p>
+              Cela signifie que si vous savez qu'un attribut de votre BufferGeometry va croître, par exemple le nombre de sommets,
+              vous devez pré-allouer un tampon suffisamment grand pour contenir tous les nouveaux sommets qui pourraient être créés. Bien sûr,
+              cela signifie également qu'il y aura une taille maximale pour votre BufferGeometry - il n'y a
+              aucun moyen de créer une BufferGeometry qui puisse être étendue efficacement indéfiniment.
+            </p>
+            <p>
+              Nous utiliserons l'exemple d'une ligne qui s'étend au moment du rendu. Nous allouerons de l'espace
+              dans le tampon pour 500 sommets, mais n'en dessinerons que deux au début, en utilisant `BufferGeometry.drawRange`.
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const MAX_POINTS = 500;
+
+// geometry
+const geometry = new THREE.BufferGeometry();
+
+// attributes
+const positions = new Float32Array( MAX_POINTS * 3 ); // 3 floats (x, y et z) par point
+geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+
+// draw range
+const drawCount = 2; // dessine seulement les 2 premiers points, seulement
+geometry.setDrawRange( 0, drawCount );
+
+// material
+const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
+
+// line
+const line = new THREE.Line( geometry, material );
+scene.add( line );
+</pre>
+             <p>
+              Ensuite, nous ajouterons aléatoirement des points à la ligne en utilisant un modèle comme :
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const positionAttribute = line.geometry.getAttribute( 'position' );
+
+let x = 0, y = 0, z = 0;
+
+for ( let i = 0; i < positionAttribute.count; i ++ ) {
+
+    positionAttribute.setXYZ( i, x, y, z );
+
+    x += ( Math.random() - 0.5 ) * 30;
+    y += ( Math.random() - 0.5 ) * 30;
+    z += ( Math.random() - 0.5 ) * 30;
+
+}
+</pre>
+            <p>
+              Si vous souhaitez modifier le <em>nombre de points</em> rendus après le premier rendu, faites ceci :
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+line.geometry.setDrawRange( 0, newValue );
+</pre>
+            <p>
+              Si vous souhaitez modifier les valeurs des données de position après le premier rendu, vous devez
+              définir le drapeau needsUpdate comme suit :
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+positionAttribute.needsUpdate = true; // requis après le premier rendu
+</pre>
+
+            <p>
+              Si vous modifiez les valeurs des données de position après le rendu initial, vous pourriez avoir besoin de recalculer
+              les volumes englobants afin que d'autres fonctionnalités du moteur comme le culling par frustum de vue ou les assistants fonctionnent correctement.
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+line.geometry.computeBoundingBox();
+line.geometry.computeBoundingSphere();
+</pre>
+
+            <p>
+              [link:https://jsfiddle.net/t4m85pLr/1/ Voici un fiddle] montrant une ligne animée que vous pouvez adapter à votre cas d'utilisation.
+            </p>
+
+            <h3>Exemples</h3>
+
+            <p>
+              [example:webgl_custom_attributes WebGL / personnalisé / attributs]<br />
+              [example:webgl_buffergeometry_custom_attributes_particles WebGL / buffergeometry / personnalisé / attributs / particules]
+            </p>
+
+          </div>
+
+          <h2>Matériaux</h2>
+          <div>
+            <p>Toutes les valeurs des uniforms peuvent être modifiées librement (par exemple couleurs, textures, opacité, etc.), les valeurs sont envoyées au shader à chaque image.</p>
+
+            <p>De plus, les paramètres liés à l'état GL peuvent changer à tout moment (depthTest, blending, polygonOffset, etc.).</p>
+
+            <p>Les propriétés suivantes ne peuvent pas être facilement modifiées à l'exécution (une fois que le matériau a été rendu au moins une fois) :</p>
+            <ul>
+              <li>nombre et types des uniforms</li>
+              <li>présence ou non de
+                <ul>
+                  <li>texture</li>
+                  <li>brouillard</li>
+                  <li>couleurs de sommet</li>
+                  <li>morphing</li>
+                  <li>shadow map</li>
+                  <li>test alpha</li>
+                  <li>transparent</li>
+                </ul>
+              </li>
+            </ul>
+
+            <p>Les modifications de ces éléments nécessitent la construction d'un nouveau programme de shader. Vous devrez définir</p>
+            <code>material.needsUpdate = true</code>
+
+            <p>Gardez à l'esprit que cela peut être assez lent et provoquer des à-coups dans la cadence d'images (surtout sous Windows, car la compilation des shaders est plus lente en DirectX qu'en OpenGL).</p>
+
+            <p>Pour une expérience plus fluide, vous pouvez émuler dans une certaine mesure les modifications de ces fonctionnalités en utilisant des valeurs "factices" comme des lumières d'intensité nulle, des textures blanches ou un brouillard de densité nulle.</p>
+
+            <p>Vous pouvez modifier librement le matériau utilisé pour les morceaux de géométrie, cependant, vous ne pouvez pas modifier la façon dont un objet est divisé en morceaux (selon les matériaux des faces). </p>
+
+            <h3>Si vous avez besoin d'avoir différentes configurations de matériaux pendant l'exécution :</h3>
+            <p>Si le nombre de matériaux / morceaux est faible, vous pouvez pré-diviser l'objet à l'avance (par exemple cheveux / visage / corps / vêtements du haut / pantalon pour un humain, avant / côtés / haut / verre / pneu / intérieur pour une voiture). </p>
+
+            <p>Si le nombre est élevé (par exemple, chaque face pourrait être potentiellement différente), envisagez une solution différente, telle que l'utilisation d'attributs / textures pour piloter un aspect différent par face.</p>
+
+            <h3>Exemples</h3>
+            <p>
+              [example:webgl_materials_car WebGL / matériaux / voiture]<br />
+              [example:webgl_postprocessing_dof WebGL / webgl_postprocessing / dof]
+            </p>
+          </div>
+
+
+          <h2>Textures</h2>
+          <div>
+            <p>Les textures d'image, de canevas, de vidéo et de données doivent avoir le drapeau suivant défini si elles sont modifiées :</p>
+            <code>
+              texture.needsUpdate = true;
+            </code>
+            <p>Les cibles de rendu se mettent à jour automatiquement.</p>
+
+            <h3>Exemples</h3>
+            <p>
+              [example:webgl_materials_video WebGL / matériaux / vidéo]<br />
+              [example:webgl_rtt WebGL / rtt]
+            </p>
+
+          </div>
+
+          <h2>Caméras</h2>
+          <div>
+            <p>La position et la cible d'une caméra sont mises à jour automatiquement. Si vous avez besoin de changer</p>
+            <ul>
+              <li>
+                fov
+              </li>
+              <li>
+                aspect
+              </li>
+              <li>
+                near
+              </li>
+              <li>
+                far
+              </li>
+            </ul>
+            <p>
+              alors vous devrez recalculer la matrice de projection :
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+camera.aspect = window.innerWidth / window.innerHeight;
+camera.updateProjectionMatrix();
+</pre>
+          </div>
+
+          <h2>InstancedMesh</h2>
+          <div>
+            <p>
+              <code>InstancedMesh</code> est une classe permettant d'accéder facilement au rendu instancié dans <code>three.js</code>. Certaines fonctionnalités de la bibliothèque comme le culling par frustum de vue ou
+              le ray casting dépendent de volumes englobants à jour (sphère englobante et boîte englobante). En raison de la façon dont <code>InstancedMesh</code> fonctionne, la classe
+              possède ses propres propriétés <code>boundingBox</code> et <code>boundingSphere</code> qui remplacent les volumes englobants au niveau de la géométrie.
+            </p>
+            <p>
+              Similaire aux géométries, vous devez recalculer la boîte englobante et la sphère chaque fois que vous modifiez les données sous-jacentes. Dans le contexte de <code>InstancedMesh</code>, cela
+              se produit lorsque vous transformez des instances via <code>setMatrixAt()</code>. Vous pouvez utiliser le même modèle qu'avec les géométries.
+            </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+instancedMesh.computeBoundingBox();
+instancedMesh.computeBoundingSphere();
+</pre>
+
+          </div>
+
+          <h2>SkinnedMesh</h2>
+          <div>
+            <p>
+              <code>SkinnedMesh</code> suit les mêmes principes que <code>InstancedMesh</code> en ce qui concerne les volumes englobants. Cela signifie que la classe a sa propre version de
+              <code>boundingBox</code> et <code>boundingSphere</code> pour enfermer correctement les maillages animés.
+              Lors de l'appel de <code>computeBoundingBox()</code> et <code>computeBoundingSphere()</code>, la classe calcule les volumes englobants respectifs en fonction de la transformation actuelle des os (ou en d'autres termes, de l'état d'animation actuel).
+            </p>
+          </div>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 142 - 0
manual/fr/how-to-use-post-processing.html

@@ -0,0 +1,142 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Comment utiliser le post-traitement</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Comment utiliser le post-traitement">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Comment utiliser le post-traitement</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <p>
+            De nombreuses applications three.js rendent leurs objets 3D directement à l'écran. Parfois, cependant, vous souhaitez appliquer un ou plusieurs effets graphiques
+            tels que la profondeur de champ, le Bloom, le grain de film ou divers types d'anti-aliasing. Le post-traitement est une approche largement utilisée
+            pour implémenter de tels effets. D'abord, la scène est rendue sur une cible de rendu qui représente un tampon dans la mémoire de la carte graphique.
+            À l'étape suivante, une ou plusieurs passes de post-traitement appliquent des filtres et des effets au tampon d'image avant qu'il ne soit finalement rendu à
+            l'écran.
+          </p>
+          <p>
+            three.js fournit une solution complète de post-traitement via `EffectComposer` pour implémenter un tel flux de travail.
+          </p>
+
+          <h2>Flux de travail</h2>
+
+          <p>
+            La première étape du processus consiste à importer tous les fichiers nécessaires depuis le répertoire d'exemples. Ce guide suppose que vous utilisez le
+            [link:https://www.npmjs.com/package/three package npm] officiel de three.js. Pour notre démo de base dans ce guide, nous avons besoin des fichiers suivants.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
+import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
+import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';
+import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
+</pre>
+
+          <p>
+            Après l'importation réussie de tous les fichiers, nous pouvons créer notre compositeur en lui passant une instance de `WebGLRenderer`.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+const composer = new EffectComposer( renderer );
+</pre>
+
+          <p>
+            Lorsque vous utilisez un compositeur, il est nécessaire de modifier la boucle d'animation de l'application. Au lieu d'appeler la méthode de rendu de
+            `WebGLRenderer`, nous utilisons maintenant la contrepartie respective de `EffectComposer`.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+function animate() {
+
+  requestAnimationFrame( animate );
+
+  composer.render();
+
+}
+</pre>
+
+          <p>
+            Notre compositeur est maintenant prêt, il est donc possible de configurer la chaîne de passes de post-traitement. Ces passes sont responsables de la création
+            du rendu visuel final de l'application. Elles sont traitées dans l'ordre de leur ajout/insertion. Dans notre exemple, l'instance de `RenderPass`
+            est exécutée en premier, puis l'instance de `GlitchPass` et enfin `OutputPass`. La dernière passe activée dans la chaîne est automatiquement rendue à l'écran.
+            La configuration des passes ressemble à ceci :
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+const renderPass = new RenderPass( scene, camera );
+composer.addPass( renderPass );
+
+const glitchPass = new GlitchPass();
+composer.addPass( glitchPass );
+
+const outputPass = new OutputPass();
+composer.addPass( outputPass );
+</pre>
+
+          <p>
+            `RenderPass` est normalement placée au début de la chaîne afin de fournir la scène rendue comme entrée pour l'étape de post-traitement suivante. Dans notre cas,
+            `GlitchPass` utilisera ces données d'image pour appliquer un effet de glitch sauvage. `OutputPass` est généralement la dernière passe de la chaîne qui effectue la conversion de l'espace colorimétrique sRGB et le mappage tonal.
+            Découvrez cet [link:https://threejs.org/examples/webgl_postprocessing_glitch exemple live] pour le voir en action.
+          </p>
+
+          <h2>Passes intégrées</h2>
+
+          <p>
+            Vous pouvez utiliser une large gamme de passes de post-traitement prédéfinies fournies par le moteur. Elles se trouvent dans le
+            répertoire [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/postprocessing postprocessing].
+          </p>
+
+          <h2>Passes personnalisées</h2>
+
+          <p>
+            Parfois, vous souhaitez écrire un shader de post-traitement personnalisé et l'inclure dans la chaîne de passes de post-traitement. Pour ce scénario,
+            vous pouvez utiliser `ShaderPass`. Après avoir importé le fichier et votre shader personnalisé, vous pouvez utiliser le code suivant pour configurer la passe.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
+import { LuminosityShader } from 'three/addons/shaders/LuminosityShader.js';
+
+// plus tard dans votre routine d'initialisation
+
+const luminosityPass = new ShaderPass( LuminosityShader );
+composer.addPass( luminosityPass );
+</pre>
+
+          <p>
+            Le dépôt fournit un fichier appelé [link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/shaders/CopyShader.js CopyShader] qui constitue un
+            bon point de départ pour votre propre shader personnalisé. `CopyShader` copie simplement le contenu de l'image du tampon de lecture (`read buffer`) de l'`EffectComposer`
+            vers son tampon d'écriture (`write buffer`) sans appliquer aucun effet.
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 594 - 5
manual/fr/indexed-textures.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Indexed Textures for Picking and Color</title>
+    <title>Textures Indexées pour la Sélection et la Couleur</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Indexed Textures for Picking and Color">
+    <meta name="twitter:title" content="Three.js – Textures Indexées pour la Sélection et la Couleur">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,601 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Indexed Textures for Picking and Color</h1>
+        <h1>Textures Indexées pour la Sélection et la Couleur</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/indexed-textures.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article est une continuation de <a href="align-html-elements-to-3d.html">un article sur l'alignement des éléments HTML en 3D</a>.
+Si vous ne l'avez pas encore lu, vous devriez commencer par là avant de continuer ici.</p>
+<p>Parfois, l'utilisation de three.js nécessite de trouver des solutions créatives.
+Je ne suis pas sûr que ce soit une excellente solution, mais j'ai pensé la partager et
+vous pouvez voir si elle suggère des solutions pour vos besoins.</p>
+<p>Dans <a href="align-html-elements-to-3d.html">l'article précédent</a>, nous
+avons affiché les noms de pays autour d'un globe 3D. Comment pourrions-nous permettre à
+l'utilisateur de sélectionner un pays et d'afficher sa sélection ?</p>
+<p>La première idée qui vient à l'esprit est de générer la géométrie pour chaque pays.
+Nous pourrions <a href="picking.html">utiliser une solution de picking</a> comme nous l'avons vu précédemment.
+Nous construirions une géométrie 3D pour chaque pays. Si l'utilisateur clique sur le maillage de
+ce pays, nous saurions quel pays a été cliqué.</p>
+<p>Donc, juste pour vérifier cette solution, j'ai essayé de générer des maillages 3D de tous les pays
+en utilisant les mêmes données que celles que j'ai utilisées pour générer les contours
+<a href="align-html-elements-to-3d.html">dans l'article précédent</a>.
+Le résultat était un fichier GLTF (.glb) binaire de 15,5 Mo. Faire télécharger 15,5 Mo
+à l'utilisateur me semble excessif.</p>
+<p>Il existe de nombreuses façons de compresser les données. La première serait probablement
+d'appliquer un algorithme pour réduire la résolution des contours. Je n'ai pas passé
+de temps à explorer cette solution. Pour les frontières des États-Unis, c'est probablement un
+gain énorme. Pour les frontières du Canada, probablement beaucoup moins.</p>
+<p>Une autre solution serait d'utiliser simplement la compression de données réelle. Par exemple, la compression Gzip
+du fichier l'a réduit à 11 Mo. C'est 30% de moins, mais probablement pas suffisant.</p>
+<p>Nous pourrions stocker toutes les données sous forme de valeurs de plage sur 16 bits au lieu de valeurs flottantes sur 32 bits.
+Ou nous pourrions utiliser quelque chose comme <a href="https://google.github.io/draco/">la compression Draco</a>
+et peut-être que cela suffirait. Je n'ai pas vérifié et je vous encourage à vérifier
+par vous-même et à me dire comment ça se passe, car j'aimerais le savoir. 😅</p>
+<p>Dans mon cas, j'ai pensé à <a href="picking.html">la solution de picking GPU</a>
+que nous avons abordée à la fin de <a href="picking.html">l'article sur le picking</a>. Dans
+cette solution, nous avons dessiné chaque maillage avec une couleur unique qui représentait
+l'ID de ce maillage. Nous avons ensuite dessiné tous les maillages et regardé la couleur
+sur laquelle on a cliqué.</p>
+<p>En nous inspirant de cela, nous pourrions pré-générer une carte des pays où
+la couleur de chaque pays est son numéro d'index dans notre tableau de pays. Nous pourrions
+alors utiliser une technique de picking GPU similaire. Nous dessinerions le globe hors écran en utilisant
+cette texture d'index. Regarder la couleur du pixel sur lequel l'utilisateur clique
+nous donnerait l'ID du pays.</p>
+<p>Donc, j'<a href="https://github.com/mrdoob/three.js/blob/master/manual/resources/tools/geo-picking/">ai écrit du code</a>
+pour générer une telle texture. La voici.</p>
+<div class="threejs_center"><img src="../examples/resources/data/world/country-index-texture.png" style="width: 700px;"></div>
+
+<p>Note : Les données utilisées pour générer cette texture proviennent de <a href="http://thematicmapping.org/downloads/world_borders.php">ce site web</a>
+et sont donc sous licence <a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>.</p>
+<p>Elle ne fait que 217 Ko, bien mieux que les 14 Mo pour les maillages de pays. En fait, nous pourrions probablement
+même réduire la résolution, mais 217 Ko semble suffisant pour l'instant.</p>
+<p>Alors essayons de l'utiliser pour sélectionner des pays.</p>
+<p>En prenant du code de <a href="picking.html">l'exemple de picking GPU</a>, nous avons besoin
+d'une scène pour le picking.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickingScene = new THREE.Scene();
+pickingScene.background = new THREE.Color(0);
+</pre>
+<p>et nous devons ajouter le globe avec notre texture d'index à la
+scène de picking.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const loader = new THREE.TextureLoader();
+  const geometry = new THREE.SphereGeometry(1, 64, 32);
+
++  const indexTexture = loader.load('resources/data/world/country-index-texture.png', render);
++  indexTexture.minFilter = THREE.NearestFilter;
++  indexTexture.magFilter = THREE.NearestFilter;
++
++  const pickingMaterial = new THREE.MeshBasicMaterial({map: indexTexture});
++  pickingScene.add(new THREE.Mesh(geometry, pickingMaterial));
+
+  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);
+  const material = new THREE.MeshBasicMaterial({map: texture});
+  scene.add(new THREE.Mesh(geometry, material));
+}
+</pre>
+<p>Ensuite, copions la classe <code class="notranslate" translate="no">GPUPickingHelper</code> que nous avons
+utilisée précédemment avec quelques modifications mineures.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class GPUPickHelper {
+  constructor() {
+    // créer une cible de rendu de 1x1 pixel
+    this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);
+    this.pixelBuffer = new Uint8Array(4);
+-    this.pickedObject = null;
+-    this.pickedObjectSavedColor = 0;
+  }
+  pick(cssPosition, scene, camera) {
+    const {pickingTexture, pixelBuffer} = this;
+
+    // définir le décalage de la vue pour représenter juste un seul pixel sous la souris
+    const pixelRatio = renderer.getPixelRatio();
+    camera.setViewOffset(
+        renderer.getContext().drawingBufferWidth,   // largeur totale
+        renderer.getContext().drawingBufferHeight,  // haut total
+        cssPosition.x * pixelRatio | 0,             // coordonnée x du rectangle
+        cssPosition.y * pixelRatio | 0,             // coordonnée y du rectangle
+        1,                                          // largeur du rectangle
+        1,                                          // hauteur du rectangle
+    );
+    // effectuer le rendu de la scène
+    renderer.setRenderTarget(pickingTexture);
+    renderer.render(scene, camera);
+    renderer.setRenderTarget(null);
+    // effacer le décalage de la vue pour que le rendu revienne à la normale
+    camera.clearViewOffset();
+    // lire le pixel
+    renderer.readRenderTargetPixels(
+        pickingTexture,
+        0,   // x
+        0,   // y
+        1,   // width
+        1,   // height
+        pixelBuffer);
+
++    const id =
++        (pixelBuffer[0] &lt;&lt; 16) |
++        (pixelBuffer[1] &lt;&lt;  8) |
++        (pixelBuffer[2] &lt;&lt;  0);
++
++    return id;
+-    const id =
+-        (pixelBuffer[0] &lt;&lt; 16) |
+-        (pixelBuffer[1] &lt;&lt;  8) |
+-        (pixelBuffer[2]      );
+-    const intersectedObject = idToObject[id];
+-    if (intersectedObject) {
+-      // pick the first object. It's the closest one
+-      this.pickedObject = intersectedObject;
+-      // save its color
+-      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+-      // set its emissive color to flashing red/yellow
+-      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+-    }
+  }
+}
+</pre>
+<p>Maintenant, nous pouvons l'utiliser pour sélectionner des pays.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickHelper = new GPUPickHelper();
+
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function pickCountry(event) {
+  // sortir si les données ne sont pas encore chargées
+  if (!countryInfos) {
+    return;
+  }
+
+  const position = getCanvasRelativePosition(event);
+  const id = pickHelper.pick(position, pickingScene, camera);
+  if (id &gt; 0) {
+    // nous avons cliqué sur un pays. Basculer sa propriété 'selected'
+    const countryInfo = countryInfos[id - 1];
+    const selected = !countryInfo.selected;
+    // si nous sélectionnons ce pays et que les touches modificatrices ne sont pas
+    // enfoncées, désélectionner tout le reste.
+    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {
+      unselectAllCountries();
+    }
+    numCountriesSelected += selected ? 1 : -1;
+    countryInfo.selected = selected;
+  } else if (numCountriesSelected) {
+    // l'océan ou le ciel a été cliqué
+    unselectAllCountries();
+  }
+  requestRenderIfNotRequested();
+}
+
+function unselectAllCountries() {
+  numCountriesSelected = 0;
+  countryInfos.forEach((countryInfo) =&gt; {
+    countryInfo.selected = false;
+  });
+}
+
+canvas.addEventListener('pointerup', pickCountry);
+</pre>
+<p>Le code ci-dessus définit/annule la propriété <code class="notranslate" translate="no">selected</code> sur
+le tableau de pays. Si <code class="notranslate" translate="no">shift</code> ou <code class="notranslate" translate="no">ctrl</code> ou <code class="notranslate" translate="no">cmd</code>
+est enfoncé, vous pouvez sélectionner plus d'un pays.</p>
+<p>Il ne reste plus qu'à afficher les pays sélectionnés. Pour l'instant,
+mettons simplement à jour les labels.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateLabels() {
+  // sortir si les données ne sont pas encore chargées
+  if (!countryInfos) {
+    return;
+  }
+
+  const large = settings.minArea * settings.minArea;
+  // obtenir une matrice qui représente une orientation relative de la caméra
+  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);
+  // obtenir la position de la caméra
+  camera.getWorldPosition(cameraPosition);
+  for (const countryInfo of countryInfos) {
+-    const {position, elem, area} = countryInfo;
+-    // large enough?
+-    if (area &lt; large) {
++    const {position, elem, area, selected} = countryInfo;
++    const largeEnough = area &gt;= large;
++    const show = selected || (numCountriesSelected === 0 &amp;&amp; largeEnough);
++    if (!show) {
+      elem.style.display = 'none';
+      continue;
+    }
+
+    ...
+</pre>
+<p>et avec cela, nous devrions pouvoir sélectionner des pays</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/indexed-textures-picking.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/indexed-textures-picking.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Le code affiche toujours les pays en fonction de leur superficie, mais si vous
+en cliquez sur un, seul celui-ci aura un label.</p>
+<p>Cela semble donc une solution raisonnable pour sélectionner des pays,
+mais qu'en est-il de la mise en évidence des pays sélectionnés ?</p>
+<p>Pour cela, nous pouvons nous inspirer des <em>graphiques palettisés</em>.</p>
+<p><a href="https://en.wikipedia.org/wiki/Palette_%28computing%29">Les graphiques palettisés</a>
+ou <a href="https://en.wikipedia.org/wiki/Indexed_color">couleurs indexées</a> sont
+ce qu'utilisaient les anciens systèmes comme l'Atari 800, l'Amiga, la NES,
+la Super Nintendo et même les anciens PC IBM. Au lieu de stocker des images bitmap
+en couleurs RGBA (8 bits par couleur, 32 octets par pixel ou plus), ils stockaient
+des images bitmap en valeurs de 8 bits ou moins. La valeur de chaque pixel était un index
+dans une palette. Par exemple, une valeur
+de 3 dans l'image signifie "afficher la couleur 3". La couleur que représente la couleur n°3 est
+définie ailleurs dans ce qu'on appelle une "palette".</p>
+<p>En JavaScript, vous pouvez l'imaginer comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const face7x7PixelImageData = [
+  0, 1, 1, 1, 1, 1, 0,
+  1, 0, 0, 0, 0, 0, 1,
+  1, 0, 2, 0, 2, 0, 1,
+  1, 0, 0, 0, 0, 0, 1,
+  1, 0, 3, 3, 3, 0, 1,
+  1, 0, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 1, 1,
+];
+
+const palette = [
+  [255, 255, 255],  // white
+  [  0,   0,   0],  // black
+  [  0, 255, 255],  // cyan
+  [255,   0,   0],  // red
+];
+</pre>
+<p>Où chaque pixel dans les données de l'image est un index dans la palette. Si vous interprétiez
+les données de l'image à travers la palette ci-dessus, vous obtiendriez cette image</p>
+<div class="threejs_center"><img src="../resources/images/7x7-indexed-face.png"></div>
+
+<p>Dans notre cas, nous avons déjà une texture ci-dessus qui a un ID différent
+par pays. Ainsi, nous pourrions utiliser cette même texture à travers une texture de palette
+pour donner à chaque pays sa propre couleur. En modifiant la texture de palette,
+nous pouvons colorer chaque pays individuellement. Par exemple, en mettant
+toute la texture de palette en noir, puis en attribuant une couleur différente à l'entrée
+d'un pays dans la palette, nous pouvons mettre en évidence uniquement ce pays.</p>
+<p>Pour réaliser des graphiques à index palettisés, il faut du code shader personnalisé.
+Modifions les shaders par défaut dans three.js.
+De cette façon, nous pourrons utiliser l'éclairage et d'autres fonctionnalités si nous le souhaitons.</p>
+<p>Comme nous l'avons vu dans <a href="optimize-lots-of-objects-animated.html">l'article sur l'animation de nombreux objets</a>,
+nous pouvons modifier les shaders par défaut en ajoutant une fonction à la propriété
+<code class="notranslate" translate="no">onBeforeCompile</code> d'un matériau.</p>
+<p>Le shader de fragment par défaut ressemble à ceci avant la compilation.</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">#include &lt;common&gt;
+#include &lt;color_pars_fragment&gt;
+#include &lt;uv_pars_fragment&gt;
+#include &lt;map_pars_fragment&gt;
+#include &lt;alphamap_pars_fragment&gt;
+#include &lt;aomap_pars_fragment&gt;
+#include &lt;lightmap_pars_fragment&gt;
+#include &lt;envmap_pars_fragment&gt;
+#include &lt;fog_pars_fragment&gt;
+#include &lt;specularmap_pars_fragment&gt;
+#include &lt;logdepthbuf_pars_fragment&gt;
+#include &lt;clipping_planes_pars_fragment&gt;
+void main() {
+    #include &lt;clipping_planes_fragment&gt;
+    vec4 diffuseColor = vec4( diffuse, opacity );
+    #include &lt;logdepthbuf_fragment&gt;
+    #include &lt;map_fragment&gt;
+    #include &lt;color_fragment&gt;
+    #include &lt;alphamap_fragment&gt;
+    #include &lt;alphatest_fragment&gt;
+    #include &lt;specularmap_fragment&gt;
+    ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
+    #ifdef USE_LIGHTMAP
+        reflectedLight.indirectDiffuse += texture2D( lightMap, vLightMapUv ).xyz * lightMapIntensity;
+    #else
+        reflectedLight.indirectDiffuse += vec3( 1.0 );
+    #endif
+    #include &lt;aomap_fragment&gt;
+    reflectedLight.indirectDiffuse *= diffuseColor.rgb;
+    vec3 outgoingLight = reflectedLight.indirectDiffuse;
+    #include &lt;envmap_fragment&gt;
+    gl_FragColor = vec4( outgoingLight, diffuseColor.a );
+    #include &lt;premultiplied_alpha_fragment&gt;
+    #include &lt;tonemapping_fragment&gt;
+    #include &lt;colorspace_fragment&gt;
+    #include &lt;fog_fragment&gt;
+}
+</pre>
+<p><a href="https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk">En fouillant dans tous ces extraits</a>,
+nous constatons que three.js utilise une variable appelée <code class="notranslate" translate="no">diffuseColor</code> pour gérer la
+couleur de base du matériau. Il la définit dans l'<a href="https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js">extrait</a> <code class="notranslate" translate="no">&lt;color_fragment&gt;</code>,
+nous devrions donc pouvoir la modifier après ce point.</p>
+<p><code class="notranslate" translate="no">diffuseColor</code> à ce stade du shader devrait déjà être la couleur de
+notre texture de contour, nous pouvons donc chercher la couleur dans une texture de palette
+et les mélanger pour le résultat final.</p>
+<p>Comme nous l'<a href="optimize-lots-of-objects-animated.html">avons fait précédemment</a>, nous allons créer un tableau
+de chaînes de recherche et de remplacement et les appliquer au shader dans
+<a href="/docs/#api/en/materials/Material.onBeforeCompile"><code class="notranslate" translate="no">Material.onBeforeCompile</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const loader = new THREE.TextureLoader();
+  const geometry = new THREE.SphereGeometry(1, 64, 32);
+
+  const indexTexture = loader.load('resources/data/world/country-index-texture.png', render);
+  indexTexture.minFilter = THREE.NearestFilter;
+  indexTexture.magFilter = THREE.NearestFilter;
+
+  const pickingMaterial = new THREE.MeshBasicMaterial({map: indexTexture});
+  pickingScene.add(new THREE.Mesh(geometry, pickingMaterial));
+
++  const fragmentShaderReplacements = [
++    {
++      from: '#include &lt;common&gt;',
++      to: `
++        #include &lt;common&gt;
++        uniform sampler2D indexTexture;
++        uniform sampler2D paletteTexture;
++        uniform float paletteTextureWidth;
++      `,
++    },
++    {
++      from: '#include &lt;color_fragment&gt;',
++      to: `
++        #include &lt;color_fragment&gt;
++        {
++          vec4 indexColor = texture2D(indexTexture, vUv);
++          float index = indexColor.r * 255.0 + indexColor.g * 255.0 * 256.0;
++          vec2 paletteUV = vec2((index + 0.5) / paletteTextureWidth, 0.5);
++          vec4 paletteColor = texture2D(paletteTexture, paletteUV);
++          // diffuseColor.rgb += paletteColor.rgb;   // white outlines
++          diffuseColor.rgb = paletteColor.rgb - diffuseColor.rgb;  // black outlines
++        }
++      `,
++    },
++  ];
+
+  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);
+  const material = new THREE.MeshBasicMaterial({map: texture});
++  material.onBeforeCompile = function(shader) {
++    fragmentShaderReplacements.forEach((rep) =&gt; {
++      shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
++    });
++  };
+  scene.add(new THREE.Mesh(geometry, material));
+}
+</pre>
+<p>Comme vous pouvez le voir ci-dessus, nous ajoutons 3 uniformes, <code class="notranslate" translate="no">indexTexture</code>, <code class="notranslate" translate="no">paletteTexture</code>,
+et <code class="notranslate" translate="no">paletteTextureWidth</code>. Nous obtenons une couleur à partir de <code class="notranslate" translate="no">indexTexture</code>
+et la convertissons en index. <code class="notranslate" translate="no">vUv</code> sont les coordonnées de texture fournies par
+three.js. Nous utilisons ensuite cet index pour obtenir une couleur à partir de la texture de palette.
+Nous mélangeons ensuite le résultat avec la <code class="notranslate" translate="no">diffuseColor</code> actuelle. La <code class="notranslate" translate="no">diffuseColor</code>
+à ce stade est notre texture de contour noir et blanc, donc si nous ajoutons les 2 couleurs,
+nous obtiendrons des contours blancs. Si nous soustrayons la couleur diffuse actuelle, nous obtiendrons
+des contours noirs.</p>
+<p>Avant de pouvoir effectuer le rendu, nous devons configurer la texture de palette
+et ces 3 uniformes.</p>
+<p>Pour la texture de palette, elle doit juste être suffisamment large pour
+contenir une couleur par pays + une pour l'océan (id = 0).
+Il y a 240 et quelques pays. Nous pourrions attendre que la
+liste des pays se charge pour obtenir un nombre exact ou le chercher.
+Il n'y a pas beaucoup de mal à choisir un nombre plus grand,
+donc choisissons 512.</p>
+<p>Voici le code pour créer la texture de palette</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const maxNumCountries = 512;
+const paletteTextureWidth = maxNumCountries;
+const paletteTextureHeight = 1;
+const palette = new Uint8Array(paletteTextureWidth * 4);
+const paletteTexture = new THREE.DataTexture(
+    palette, paletteTextureWidth, paletteTextureHeight);
+paletteTexture.minFilter = THREE.NearestFilter;
+paletteTexture.magFilter = THREE.NearestFilter;
+</pre>
+<p>Une <a href="/docs/#api/en/textures/DataTexture"><code class="notranslate" translate="no">DataTexture</code></a> nous permet de donner des données brutes à une texture. Dans ce cas,
+nous lui donnons 512 couleurs RGBA, 4 octets chacune où chaque octet représente
+respectivement le rouge, le vert et le bleu en utilisant des valeurs allant de 0 à 255.</p>
+<p>Remplissons-la avec des couleurs aléatoires juste pour voir si ça fonctionne</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (let i = 1; i &lt; palette.length; ++i) {
+  palette[i] = Math.random() * 256;
+}
+// définir la couleur de l'océan (index #0)
+palette.set([100, 200, 255, 255], 0);
+paletteTexture.needsUpdate = true;
+</pre>
+<p>Chaque fois que nous voulons que three.js mette à jour la texture de palette avec
+le contenu du tableau <code class="notranslate" translate="no">palette</code>, nous devons définir <code class="notranslate" translate="no">paletteTexture.needsUpdate</code>
+sur <code class="notranslate" translate="no">true</code>.</p>
+<p>Et ensuite, nous devons toujours définir les uniformes sur le matériau.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.SphereGeometry(1, 64, 32);
+const material = new THREE.MeshBasicMaterial({map: texture});
+material.onBeforeCompile = function(shader) {
+  fragmentShaderReplacements.forEach((rep) =&gt; {
+    shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
+  });
++  shader.uniforms.paletteTexture = {value: paletteTexture};
++  shader.uniforms.indexTexture = {value: indexTexture};
++  shader.uniforms.paletteTextureWidth = {value: paletteTextureWidth};
+};
+scene.add(new THREE.Mesh(geometry, material));
+</pre>
+<p>et avec cela, nous obtenons des pays colorés aléatoirement.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/indexed-textures-random-colors.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/indexed-textures-random-colors.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Maintenant que nous pouvons voir que les textures d'index et de palette fonctionnent,
+manipulons la palette pour la mise en évidence.</p>
+<p>Faisons d'abord une fonction qui nous permettra de passer une couleur de style three.js
+et de nous donner les valeurs que nous pouvons mettre dans la texture de palette.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tempColor = new THREE.Color();
+function get255BasedColor(color) {
+  tempColor.set(color);
+  const base = tempColor.toArray().map(v =&gt; v * 255);
+  base.push(255); // alpha
+  return base;
+}
+</pre>
+<p>L'appeler comme ceci <code class="notranslate" translate="no">color = get255BasedColor('red')</code> retournera
+un tableau comme <code class="notranslate" translate="no">[255, 0, 0, 255]</code>.</p>
+<p>Ensuite, utilisons-la pour créer quelques couleurs et remplir la
+palette.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const selectedColor = get255BasedColor('red');
+const unselectedColor = get255BasedColor('#444');
+const oceanColor = get255BasedColor('rgb(100,200,255)');
+resetPalette();
+
+function setPaletteColor(index, color) {
+  palette.set(color, index * 4);
+}
+
+function resetPalette() {
+  // définir toutes les couleurs sur la couleur non sélectionnée
+  for (let i = 1; i &lt; maxNumCountries; ++i) {
+    setPaletteColor(i, unselectedColor);
+  }
+
+  // définir la couleur de l'océan (index #0)
+  setPaletteColor(0, oceanColor);
+  paletteTexture.needsUpdate = true;
+}
+</pre>
+<p>Maintenant, utilisons ces fonctions pour mettre à jour la palette lorsqu'un pays
+est sélectionné</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function pickCountry(event) {
+  // sortir si les données ne sont pas encore chargées
+  if (!countryInfos) {
+    return;
+  }
+
+  const position = getCanvasRelativePosition(event);
+  const id = pickHelper.pick(position, pickingScene, camera);
+  if (id &gt; 0) {
+    const countryInfo = countryInfos[id - 1];
+    const selected = !countryInfo.selected;
+    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {
+      unselectAllCountries();
+    }
+    numCountriesSelected += selected ? 1 : -1;
+    countryInfo.selected = selected;
++    setPaletteColor(id, selected ? selectedColor : unselectedColor);
++    paletteTexture.needsUpdate = true;
+  } else if (numCountriesSelected) {
+    unselectAllCountries();
+  }
+  requestRenderIfNotRequested();
+}
+
+function unselectAllCountries() {
+  numCountriesSelected = 0;
+  countryInfos.forEach((countryInfo) =&gt; {
+    countryInfo.selected = false;
+  });
++  resetPalette();
+}
+</pre>
+<p>et avec cela, nous devrions pouvoir mettre en évidence 1 ou plusieurs pays.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/indexed-textures-picking-and-highlighting.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/indexed-textures-picking-and-highlighting.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela semble fonctionner !</p>
+<p>Un petit détail est que nous ne pouvons pas faire tourner le globe sans changer
+l'état de sélection. Si nous sélectionnons un pays et voulons ensuite
+faire pivoter le globe, la sélection changera.</p>
+<p>Essayons de régler cela. Rapidement, nous pouvons vérifier 2 choses.
+Le temps écoulé entre le clic et le lâcher. Une autre est de savoir si
+l'utilisateur a réellement déplacé la souris. Si le
+temps est court ou s'il n'a pas bougé la souris, c'était
+probablement un clic. Sinon, il essayait probablement de
+faire glisser le globe.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const maxClickTimeMs = 200;
++const maxMoveDeltaSq = 5 * 5;
++const startPosition = {};
++let startTimeMs;
++
++function recordStartTimeAndPosition(event) {
++  startTimeMs = performance.now();
++  const pos = getCanvasRelativePosition(event);
++  startPosition.x = pos.x;
++  startPosition.y = pos.y;
++}
+
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function pickCountry(event) {
+  // sortir si les données ne sont pas encore chargées
+  if (!countryInfos) {
+    return;
+  }
+
++  // s'il s'est écoulé un certain temps depuis que l'utilisateur a commencé
++  // alors supposer qu'il s'agissait d'une action de glissement, pas de sélection
++  const clickTimeMs = performance.now() - startTimeMs;
++  if (clickTimeMs &gt; maxClickTimeMs) {
++    return;
++  }
++
++  // s'ils ont bougé, supposer qu'il s'agissait d'une action de glissement
++  const position = getCanvasRelativePosition(event);
++  const moveDeltaSq = (startPosition.x - position.x) ** 2 +
++                      (startPosition.y - position.y) ** 2;
++  if (moveDeltaSq &gt; maxMoveDeltaSq) {
++    return;
++  }
+
+-  const position = {x: event.clientX, y: event.clientY};
+  const id = pickHelper.pick(position, pickingScene, camera);
+  if (id &gt; 0) {
+    const countryInfo = countryInfos[id - 1];
+    const selected = !countryInfo.selected;
+    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {
+      unselectAllCountries();
+    }
+    numCountriesSelected += selected ? 1 : -1;
+    countryInfo.selected = selected;
+    setPaletteColor(id, selected ? selectedColor : unselectedColor);
+    paletteTexture.needsUpdate = true;
+  } else if (numCountriesSelected) {
+    unselectAllCountries();
+  }
+  requestRenderIfNotRequested();
+}
+
+function unselectAllCountries() {
+  numCountriesSelected = 0;
+  countryInfos.forEach((countryInfo) =&gt; {
+    countryInfo.selected = false;
+  });
+  resetPalette();
+}
+
++canvas.addEventListener('pointerdown', recordStartTimeAndPosition);
+canvas.addEventListener('pointerup', pickCountry);
+</pre>
+<p>et avec ces modifications, il <em>semble</em> que cela fonctionne pour moi.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/indexed-textures-picking-debounced.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/indexed-textures-picking-debounced.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Je ne suis pas expert en UX, donc j'aimerais savoir s'il existe une meilleure
+solution.</p>
+<p>J'espère que cela vous a donné une idée de l'utilité des graphiques indexés et de la façon dont vous pouvez modifier les shaders créés par three.js pour ajouter des fonctionnalités simples. L'utilisation de GLSL, le langage dans lequel les shaders sont écrits, est trop vaste pour cet article. Il y a quelques liens vers des informations dans <a href="post-processing.html">l'article sur le post-traitement</a>.</p>
 
         </div>
       </div>

+ 306 - 0
manual/fr/installation.html

@@ -0,0 +1,306 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Installation</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Installation">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Installation</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <h2>Structure du projet</h2>
+
+          <p>
+            Tout projet three.js nécessite au moins un fichier HTML pour définir la page web, et un fichier JavaScript pour exécuter votre code three.js. La structure et les choix de noms ci-dessous ne sont pas obligatoires, mais seront utilisés tout au long de ce guide par souci de cohérence.
+          </p>
+
+          <ul>
+            <li>
+              <i>index.html</i>
+    <pre class="prettyprint notranslate lang-js" translate="no">
+&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+  &lt;head&gt;
+    &lt;meta charset="utf-8"&gt;
+    &lt;title&gt;Ma première application three.js&lt;/title&gt;
+    &lt;style&gt;
+      body { margin: 0; }
+    &lt;/style&gt;
+  &lt;/head&gt;
+  &lt;body&gt;
+    &lt;script type="module" src="/main.js"&gt;&lt;/script&gt;
+  &lt;/body&gt;
+&lt;/html&gt;
+    </pre>
+  </li>
+  <li>
+    <i>main.js</i>
+<pre class="prettyprint notranslate lang-js" translate="no">
+import * as THREE from 'three';
+
+...
+</pre>
+            </li>
+            <li>
+              <i>public/</i>
+              <ul>
+                <li>
+                  Le dossier <i>public/</i> est parfois aussi appelé dossier "static", car les fichiers qu'il contient sont poussés vers le site web sans modification. Généralement, les textures, l'audio et les modèles 3D s'y trouvent.
+                </li>
+              </ul>
+            </li>
+          </ul>
+
+          <p>
+            Maintenant que nous avons mis en place la structure de base du projet, nous avons besoin d'un moyen pour exécuter le projet localement et y accéder via un navigateur web. L'installation et le développement local peuvent être accomplis avec npm et un outil de build, ou en important three.js depuis un CDN. Les deux options sont expliquées dans les sections ci-dessous.
+          </p>
+
+          <h2>Option 1 : Installation avec NPM et un outil de build</h2>
+
+          <h3>Développement</h3>
+
+          <p>
+            L'installation depuis le [link:https://www.npmjs.com/ registre de packages npm] et l'utilisation d'un [link:https://eloquentjavascript.net/10_modules.html#h_zWTXAU93DC outil de build] est l'approche recommandée pour la plupart des utilisateurs — plus votre projet a de dépendances, plus vous êtes susceptible de rencontrer des problèmes que l'hébergement statique ne peut pas facilement résoudre. Avec un outil de build, l'importation de fichiers JavaScript locaux et de packages npm devrait fonctionner directement, sans cartes d'importation.
+          </p>
+
+
+          <ol>
+            <li>
+              Installez [link:https://nodejs.org/ Node.js]. Nous en aurons besoin pour gérer les dépendances et exécuter notre outil de build.
+            </li>
+            <li>
+              <p>
+                Installez three.js et un outil de build, [link:https://vitejs.dev/ Vite], en utilisant un [link:https://www.joshwcomeau.com/javascript/terminal-for-js-devs/ terminal] dans le dossier de votre projet. Vite sera utilisé pendant le développement, mais ne fait pas partie de la page web finale. Si vous préférez utiliser un autre outil de build, c'est bien — nous supportons les outils de build modernes qui peuvent importer les [link:https://eloquentjavascript.net/10_modules.html#h_zWTXAU93DC Modules ES].
+              </p>
+<pre class="prettyprint notranslate" translate="no">
+# three.js
+npm install --save three
+
+# vite
+npm install --save-dev vite
+</pre>
+              <aside>
+                <details>
+                  <summary>L'installation a ajouté les dossiers <i>node_modules/</i> et <i>package.json</i> à mon projet. De quoi s'agit-il ?</summary>
+                  <p>
+                    npm utilise <i>package.json</i> pour décrire quelles versions de chaque dépendance vous avez installées. Si vous travaillez sur le projet avec d'autres personnes, elles peuvent installer les versions originales de chaque dépendance simplement en exécutant <i>npm install</i>. Si vous utilisez l'historique des versions, commitez <i>package.json</i>.
+                  </p>
+                  <p>
+                    npm installe le code de chaque dépendance dans un nouveau dossier <i>node_modules/</i>. Lorsque Vite construit votre application, il voit les importations pour 'three' et extrait automatiquement les fichiers three.js de ce dossier. Le dossier <i>node_modules/</i> est utilisé uniquement pendant le développement, et ne doit pas être téléversé chez votre hébergeur web ou commité dans l'historique des versions.
+                  </p>
+                </details>
+                <details>
+                  <summary>Améliorez l'auto-complétion de votre éditeur avec <i>jsconfig</i> ou <i>tsconfig</i></summary>
+                  <p>
+                    Placez un fichier <i>jsconfig.json</i> (ou <i>tsconfig.json</i> pour les projets TypeScript) à la racine de votre projet. L'ajout de la configuration ci-dessous aide votre éditeur à localiser les fichiers three.js pour une auto-complétion améliorée.
+                  </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+{
+  "compilerOptions": {
+    // other options...
+    "paths": {
+      "three/webgpu": ["node_modules/three/build/three.webgpu.js"],
+      "three/tsl": ["node_modules/three/build/three.tsl.js"],
+    },
+  }
+}
+</pre>
+                </details>
+              </aside>
+            </li>
+            <li>
+              Depuis votre terminal, exécutez :
+              <pre class="prettyprint notranslate" translate="no">npx vite </pre>
+              <aside>
+                <details>
+                  <summary>Qu'est-ce que <i>npx</i> ?</summary>
+                  <p>
+                    npx est installé avec Node.js, et exécute des programmes en ligne de commande comme Vite afin que vous n'ayez pas à chercher vous-même le bon fichier dans <i>node_modules/</i>. Si vous préférez, vous pouvez mettre les [link:https://vitejs.dev/guide/#command-line-interface commandes courantes de Vite] dans la liste [link:https://docs.npmjs.com/cli/v9/using-npm/scripts package.json:scripts], et utiliser <i>npm run dev</i> à la place.
+                  </p>
+                </details>
+              </aside>
+            </li>
+            <li>
+              Si tout s'est bien passé, vous verrez une URL comme <i>http://localhost:5173</i> apparaître dans votre terminal, et vous pourrez ouvrir cette URL pour voir votre application web.
+            </li>
+          </ol>
+
+          <p>
+            La page sera vide — vous êtes prêt à <a href="creating-a-scene.html">créer une scène</a>.
+          </p>
+
+          <p>
+            Si vous voulez en savoir plus sur ces outils avant de continuer, consultez :
+          </p>
+
+          <ul>
+            <li>
+              [link:https://threejs-journey.com/lessons/local-server three.js journey : Serveur local]
+            </li>
+            <li>
+              [link:https://vitejs.dev/guide/cli.html Vite : Interface en ligne de commande]
+            </li>
+            <li>
+              [link:https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management MDN : Principes de base de la gestion des packages]
+            </li>
+          </ul>
+
+          <h3>Production</h3>
+
+          <p>
+            Plus tard, lorsque vous serez prêt à déployer votre application web, il vous suffira d'indiquer à Vite d'exécuter une build de production — <i>npx vite build</i>. Tout ce qui est utilisé par l'application sera compilé, optimisé et copié dans le dossier <i>dist/</i>. Le contenu de ce dossier est prêt à être hébergé sur votre site web.
+          </p>
+
+          <h2>Option 2 : Importation depuis un CDN</h2>
+
+          <h3>Développement</h3>
+
+          <p>L'installation sans outils de build nécessitera quelques modifications de la structure du projet donnée ci-dessus.</p>
+
+          <ol>
+            <li>
+              <p>
+                Nous avons importé du code depuis 'three' (un package npm) dans <i>main.js</i>, et les navigateurs web ne savent pas ce que cela signifie. Dans <i>index.html</i>, nous devrons ajouter une [link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap carte d'importation] définissant où obtenir le package. Placez le code ci-dessous à l'intérieur de la balise <i>&lt;head>&lt/head></i>, après les styles.
+              </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+&lt;script type="importmap">
+{
+  "imports": {
+    "three": "https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/build/three.module.js",
+    "three/addons/": "https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/examples/jsm/"
+  }
+}
+&lt;/script>
+</pre>
+              <p>
+                N'oubliez pas de remplacer <i>&lt;version&gt;</i> par une version réelle de three.js, comme <i>"v0.149.0"</i>. La version la plus récente peut être trouvée sur la [link:https://www.npmjs.com/package/three?activeTab=versions liste des versions npm].
+              </p>
+            </li>
+            <li>
+              <p>
+                Nous aurons également besoin d'exécuter un <i>serveur local</i> pour héberger ces fichiers à une URL accessible par le navigateur web. Bien qu'il soit techniquement possible de double-cliquer sur un fichier HTML et de l'ouvrir dans votre navigateur, des fonctionnalités importantes que nous implémenterons plus tard ne fonctionnent pas lorsque la page est ouverte de cette manière, pour des raisons de sécurité.
+              </p>
+              <p>
+                Installez [link:https://nodejs.org/ Node.js], puis exécutez [link:https://www.npmjs.com/package/serve serve] pour démarrer un serveur local dans le répertoire du projet :
+              </p>
+              <pre class="prettyprint notranslate" translate="no">npx serve .</pre>
+            </li>
+            <li>
+              Si tout s'est bien passé, vous verrez une URL comme http://localhost:3000 apparaître dans votre terminal, et vous pourrez ouvrir cette URL pour voir votre application web.
+            </li>
+          </ol>
+
+          <p>
+            La page sera vide — vous êtes prêt à [link:#manual/introduction/Creating-a-scene créer une scène].
+          </p>
+
+          <p>
+            De nombreux autres serveurs statiques locaux sont disponibles — certains utilisent des langages différents au lieu de Node.js, et d'autres sont des applications de bureau. Ils fonctionnent tous fondamentalement de la même manière, et nous avons fourni quelques alternatives ci-dessous.
+          </p>
+
+          <details>
+            <summary>Plus de serveurs locaux</summary>
+
+            <h3>Ligne de commande</h3>
+
+            <p>Les serveurs locaux en ligne de commande s'exécutent depuis une fenêtre de terminal. Le langage de programmation associé peut devoir être installé au préalable.</p>
+
+            <ul>
+              <li><i>npx http-server</i> (Node.js)</li>
+              <li><i>npx five-server</i> (Node.js)</li>
+              <li><i>python -m SimpleHTTPServer</i> (Python 2.x)</li>
+              <li><i>python -m http.server</i> (Python 3.x)</li>
+              <li><i>php -S localhost:8000</i> (PHP 5.4+)</li>
+            </ul>
+
+
+            <h3>GUI</h3>
+
+            <p>Les serveurs locaux GUI s'exécutent sous forme de fenêtre d'application sur votre ordinateur, et peuvent avoir une interface utilisateur.</p>
+
+            <ul>
+              <li>[link:https://greggman.github.io/servez Servez]</li>
+            </ul>
+
+            <h3>Plugins d'éditeur de code</h3>
+
+            <p>Certains éditeurs de code disposent de plugins qui lancent un simple serveur à la demande.</p>
+
+            <ul>
+              <li>[link:https://marketplace.visualstudio.com/items?itemName=yandeu.five-server Five Server] pour Visual Studio Code</li>
+              <li>[link:https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer Live Server] pour Visual Studio Code</li>
+              <li>[link:https://atom.io/packages/atom-live-server Live Server] pour Atom</li>
+            </ul>
+
+
+          </details>
+
+          <h3>Production</h3>
+
+          <p>
+            Lorsque vous êtes prêt à déployer votre application web, poussez les fichiers source chez votre hébergeur web — pas besoin de build ou de compiler quoi que ce soit. L'inconvénient de ce compromis est que vous devrez veiller à maintenir la carte d'importation à jour avec toutes les dépendances (et les dépendances des dépendances !) dont votre application a besoin. Si le CDN hébergeant vos dépendances tombe temporairement, votre site web cessera également de fonctionner.
+          </p>
+
+          <p>
+            <i><b>IMPORTANT :</b> Importez toutes les dépendances depuis la même version de three.js et depuis le même CDN. Mélanger des fichiers de différentes sources peut entraîner l'inclusion de code dupliqué, ou même casser l'application de manière inattendue.</i>
+          </p>
+
+          <h2>Addons</h2>
+
+          <p>
+            Par défaut, three.js inclut les fondamentaux d'un moteur 3D. Les autres composants de three.js — tels que les contrôles, les chargeurs et les effets de post-traitement — font partie du répertoire [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm addons/]. Les Addons n'ont pas besoin d'être <i>installés</i> séparément, mais doivent être <i>importés</i> séparément.
+          </p>
+
+          <p>
+            L'exemple ci-dessous montre comment importer three.js avec les addons `OrbitControls` et `GLTFLoader`. Si nécessaire, cela sera également mentionné dans la documentation ou les exemples de chaque addon.
+          </p>
+
+<pre class="prettyprint notranslate lang-js" translate="no">
+import * as THREE from 'three';
+import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
+
+const controls = new OrbitControls( camera, renderer.domElement );
+const loader = new GLTFLoader();
+</pre>
+
+          <p>
+            D'excellents projets tiers sont également disponibles pour three.js. Ceux-ci doivent être installés séparément — voir <a href="libraries-and-plugins">Bibliothèques et Plugins</a>.
+          </p>
+
+          <h2>Étapes suivantes</h2>
+
+          <p>
+            Vous êtes maintenant prêt à <a href="creating-a-scene.html">créer une scène</a>.
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 146 - 0
manual/fr/libraries-and-plugins.html

@@ -0,0 +1,146 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Bibliothèques et Plugins</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Bibliothèques et Plugins">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Bibliothèques et Plugins</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p class="desc">
+            Voici une liste de bibliothèques et plugins compatibles développés en externe pour three.js. Cette
+            liste et les paquets associés sont maintenus par la communauté et ne sont pas garantis
+            d'être à jour. Si vous souhaitez mettre à jour cette liste, faites une Pull Request !
+          </p>
+      
+          <h3>Physique</h3>
+      
+          <ul>
+            <li>[link:https://github.com/lo-th/Oimo.js/ Oimo.js]</li>
+            <li>[link:https://enable3d.io/ enable3d]</li>
+            <li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
+            <li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
+            <li>[link:https://rapier.rs/ rapier]</li>
+            <li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
+            
+          </ul>
+      
+          <h3>Post-traitement</h3>
+      
+          <p>
+            En plus des [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/postprocessing effets de post-traitement officiels de three.js],
+            la prise en charge d'effets et de frameworks supplémentaires est disponible via des bibliothèques externes.
+          </p>
+      
+          <ul>
+            <li>[link:https://github.com/vanruesc/postprocessing postprocessing]</li>
+          </ul>
+      
+          <h3>Performance d'Intersection et de Raycast</h3>
+      
+          <ul>
+            <li>[link:https://github.com/gkjohnson/three-mesh-bvh three-mesh-bvh]</li>
+          </ul>
+      
+          <h3>Tracé de chemin</h3>
+          
+          <ul>
+            <li>[link:https://github.com/gkjohnson/three-gpu-pathtracer three-gpu-pathtracer]</li>
+          </ul>
+          
+          <h3>Formats de fichier</h3>
+      
+          <p>
+            En plus des [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/loaders chargeurs officiels de three.js],
+            la prise en charge de formats supplémentaires est disponible via des bibliothèques externes.
+          </p>
+      
+          <ul>
+            <li>[link:https://github.com/gkjohnson/urdf-loaders/tree/master/javascript urdf-loader]</li>
+            <li>[link:https://github.com/NASA-AMMOS/3DTilesRendererJS 3d-tiles-renderer-js]</li>
+            <li>[link:https://github.com/kaisalmen/WWOBJLoader Chargeur OBJ WebWorker]</li>
+            <li>[link:https://github.com/IFCjs/web-ifc-three IFC.js]</li>
+          </ul>
+      
+          <h3>Géométrie</h3>
+      
+          <ul>
+            <li>[link:https://github.com/spite/THREE.MeshLine THREE.MeshLine]</li>
+          </ul>
+      
+          <h3>Texte et Mise en page 3D</h3>
+      
+          <ul>
+            <li>[link:https://github.com/protectwise/troika/tree/master/packages/troika-three-text troika-three-text]</li>
+            <li>[link:https://github.com/felixmariotto/three-mesh-ui three-mesh-ui]</li>
+          </ul>
+      
+          <h3>Systèmes de particules</h3>
+      
+          <ul>
+            <li>[link:https://github.com/Alchemist0823/three.quarks three.quarks]</li>
+            <li>[link:https://github.com/creativelifeform/three-nebula three-nebula]</li>
+          </ul>
+      
+          <h3>Cinématique inverse</h3>
+      
+          <ul>
+            <li>[link:https://github.com/jsantell/THREE.IK THREE.IK]</li>
+            <li>[link:https://github.com/lo-th/fullik fullik]</li>
+            <li>[link:https://github.com/gkjohnson/closed-chain-ik-js closed-chain-ik]</li>
+          </ul>
+      
+          <h3>IA de jeu</h3>
+      
+          <ul>
+            <li>[link:https://mugen87.github.io/yuka/ yuka]</li>
+            <li>[link:https://github.com/donmccurdy/three-pathfinding three-pathfinding]</li>
+            <li>[link:https://github.com/isaac-mason/recast-navigation-js recast-navigation-js]</li>
+          </ul>
+      
+          <h3>Wrappers et Frameworks</h3>
+      
+          <ul>
+            <li>[link:https://aframe.io/ A-Frame]</li>
+            <li>[link:https://lume.io/ Lume] - Éléments HTML pour graphismes 3D basés sur Three.</li>
+            <li>[link:https://github.com/pmndrs/react-three-fiber react-three-fiber] - Composants React pour graphismes 3D basés sur Three.</li>
+            <li>[link:https://threepipe.org/ threepipe] - Un framework de visualisation 3D polyvalent utilisant three.js pour le rendu.</li>
+            <li>[link:https://ecsyjs/ecsy-three ECSY]</li>
+            <li>[link:https://threlte.xyz/ Threlte] - Composants Svelte pour graphismes 3D basés sur Three.</li>
+            <li>[link:https://needle.tools/ Needle Engine]</li>
+            <li>[link:https://tresjs.org/ tresjs] - Composants Vue pour graphismes 3D basés sur Three.</li>
+            <li>[link:https://giro3d.org Giro3D] - Framework polyvalent basé sur Three pour visualiser et interagir avec des données géospatiales 2D, 2.5D et 3D.</li>
+            <li>[link:https://zap.works/mattercraft/ Mattercraft] - Éditeur visuel basé sur navigateur pour le contenu web AR, WebXR et 3D, construit sur three.js avec aperçu en temps réel et moteur physique.</li>
+          </ul>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 171 - 88
manual/fr/lights.html

@@ -26,11 +26,15 @@
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>. Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là ou aussi voir l'article sur <a href="setup.html">la configuration de votre environnement</a>. L'
-<a href="textures.html">article précédent</a> parlait des textures.</p>
-<p>Voyons comment utiliser les différents types de lumières.</p>
-<p>En commençant avec l'un de nos exemples précédents, mettons à jour la caméra. Nous allons régler le champ de vision à 45 degrés, le plan éloigné à 100 unités, et nous déplacerons la caméra de 10 unités vers le haut et 20 unités en arrière de l'origine.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le
+premier article est <a href="fundamentals.html">les bases de three.js</a>. Si
+vous ne l'avez pas encore lu et que vous débutez avec three.js, vous pourriez envisager de
+commencer par là, ainsi que l'article sur <a href="setup.html">la configuration de votre environnement</a>. Le
+<a href="textures.html">l'article précédent portait sur les textures</a>.</p>
+<p>Voyons comment utiliser les différents types de lumières dans three.js.</p>
+<p>En partant d'un de nos exemples précédents, mettons à jour la caméra.
+Nous définirons le champ de vision à 45 degrés, le plan lointain à 100 unités,
+et nous déplacerons la caméra de 10 unités vers le haut et de 20 unités vers l'arrière par rapport à l'origine</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">*const fov = 45;
 const aspect = 2;  // the canvas default
 const near = 0.1;
@@ -38,26 +42,37 @@ const near = 0.1;
 const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 +camera.position.set(0, 10, 20);
 </pre>
-<p>Ajoutons ensuite <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>. <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> permet à l'utilisateur de tourner ou de mettre la caméra en <em>orbite</em> autour d'un certain point. Il s'agit d'une fonctionnalité facultative de Three.js, nous devons donc d'abord l'importer</p>
+<p>Ajoutons ensuite <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>. Les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>
+permettent à l'utilisateur de faire tourner ou d'<em>orbiter</em> la caméra autour d'un point. Les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>
+sont une fonctionnalité optionnelle de three.js, nous devons donc d'abord les inclure
+dans notre page</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
 +import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
 </pre>
-<p>Ensuite, nous pouvons l'utiliser. Nous passons à <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> une caméra à contrôler et l'élément DOM à utiliser.</p>
+<p>Ensuite, nous pouvons les utiliser. Nous passons aux <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> une caméra à
+contrôler et l'élément DOM à utiliser pour obtenir les événements d'entrée</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const controls = new OrbitControls(camera, canvas);
 controls.target.set(0, 5, 0);
 controls.update();
 </pre>
-<p>Nous plaçons également la cible en orbite, 5 unités au-dessus de l'origine
-et appelons <code class="notranslate" translate="no">controls.update</code> afin que les contrôles utilisent la nouvelle cible.</p>
-<p>Ensuite, créons des choses à éclairer. Nous allons d'abord faire un plan au sol. Nous allons appliquer une petite texture en damier de 2x2 pixels qui ressemble à ceci</p>
+<p>Nous définissons également la cible d'orbite à 5 unités au-dessus de l'origine
+et appelons ensuite <code class="notranslate" translate="no">controls.update</code> pour que les contrôles utilisent la nouvelle
+cible.</p>
+<p>Voyons ensuite comment créer des éléments à éclairer. D'abord, nous allons créer un
+plan au sol. Nous appliquerons une petite texture en damier de 2x2 pixels qui
+ressemble à ceci :</p>
 <div class="threejs_center">
   <img src="../examples/resources/images/checker.png" class="border" style="
     image-rendering: pixelated;
     width: 128px;
-  ">
+  " alt="">
 </div>
 
-<p>Tout d'abord, chargeons la texture, définissons-la sur répétition, définissons le filtrage au plus proche et définissons le nombre de fois que nous voulons qu'elle se répète. Étant donné que la texture est un damier de 2x2 pixels, en répétant et en définissant la répétition à la moitié de la taille du plan, chaque case sur le damier aura exactement 1 unité de large ;</p>
+<p>Nous chargeons d'abord la texture, la définissons en mode répétition, définissons le filtrage au
+plus proche, et définissons le nombre de fois que nous voulons qu'elle se répète. Étant donné que la
+texture est un damier de 2x2 pixels, en la répétant et en définissant la
+répétition à la moitié de la taille du plan, chaque case du damier
+aura exactement 1 unité de taille ;</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeSize = 40;
 
 const loader = new THREE.TextureLoader();
@@ -69,7 +84,9 @@ texture.colorSpace = THREE.SRGBColorSpace;
 const repeats = planeSize / 2;
 texture.repeat.set(repeats, repeats);
 </pre>
-<p>Nous fabriquons ensuite une géométrie 'plane', un matériau et une 'mesh' pour l'insérer dans la scène. Les plans sont par défaut dans le plan XY, mais le sol est dans le plan XZ, nous le faisons donc pivoter.</p>
+<p>Nous créons ensuite une géométrie de plan, un matériau pour le plan et un maillage
+pour l'insérer dans la scène. Les plans sont par défaut dans le plan XY,
+mais le sol est dans le plan XZ, nous le faisons donc pivoter.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
 const planeMat = new THREE.MeshPhongMaterial({
   map: texture,
@@ -79,7 +96,7 @@ const mesh = new THREE.Mesh(planeGeo, planeMat);
 mesh.rotation.x = Math.PI * -.5;
 scene.add(mesh);
 </pre>
-<p>Ajoutons un cube et une sphère, ainsi nous aurons 3 choses à éclairer dont le plan</p>
+<p>Ajoutons un cube et une sphère pour avoir 3 éléments à éclairer, y compris le plan.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const cubeSize = 4;
   const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
@@ -101,16 +118,21 @@ scene.add(mesh);
 </pre>
 <p>Maintenant que nous avons une scène à éclairer, ajoutons des lumières !</p>
 <h2 id="-ambientlight-"><a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a></h2>
-<p>D'abord mettons en place une <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a></p>
+<p>Commençons par créer une <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">Lumière Ambiante</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
 const intensity = 1;
 const light = new THREE.AmbientLight(color, intensity);
 scene.add(light);
 </pre>
-<p>Faisons aussi en sorte que nous puissions ajuster les paramètres de la lumière.
-Utilisons à nouveau <a href="https://github.com/georgealways/lil-gui">lil-gui</a>.
-Pour pouvoir ajuster la couleur via lil-gui, nous avons besoin d'un petit 'helper' qui fournit à lil-gui une couleur en hexadécimale (eg: <code class="notranslate" translate="no">#FF8844</code>). Notre 'helper' obtiendra la couleur d'une propriété nommée, la convertira en une chaîne hexadécimale à offrir à lil-gui. Lorsque lil-gui essaie de définir la propriété de l'assistant, nous attribuons le résultat à la couleur de la lumière.</p>
-<p>Voici notre 'helper':</p>
+<p>Faisons en sorte de pouvoir également ajuster les paramètres de la lumière.
+Nous utiliserons de nouveau <a href="https://github.com/georgealways/lil-gui">lil-gui</a>.
+Pour pouvoir ajuster la couleur via lil-gui, nous avons besoin d'un petit assistant
+qui présente une propriété à lil-gui qui ressemble à une chaîne de couleur hexadécimale CSS
+(par ex. : <code class="notranslate" translate="no">#FF8844</code>). Notre assistant obtiendra la couleur d'une propriété nommée,
+la convertira en chaîne hexadécimale pour l'offrir à lil-gui.
+Lorsque lil-gui essaiera de définir la propriété de l'assistant, nous assignerons le résultat à la
+couleur de la lumière.</p>
+<p>Voici l'assistant :</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ColorGUIHelper {
   constructor(object, prop) {
     this.object = object;
@@ -124,25 +146,34 @@ Pour pouvoir ajuster la couleur via lil-gui, nous avons besoin d'un petit 'helpe
   }
 }
 </pre>
-<p>Et voici le code de configuration de lil-gui</p>
+<p>Et voici notre code de configuration de lil-gui</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur');
 gui.add(light, 'intensity', 0, 5, 0.01);
 </pre>
-<p>Le résultat :</p>
+<p>Et voici le résultat</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-ambient.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-ambient.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-ambient.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Cliquez/glissez pour mettre la caméra en <em>orbite</em>.</p>
-<p>Remarquez qu'il n'y a pas de définition. Les formes sont plates. L'<a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a> multiplie simplement la couleur du matériau par la couleur de la lumière multipliée par l'intensité.</p>
+<p>Cliquez et faites glisser dans la scène pour faire <em>orbiter</em> la caméra.</p>
+<p>Remarquez qu'il n'y a pas de définition. Les formes sont plates. La <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">Lumière Ambiante</code></a>
+multiplie simplement la couleur du matériau par la couleur de la lumière multipliée par l'
+intensité.</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">color = materialColor * light.color * light.intensity;
-</pre><p>C'est tout. Il n'a pas de direction. Ce style d'éclairage ambiant n'est en fait pas très utile en tant qu'éclairage, à part changer la couleur de toute la scène, ce n'est pas vraiment un éclairage, ça rend juste les ténèbres moins sombres.</p>
-
+</pre><p>C'est tout. Elle n'a pas de direction.
+Ce style d'éclairage ambiant n'est pas très utile en tant qu'éclairage car il est
+uniformément réparti, donc à part changer la couleur
+de tout dans la scène, il ne ressemble pas beaucoup à un <em>éclairage</em>.
+Ce qui aide, c'est qu'il rend les zones sombres moins sombres.</p>
 <h2 id="-hemispherelight-"><a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a></h2>
-<p>Passons à une <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a>. Une <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> prend une couleur de ciel et une couleur de sol et multiplie simplement la couleur du matériau entre ces 2 couleurs : la couleur du ciel si la surface de l'objet pointe vers le haut et la couleur du sol si la surface de l'objet pointe vers le bas.</p>
+<p>Passons au code pour une <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">Lumière Hémisphérique</code></a>. Une <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">Lumière Hémisphérique</code></a>
+prend une couleur de ciel et une couleur de sol et multiplie simplement la
+couleur du matériau entre ces 2 couleurs — la couleur du ciel si la
+surface de l'objet pointe vers le haut et la couleur du sol si
+la surface de l'objet pointe vers le bas.</p>
 <p>Voici le nouveau code</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const color = 0xFFFFFF;
 +const skyColor = 0xB1E1FF;  // light blue
@@ -152,24 +183,28 @@ const intensity = 1;
 +const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
 scene.add(light);
 </pre>
-<p>Mettons aussi à jour le code de lil-gui avec ces 2 couleurs</p>
+<p>Mettons également à jour le code lil-gui pour éditer les deux couleurs</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
 -gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
-+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('skyColor');
-+gui.addColor(new ColorGUIHelper(light, 'groundColor'), 'value').name('groundColor');
++gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur du ciel');
++gui.addColor(new ColorGUIHelper(light, 'groundColor'), 'value').name('couleur du sol');
 gui.add(light, 'intensity', 0, 5, 0.01);
 </pre>
 <p>Le résultat :</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-hemisphere.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-hemisphere.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-hemisphere.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Remarquez encore une fois qu'il n'y a presque pas de définition, tout a l'air plutôt plat. L'<a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> utilisée en combinaison avec une autre lumière peut aider à donner une belle sorte d'influence de la couleur du ciel et du sol. Retenez qu'il est préférable de l'utiliser en combinaison avec une autre lumière ou à la place d'une <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a>.</p>
+<p>Remarquez de nouveau qu'il n'y a presque pas de définition, tout semble un peu
+plat. La <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">Lumière Hémisphérique</code></a> utilisée en combinaison avec une autre lumière
+peut aider à donner une belle influence de la couleur du
+ciel et du sol. De cette façon, elle est mieux utilisée en combinaison avec une
+autre lumière ou en substitut d'une <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">Lumière Ambiante</code></a>.</p>
 <h2 id="-directionallight-"><a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a></h2>
-<p>Remplaçons le code par une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>.
-Une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> est souvent utilisée pour représenter la lumière du soleil.</p>
+<p>Passons au code pour une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a>.
+Une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a> est souvent utilisée pour représenter le soleil.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
 const intensity = 1;
 const light = new THREE.DirectionalLight(color, intensity);
@@ -178,11 +213,13 @@ light.target.position.set(-5, 0, 0);
 scene.add(light);
 scene.add(light.target);
 </pre>
-<p>Notez que nous avons dû ajouter une <code class="notranslate" translate="no">light</code> et une <code class="notranslate" translate="no">light.target</code>
-à la scène. Une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> doit illuminer une cible.</p>
-<p>Faisons en sorte que nous puissions déplacer la cible en l'ajoutant à lil-gui.</p>
+<p>Remarquez que nous avons dû ajouter la <code class="notranslate" translate="no">light</code> et la <code class="notranslate" translate="no">light.target</code>
+à la scène. Une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a> three.js
+brillera dans la direction de sa cible.</p>
+<p>Faisons en sorte de pouvoir déplacer la cible en l'ajoutant à
+notre interface GUI.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur');
 gui.add(light, 'intensity', 0, 5, 0.01);
 gui.add(light.target.position, 'x', -10, 10);
 gui.add(light.target.position, 'z', -10, 10);
@@ -190,16 +227,23 @@ gui.add(light.target.position, 'y', 0, 10);
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-directional.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-directional.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-directional.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>C'est un peu difficile de voir ce qui se passe. Three.js a un tas de 'helper' que nous pouvons ajouter à la scène pour voir les objets invisibles. Utilisons, dans ce cas,
-<a href="/docs/#api/en/helpers/DirectionalLightHelper"><code class="notranslate" translate="no">DirectionalLightHelper</code></a> qui a représente la source de lumière en direction de sa cible. Il suffit de lui ajouter une lumière et de l'ajouter à la scène.</p>
+<p>Il est un peu difficile de voir ce qui se passe. Three.js dispose d'un ensemble
+d'objets d'aide que nous pouvons ajouter à notre scène pour aider à visualiser
+les parties invisibles d'une scène. Dans ce cas, nous utiliserons le
+<a href="/docs/#api/en/helpers/DirectionalLightHelper"><code class="notranslate" translate="no">Helper de Lumière Directionnelle</code></a> qui dessinera un plan, pour représenter
+la lumière, et une ligne de la lumière à la cible. Nous lui
+passons simplement la lumière et l'ajoutons à la scène.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const helper = new THREE.DirectionalLightHelper(light);
 scene.add(helper);
 </pre>
-<p>Pendant que nous y sommes, faisons en sorte que nous puissions définir à la fois la position de la lumière et la cible. Pour ce faire, nous allons créer une fonction qui, étant donné un <a href="/docs/#api/en/math/Vector3"><code class="notranslate" translate="no">Vector3</code></a>, ajustera ses propriétés <code class="notranslate" translate="no">x</code>, <code class="notranslate" translate="no">y</code> et <code class="notranslate" translate="no">z</code> à l'aide de <code class="notranslate" translate="no">lil-gui</code>.</p>
+<p>Pendant que nous y sommes, faisons en sorte de pouvoir définir à la fois la position
+de la lumière et la cible. Pour ce faire, nous allons créer une fonction
+qui, étant donné un <a href="/docs/#api/en/math/Vector3"><code class="notranslate" translate="no">Vector3</code></a>, ajustera ses propriétés <code class="notranslate" translate="no">x</code>, <code class="notranslate" translate="no">y</code>, et <code class="notranslate" translate="no">z</code>
+en utilisant <code class="notranslate" translate="no">lil-gui</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeXYZGUI(gui, vector3, name, onChangeFn) {
   const folder = gui.addFolder(name);
   folder.add(vector3, 'x', -10, 10).onChange(onChangeFn);
@@ -208,8 +252,12 @@ scene.add(helper);
   folder.open();
 }
 </pre>
-<p>Notez que nous devons appeler la fonction <code class="notranslate" translate="no">update</code> du 'helper' à chaque fois que nous modifions quelque chose afin que l'assistant sache se mettre à jour. En tant que tel, nous passons une fonction <code class="notranslate" translate="no">onChangeFn</code> pour être appelée à chaque fois que lil-gui met à jour une valeur.</p>
-<p>Ensuite, nous pouvons l'utiliser à la fois pour la position de la lumière et la position de la cible comme ceci</p>
+<p>Notez que nous devons appeler la fonction <code class="notranslate" translate="no">update</code> de l'assistant
+chaque fois que nous changeons quelque chose afin que l'assistant sache qu'il doit se mettre à
+jour. Ainsi, nous passons une fonction <code class="notranslate" translate="no">onChangeFn</code> qui sera
+appelée chaque fois que lil-gui met à jour une valeur.</p>
+<p>Ensuite, nous pouvons l'utiliser à la fois pour la position de la lumière
+et pour la position de la cible, comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function updateLight() {
 +  light.target.updateMatrixWorld();
 +  helper.update();
@@ -217,22 +265,27 @@ scene.add(helper);
 +updateLight();
 
 const gui = new GUI();
-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur');
 gui.add(light, 'intensity', 0, 5, 0.01);
 
 +makeXYZGUI(gui, light.position, 'position', updateLight);
-+makeXYZGUI(gui, light.target.position, 'target', updateLight);
++makeXYZGUI(gui, light.target.position, 'cible', updateLight);
 </pre>
-<p>Maintenant, nous pouvons bouger la lumière, et sa cible.</p>
+<p>Nous pouvons maintenant déplacer la lumière, et sa cible</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-directional-w-helper.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-directional-w-helper.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-directional-w-helper.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Mettez la caméra en orbite et il devient plus facile de voir. Le plan représente une lumière directionnelle car une lumière directionnelle calcule la lumière venant dans une direction. Il n'y a aucun point d'où vient la lumière, c'est un plan de lumière infini qui projette des rayons de lumière parallèles.</p>
+<p>Faites orbiter la caméra et il devient plus facile de voir. Le plan
+représente une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a> car une lumière directionnelle
+calcule la lumière venant dans une seule direction. Il n'y a pas de
+<em>point</em> d'où la lumière provient, c'est un plan infini de lumière
+émettant des rayons parallèles.</p>
 <h2 id="-pointlight-"><a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a></h2>
-<p>Un <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> est une lumière qui se trouve en un point et projette de la lumière dans toutes les directions à partir de ce point. Changeons le code.</p>
+<p>Une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">Lumière Ponctuelle</code></a> est une lumière qui se situe à un point et projette de la lumière
+dans toutes les directions à partir de ce point. Modifions le code.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
 -const intensity = 1;
 +const intensity = 150;
@@ -243,42 +296,53 @@ light.position.set(0, 10, 0);
 scene.add(light);
 -scene.add(light.target);
 </pre>
-<p>Passons également à un <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">PointLightHelper</code></a></p>
+<p>Passons également à un <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">Helper de Lumière Ponctuelle</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const helper = new THREE.DirectionalLightHelper(light);
 +const helper = new THREE.PointLightHelper(light);
 scene.add(helper);
 </pre>
-<p>et comme il n'y a pas de cible la fonction <code class="notranslate" translate="no">onChange</code> peut être simplifiée.</p>
+<p>et comme il n'y a pas de cible, la fonction <code class="notranslate" translate="no">onChange</code> peut être plus simple.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateLight() {
 -  light.target.updateMatrixWorld();
   helper.update();
 }
 -updateLight();
 </pre>
-<p>Notez qu'à un certain niveau, un <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">PointLightHelper</code></a> n'a pas de point. Il dessine juste un petit diamant filaire. Ou n'importe quelle autre forme que vous voulez, ajoutez simplement un maillage à la lumière elle-même.</p>
-<p>Une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> a une propriété supplémentaire, <a href="/docs/#api/en/lights/PointLight#distance"><code class="notranslate" translate="no">distance</code></a>.
-Si la <code class="notranslate" translate="no">distance</code> est de 0, le <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> brille à l'infini. Si la <code class="notranslate" translate="no">distance</code> est supérieure à 0, la lumière brille de toute son intensité vers la lumière et s'estompe jusqu'à n'avoir aucune influence à des unités de <code class="notranslate" translate="no">distance</code> de la lumière.</p>
-<p>Mettons à jour lil-gui pour pouvoir modifier la distance.</p>
+<p>Notez qu'à un certain niveau, un <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">Helper de Lumière Ponctuelle</code></a> n'a pas de... point.
+Il dessine simplement un petit losange en fil de fer. Cela pourrait tout aussi facilement
+être n'importe quelle forme que vous souhaitez, il suffit d'ajouter un maillage à la lumière elle-même.</p>
+<p>Une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">Lumière Ponctuelle</code></a> a la propriété supplémentaire de <a href="/docs/#api/en/lights/PointLight#distance"><code class="notranslate" translate="no">distance</code></a>.
+Si la <code class="notranslate" translate="no">distance</code> est 0, alors la <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">Lumière Ponctuelle</code></a> brille à
+l'infini. Si la <code class="notranslate" translate="no">distance</code> est supérieure à 0, alors la lumière brille
+à pleine intensité au niveau de la lumière et s'estompe jusqu'à ne plus avoir d'influence à
+<code class="notranslate" translate="no">distance</code> unités de distance de la lumière.</p>
+<p>Configurons l'interface GUI pour que nous puissions ajuster la distance.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur');
 gui.add(light, 'intensity', 0, 250, 1);
 +gui.add(light, 'distance', 0, 40).onChange(updateLight);
 
 makeXYZGUI(gui, light.position, 'position', updateLight);
 -makeXYZGUI(gui, light.target.position, 'target', updateLight);
 </pre>
-<p>Et maintenant, testons.</p>
+<p>Et maintenant, essayez.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-point.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-point.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-point.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Remarquez comment la lumière s'éteint lorsque la <code class="notranslate" translate="no">distance</code> est &gt; 0.</p>
+<p>Remarquez quand <code class="notranslate" translate="no">distance</code> est > 0 comment la lumière s'estompe.</p>
 <h2 id="-spotlight-"><a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a></h2>
-<p>La <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> (projecteur), est une lumière ponctuelle avec un cône attaché et où la lumière ne brille qu'à l'intérieur de celui-ci. Il y a en fait 2 cônes. Un cône extérieur et un cône intérieur. Entre le cône intérieur et le cône extérieur, la lumière passe de la pleine intensité à zéro.</p>
-<p>Pour utiliser une <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>, nous avons besoin d'une cible tout comme la lumière directionnelle. Le cône de lumière s'ouvrira vers la cible.</p>
-<p>Modifions notre <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> avec le 'helper' vu plus haut</p>
+<p>Les projecteurs sont effectivement une lumière ponctuelle avec un cône
+attaché où la lumière ne brille qu'à l'intérieur du cône.
+Il y a en fait 2 cônes. Un cône extérieur et un cône intérieur.
+Entre le cône intérieur et le cône extérieur, la
+lumière s'estompe de la pleine intensité à zéro.</p>
+<p>Pour utiliser une <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">Projecteur</code></a>, nous avons besoin d'une cible, tout comme
+pour la lumière directionnelle. Le cône de la lumière s'ouvrira
+vers la cible.</p>
+<p>En modifiant notre <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a> avec l'assistant d'en haut</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
 -const intensity = 1;
 +const intensity = 150;
@@ -291,25 +355,37 @@ scene.add(light.target);
 +const helper = new THREE.SpotLightHelper(light);
 scene.add(helper);
 </pre>
-<p>L'angle du cône de la <code class="notranslate" translate="no">Spotlight</code> est défini avec la propriété <a href="/docs/#api/en/lights/SpotLight#angle"><code class="notranslate" translate="no">angle</code></a>
-en radians. Utilisons notre <code class="notranslate" translate="no">DegRadHelper</code> vu dans
-<a href="textures.html">l'article sur les textures</a> pour modifier l'angle avec lil-gui.</p>
+<p>L'angle du cône du projecteur est défini avec la propriété <a href="/docs/#api/en/lights/SpotLight#angle"><code class="notranslate" translate="no">angle</code></a>
+en radians. Nous utiliserons notre <code class="notranslate" translate="no">DegRadHelper</code> de l'<a href="textures.html">article sur les textures</a>
+pour présenter une interface utilisateur en
+degrés.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">gui.add(new DegRadHelper(light, 'angle'), 'value', 0, 90).name('angle').onChange(updateLight);
 </pre>
-<p>Le cône intérieur est défini en paramétrant la propriété <a href="/docs/#api/en/lights/SpotLight#penumbra"><code class="notranslate" translate="no">penumbra</code></a> en pourcentage du cône extérieur. En d'autres termes, lorsque la pénombre est de 0, le cône intérieur a la même taille (0 = aucune différence) que le cône extérieur. Lorsque la pénombre est de 1, la lumière s'estompe en partant du centre du cône jusqu'au cône extérieur. Lorsque la pénombre est de 0,5, la lumière s'estompe à partir de 50 % entre le centre et le cône extérieur.</p>
+<p>Le cône intérieur est défini en réglant la propriété <a href="/docs/#api/en/lights/SpotLight#penumbra"><code class="notranslate" translate="no">pénombre</code></a>
+comme un pourcentage à partir du cône extérieur. En d'autres termes, quand <code class="notranslate" translate="no">penumbra</code> est 0, alors le
+cône intérieur a la même taille (0 = aucune différence) que le cône extérieur. Quand la
+<code class="notranslate" translate="no">penumbra</code> est 1, alors la lumière s'estompe en partant du centre du cône jusqu'au
+cône extérieur. Quand <code class="notranslate" translate="no">penumbra</code> est 0,5, alors la lumière s'estompe en partant de 50 % entre
+le centre du cône extérieur.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">gui.add(light, 'penumbra', 0, 1, 0.01);
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-spot-w-helper.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-spot-w-helper.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-spot-w-helper.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Remarquez qu'avec <code class="notranslate" translate="no">penumbra</code> par défaut à 0, le projecteur a un bord très net alors que lorsque vous l'ajustez à 1, le bord devient flou.</p>
-<p>Il peut être difficile de voir le <em>cône</em> des spotlight. C'est parce qu'il se trouve sous le sol. Raccourcissez la distance à environ 5 et vous verrez l'extrémité ouverte du cône.</p>
+<p>Remarquez qu'avec la <code class="notranslate" translate="no">penumbra</code> par défaut de 0, le projecteur a un bord très net,
+tandis que lorsque vous ajustez la <code class="notranslate" translate="no">penumbra</code> vers 1, le bord devient flou.</p>
+<p>Il peut être difficile de voir le <em>cône</em> du projecteur. La raison est qu'il est
+en dessous du sol. Raccourcissez la distance à environ 5 et vous verrez l'extrémité ouverte
+du cône.</p>
 <h2 id="-rectarealight-"><a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a></h2>
-<p>Il existe un autre type de lumière, la <a href="https://threejs.org/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a>, qui ressemble à une zone de lumière rectangulaire comme une longue lampe fluorescente ou peut-être une lucarne dépolie dans un plafond.</p>
-<p>Le <a href="https://threejs.org/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> ne fonctionne qu'avec les <a href="https://threejs.org/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> et <a href="https://threejs.org/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a> donc changeons tous nos matériaux en <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a></p>
+<p>Il existe un autre type de lumière, la <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">Lumière Rectangulaire</code></a>, qui représente
+exactement ce à quoi cela ressemble : une zone rectangulaire de lumière, comme un long
+néon fluorescent ou peut-être une lucarne dépolie dans un plafond.</p>
+<p>La <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">Lumière Rectangulaire</code></a> ne fonctionne qu'avec les matériaux <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> et
+<a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a>, nous allons donc changer tous nos matériaux en <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">  ...
 
   const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
@@ -343,7 +419,8 @@ en radians. Utilisons notre <code class="notranslate" translate="no">DegRadHelpe
   scene.add(mesh);
 }
 </pre>
-<p>Pour utiliser <a href="https://threejs.org/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> nous devons importer <a href="https://threejs.org/docs/#api/en/helpers/RectAreaLightHelper"><code class="notranslate" translate="no">RectAreaLightHelper</code></a> pour nous aider à voir la lumière.</p>
+<p>Pour utiliser la <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">Lumière Rectangulaire</code></a>, nous devons inclure des données optionnelles supplémentaires de three.js et nous inclurons le
+<a href="/docs/#api/en/helpers/RectAreaLightHelper"><code class="notranslate" translate="no">Helper de Lumière Rectangulaire</code></a> pour nous aider à visualiser la lumière</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
 +import {RectAreaLightUniformsLib} from 'three/addons/lights/RectAreaLightUniformsLib.js';
 +import {RectAreaLightHelper} from 'three/addons/helpers/RectAreaLightHelper.js';
@@ -354,8 +431,9 @@ en radians. Utilisons notre <code class="notranslate" translate="no">DegRadHelpe
   const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
 +  RectAreaLightUniformsLib.init();
 </pre>
-<p>Si vous oubliez les données, la lumière fonctionnera toujours, mais de manière bizarre, alors n'oubliez pas d'inclure les données supplémentaires.</p>
-<p>Maintenant, nous pouvons créer la lumière</p>
+<p>Si vous oubliez les données, la lumière fonctionnera toujours mais elle aura un aspect étrange, alors
+n'oubliez pas d'inclure les données supplémentaires.</p>
+<p>Nous pouvons maintenant créer la lumière</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
 *const intensity = 5;
 +const width = 12;
@@ -368,29 +446,34 @@ scene.add(light);
 *const helper = new RectAreaLightHelper(light);
 *light.add(helper);
 </pre>
-<p>Une chose à noter est que contrairement au <a href="https://threejs.org/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> et à la <a href="https://threejs.org/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>, la <a href="https://threejs.org/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> n'utilise pas de cible. Elle utilise juste sa rotation. Une autre chose à noter est que le 'helper' doit être un enfant de la lumière. Ce n'est pas un enfant de la scène comme les autres 'helpers'.</p>
-<p>Ajustons-la également à lil-gui. Nous allons le faire pour que nous puissions faire pivoter la lumière et ajuster sa <code class="notranslate" translate="no">width</code> et sa <code class="notranslate" translate="no">height</code></p>
+<p>Une chose à noter est que, contrairement à la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">Lumière Directionnelle</code></a> et au <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">Projecteur</code></a>, la
+<a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">Lumière Rectangulaire</code></a> n'utilise pas de cible. Elle utilise simplement sa rotation. Une autre chose
+à noter est que l'assistant doit être un enfant de la lumière. Il n'est pas un enfant de la
+scène comme les autres assistants.</p>
+<p>Ajustons également l'interface GUI. Nous allons faire en sorte de pouvoir faire pivoter la lumière et ajuster
+sa <code class="notranslate" translate="no">width</code> et sa <code class="notranslate" translate="no">height</code></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('couleur');
 gui.add(light, 'intensity', 0, 10, 0.01);
 gui.add(light, 'width', 0, 20);
 gui.add(light, 'height', 0, 20);
-gui.add(new DegRadHelper(light.rotation, 'x'), 'value', -180, 180).name('x rotation');
-gui.add(new DegRadHelper(light.rotation, 'y'), 'value', -180, 180).name('y rotation');
-gui.add(new DegRadHelper(light.rotation, 'z'), 'value', -180, 180).name('z rotation');
+gui.add(new DegRadHelper(light.rotation, 'x'), 'value', -180, 180).name('rotation x');
+gui.add(new DegRadHelper(light.rotation, 'y'), 'value', -180, 180).name('rotation y');
+gui.add(new DegRadHelper(light.rotation, 'z'), 'value', -180, 180).name('rotation z');
 
 makeXYZGUI(gui, light.position, 'position');
 </pre>
-<p>Et voici ce que ça donne.</p>
+<p>Et voici cela.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-rectarea.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/lights-rectarea.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/lights-rectarea.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-
-<p>Il est important de noter que chaque lumière que vous ajoutez à la scène ralentit la vitesse à laquelle Three.js rend la scène, vous devez donc toujours essayer d'en utiliser le moins possible pour atteindre vos objectifs.</p>
-<p>Passons maintenant à <a href="cameras.html">la gestion des caméras</a>.</p>
+<p>Il est important de noter que chaque lumière que vous ajoutez à la scène ralentit la vitesse
+de rendu de la scène par three.js, vous devriez donc toujours essayer d'en
+utiliser le moins possible pour atteindre vos objectifs.</p>
+<p>Ensuite, passons à <a href="cameras.html">la gestion des caméras</a>.</p>
 <p><canvas id="c"></canvas></p>
 <script type="module" src="../resources/threejs-lights.js"></script>
 
@@ -404,4 +487,4 @@ makeXYZGUI(gui, light.position, 'position');
 
 
 
-</body></html>
+</body></html>

+ 551 - 5
manual/fr/load-gltf.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Loading a .GLTF File</title>
+    <title>Chargement d'un fichier .GLTF</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Loading a .GLTF File">
+    <meta name="twitter:title" content="Three.js – Chargement d'un fichier .GLTF">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,558 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Loading a .GLTF File</h1>
+        <h1>Chargement d'un fichier .GLTF</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/load-gltf.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Dans une leçon précédente, nous avons <a href="load-obj.html">chargé un fichier .OBJ</a>. Si vous ne l'avez pas lue, vous pourriez vouloir la consulter d'abord.</p>
+<p>Comme indiqué là-bas, le format de fichier .OBJ est très ancien et assez simple. Il ne fournit aucun graphe de scène, donc tout ce qui est chargé est un seul grand maillage. Il a été conçu principalement comme un moyen simple de passer des données entre des éditeurs 3D.</p>
+<p><a href="https://github.com/KhronosGroup/glTF">Le format gLTF</a> est en réalité un format conçu dès le départ pour être utilisé pour afficher des graphiques. Les formats 3D peuvent être divisés en 3 ou 4 types de base.</p>
+<ul>
+<li><p>Formats d'éditeurs 3D</p>
+<p>Ce sont des formats spécifiques à une seule application. .blend (Blender), .max (3d Studio Max), .mb et .ma (Maya), etc...</p>
+</li>
+<li><p>Formats d'échange</p>
+<p>Ce sont des formats comme .OBJ, .DAE (Collada), .FBX. Ils sont conçus pour aider à échanger des informations entre les éditeurs 3D. En tant que tels, ils sont généralement beaucoup plus volumineux que nécessaire avec des informations supplémentaires utilisées uniquement à l'intérieur des éditeurs 3D.</p>
+</li>
+<li><p>Formats d'application</p>
+<p>Ceux-ci sont généralement spécifiques à certaines applications, généralement des jeux.</p>
+</li>
+<li><p>Formats de transmission</p>
+<p>gLTF pourrait être le premier véritable format de transmission. Je suppose que VRML pourrait être considéré comme tel, mais VRML était en réalité un très mauvais format.</p>
+<p>gLTF est conçu pour bien faire certaines choses que tous ces autres formats ne font pas</p>
+<ol>
+<li><p>Être petit pour la transmission</p>
+<p>Par exemple, cela signifie qu'une grande partie de leurs données volumineuses, comme les sommets, est stockée en binaire. Lorsque vous téléchargez un fichier .gLTF, ces données peuvent être téléchargées sur le GPU sans aucun traitement. Elles sont prêtes telles quelles. C'est en contraste avec, par exemple, VRML, .OBJ ou .DAE où les sommets sont stockés sous forme de texte et doivent être analysés. Les positions de sommets en texte peuvent facilement être 3 à 5 fois plus volumineuses qu'en binaire.</p>
+</li>
+<li><p>Être prêt à être rendu</p>
+<p>C'est encore différent des autres formats, sauf peut-être les formats d'application. Les données d'un fichier glTF sont destinées à être rendues, pas éditées. Les données qui ne sont pas importantes pour le rendu ont généralement été supprimées. Les polygones ont été convertis en triangles. Les matériaux ont des valeurs connues qui sont censées fonctionner partout.</p>
+</li>
+</ol>
+</li>
+</ul>
+<p>gLTF a été spécifiquement conçu pour que vous puissiez télécharger un fichier glTF et l'afficher avec un minimum de problèmes. Croisons les doigts pour que ce soit vraiment le cas, car aucun autre format n'a été capable de faire cela.</p>
+<p>Je n'étais pas vraiment sûr de ce que je devais montrer. À un certain niveau, le chargement et l'affichage d'un fichier gLTF sont plus simples qu'un fichier .OBJ. Contrairement à un fichier .OBJ, les matériaux font directement partie du format. Cela dit, j'ai pensé que je devais au moins en charger un et je pense qu'examiner les problèmes que j'ai rencontrés pourrait fournir de bonnes informations.</p>
+<p>En cherchant sur le net, j'ai trouvé <a href="https://sketchfab.com/models/edd1c604e1e045a0a2a552ddd9a293e6">cette ville low-poly</a> par <a href="https://sketchfab.com/antonmoek">antonmoek</a> qui semblait, si nous avons de la chance, faire un bon exemple.</p>
+<div class="threejs_center"><img src="../resources/images/cartoon_lowpoly_small_city_free_pack.jpg"></div>
+
+<p>En partant d'<a href="load-obj.html">un exemple de l'article sur les fichiers .OBJ</a>, j'ai supprimé le code de chargement de .OBJ et je l'ai remplacé par le code de chargement de .GLTF.</p>
+<p>L'ancien code .OBJ était</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mtlLoader = new MTLLoader();
+mtlLoader.loadMtl('resources/models/windmill/windmill-fixed.mtl', (mtl) =&gt; {
+  mtl.preload();
+  mtl.materials.Material.side = THREE.DoubleSide;
+  objLoader.setMaterials(mtl);
+  objLoader.load('resources/models/windmill/windmill.obj', (event) =&gt; {
+    const root = event.detail.loaderRootNode;
+    scene.add(root);
+    ...
+  });
+});
+</pre>
+<p>Le nouveau code .GLTF est</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const gltfLoader = new GLTFLoader();
+  const url = 'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf';
+  gltfLoader.load(url, (gltf) =&gt; {
+    const root = gltf.scene;
+    scene.add(root);
+    ...
+  });
+</pre>
+<p>J'ai gardé le code de cadrage automatique comme auparavant.</p>
+<p>Nous devons également inclure le <a href="/docs/#examples/loaders/GLTFLoader"><code class="notranslate" translate="no">GLTFLoader</code></a> et nous pouvons nous débarrasser du <a href="/docs/#examples/loaders/OBJLoader"><code class="notranslate" translate="no">OBJLoader</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">-import {LoaderSupport} from 'three/addons/loaders/LoaderSupport.js';
+-import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';
+-import {MTLLoader} from 'three/addons/loaders/MTLLoader.js';
++import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
+</pre>
+<p>Et en exécutant cela, nous obtenons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-gltf.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-gltf.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Magie ! Ça fonctionne tout seul, textures comprises.</p>
+<p>Ensuite, je voulais voir si je pouvais animer les voitures qui circulent, j'ai donc eu besoin de vérifier si la scène avait les voitures comme entités séparées et si elles étaient configurées d'une manière que je pouvais utiliser.</p>
+<p>J'ai écrit du code pour afficher le graphe de scène dans la <a href="debugging-javascript.html">console JavaScript</a>.</p>
+<p>Voici le code pour imprimer le graphe de scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function dumpObject(obj, lines = [], isLast = true, prefix = '') {
+  const localPrefix = isLast ? '└─' : '├─';
+  lines.push(`${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]`);
+  const newPrefix = prefix + (isLast ? '  ' : '│ ');
+  const lastNdx = obj.children.length - 1;
+  obj.children.forEach((child, ndx) =&gt; {
+    const isLast = ndx === lastNdx;
+    dumpObject(child, lines, isLast, newPrefix);
+  });
+  return lines;
+}
+</pre>
+<p>Et je l'ai appelée juste après avoir chargé la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gltfLoader = new GLTFLoader();
+gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {
+  const root = gltf.scene;
+  scene.add(root);
+  console.log(dumpObject(root).join('\n'));
+</pre>
+<p><a href="../examples/load-gltf-dump-scenegraph.html">En exécutant cela</a>, j'ai obtenu cette liste</p>
+<pre class="prettyprint showlinemods notranslate lang-text" translate="no">OSG_Scene [Scene]
+  └─RootNode_(gltf_orientation_matrix) [Object3D]
+    └─RootNode_(model_correction_matrix) [Object3D]
+      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
+        └─RootNode [Object3D]
+          │ ...
+          ├─Cars [Object3D]
+          │ ├─CAR_03_1 [Object3D]
+          │ │ └─CAR_03_1_World_ap_0 [Mesh]
+          │ ├─CAR_03 [Object3D]
+          │ │ └─CAR_03_World_ap_0 [Mesh]
+          │ ├─Car_04 [Object3D]
+          │ │ └─Car_04_World_ap_0 [Mesh]
+          │ ├─CAR_03_2 [Object3D]
+          │ │ └─CAR_03_2_World_ap_0 [Mesh]
+          │ ├─Car_04_1 [Object3D]
+          │ │ └─Car_04_1_World_ap_0 [Mesh]
+          │ ├─Car_04_2 [Object3D]
+          │ │ └─Car_04_2_World_ap_0 [Mesh]
+          │ ├─Car_04_3 [Object3D]
+          │ │ └─Car_04_3_World_ap_0 [Mesh]
+          │ ├─Car_04_4 [Object3D]
+          │ │ └─Car_04_4_World_ap_0 [Mesh]
+          │ ├─Car_08_4 [Object3D]
+          │ │ └─Car_08_4_World_ap8_0 [Mesh]
+          │ ├─Car_08_3 [Object3D]
+          │ │ └─Car_08_3_World_ap9_0 [Mesh]
+          │ ├─Car_04_1_2 [Object3D]
+          │ │ └─Car_04_1_2_World_ap_0 [Mesh]
+          │ ├─Car_08_2 [Object3D]
+          │ │ └─Car_08_2_World_ap11_0 [Mesh]
+          │ ├─CAR_03_1_2 [Object3D]
+          │ │ └─CAR_03_1_2_World_ap_0 [Mesh]
+          │ ├─CAR_03_2_2 [Object3D]
+          │ │ └─CAR_03_2_2_World_ap_0 [Mesh]
+          │ ├─Car_04_2_2 [Object3D]
+          │ │ └─Car_04_2_2_World_ap_0 [Mesh]
+          ...
+</pre>
+<p>À partir de cela, nous pouvons voir que toutes les voitures se trouvent sous un parent appelé <code class="notranslate" translate="no">"Cars"</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-text" translate="no">*          ├─Cars [Object3D]
+          │ ├─CAR_03_1 [Object3D]
+          │ │ └─CAR_03_1_World_ap_0 [Mesh]
+          │ ├─CAR_03 [Object3D]
+          │ │ └─CAR_03_World_ap_0 [Mesh]
+          │ ├─Car_04 [Object3D]
+          │ │ └─Car_04_World_ap_0 [Mesh]
+</pre>
+<p>Donc, comme test simple, j'ai pensé que j'essaierais juste de faire tourner tous les enfants du nœud "Cars" autour de leur axe Y.</p>
+<p>J'ai cherché le nœud "Cars" après avoir chargé la scène et j'ai sauvegardé le résultat.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+let cars;
+{
+  const gltfLoader = new GLTFLoader();
+  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {
+    const root = gltf.scene;
+    scene.add(root);
++    cars = root.getObjectByName('Cars');
+</pre>
+<p>Ensuite, dans la fonction <code class="notranslate" translate="no">render</code>, nous pouvons simplement définir la rotation de chaque enfant de <code class="notranslate" translate="no">cars</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function render(time) {
++  time *= 0.001;  // convertir en secondes
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
++  if (cars) {
++    for (const car of cars.children) {
++      car.rotation.y = time;
++    }
++  }
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Et nous obtenons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-gltf-rotate-cars.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-gltf-rotate-cars.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Hmmm, il semble que malheureusement cette scène n'a pas été conçue pour animer les voitures, car leurs origines ne sont pas configurées à cette fin. Les camions tournent dans la mauvaise direction.</p>
+<p>Cela soulève un point important : si vous allez faire quelque chose en 3D, vous devez planifier à l'avance et concevoir vos éléments de manière à ce que leurs origines soient aux bons endroits, qu'ils aient la bonne échelle, etc.</p>
+<p>Comme je ne suis pas un artiste et que je ne connais pas très bien Blender, je vais bricoler cet exemple. Nous allons prendre chaque voiture et la faire appartenir à un autre <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Nous allons ensuite déplacer ces objets <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> pour déplacer les voitures, mais séparément, nous pouvons définir l'<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> d'origine de la voiture pour la réorienter afin qu'elle soit à peu près là où nous en avons vraiment besoin.</p>
+<p>En regardant à nouveau la liste du graphe de scène, il semble qu'il n'y ait en réalité que 3 types de voitures : "Car_08", "CAR_03" et "Car_04". Espérons que chaque type de voiture fonctionnera avec les mêmes ajustements.</p>
+<p>J'ai écrit ce code pour passer en revue chaque voiture, la faire appartenir à un nouvel <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>, faire appartenir ce nouvel <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> à la scène, appliquer des paramètres par *type* de voiture pour corriger son orientation, et ajouter le nouvel <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> à un tableau <code class="notranslate" translate="no">cars</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-let cars;
++const cars = [];
+{
+  const gltfLoader = new GLTFLoader();
+  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {
+    const root = gltf.scene;
+    scene.add(root);
+
+-    cars = root.getObjectByName('Cars');
++    const loadedCars = root.getObjectByName('Cars');
++    const fixes = [
++      { prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },
++      { prefix: 'CAR_03', rot: [0, Math.PI, 0], },
++      { prefix: 'Car_04', rot: [0, Math.PI, 0], },
++    ];
++
++    root.updateMatrixWorld();
++    for (const car of loadedCars.children.slice()) {
++      const fix = fixes.find(fix =&gt; car.name.startsWith(fix.prefix));
++      const obj = new THREE.Object3D();
++      car.getWorldPosition(obj.position);
++      car.position.set(0, 0, 0);
++      car.rotation.set(...fix.rot);
++      obj.add(car);
++      scene.add(obj);
++      cars.push(obj);
++    }
+     ...
+</pre>
+<p>Cela corrige l'orientation des voitures.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-gltf-rotate-cars-fixed.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-gltf-rotate-cars-fixed.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Maintenant, faisons-les rouler.</p>
+<p>Faire même un simple système de conduite est trop pour ce post, mais il semble qu'au lieu de cela, nous pourrions simplement créer un chemin alambiqué qui parcourt toutes les routes, puis placer les voitures sur ce chemin. Voici une image de Blender à mi-chemin de la construction du chemin.</p>
+<div class="threejs_center"><img src="../resources/images/making-path-for-cars.jpg" style="width: 1094px"></div>
+
+<p>J'avais besoin d'un moyen d'obtenir les données de ce chemin depuis Blender. Heureusement, j'ai pu sélectionner juste mon chemin et exporter en .OBJ en cochant "write nurbs".</p>
+<div class="threejs_center"><img src="../resources/images/blender-export-obj-write-nurbs.jpg" style="width: 498px"></div>
+
+<p>En ouvrant le fichier .OBJ, j'ai pu obtenir une liste de points que j'ai formatée ainsi</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const controlPoints = [
+  [1.118281, 5.115846, -3.681386],
+  [3.948875, 5.115846, -3.641834],
+  [3.960072, 5.115846, -0.240352],
+  [3.985447, 5.115846, 4.585005],
+  [-3.793631, 5.115846, 4.585006],
+  [-3.826839, 5.115846, -14.736200],
+  [-14.542292, 5.115846, -14.765865],
+  [-14.520929, 5.115846, -3.627002],
+  [-5.452815, 5.115846, -3.634418],
+  [-5.467251, 5.115846, 4.549161],
+  [-13.266233, 5.115846, 4.567083],
+  [-13.250067, 5.115846, -13.499271],
+  [4.081842, 5.115846, -13.435463],
+  [4.125436, 5.115846, -5.334928],
+  [-14.521364, 5.115846, -5.239871],
+  [-14.510466, 5.115846, 5.486727],
+  [5.745666, 5.115846, 5.510492],
+  [5.787942, 5.115846, -14.728308],
+  [-5.423720, 5.115846, -14.761919],
+  [-5.373599, 5.115846, -3.704133],
+  [1.004861, 5.115846, -3.641834],
+];
+</pre>
+<p>THREE.js possède des classes de courbes. La <a href="/docs/#api/en/extras/curves/CatmullRomCurve3"><code class="notranslate" translate="no">CatmullRomCurve3</code></a> semblait pouvoir fonctionner. L'intérêt de ce type de courbe est qu'elle essaie de créer une courbe lisse passant par les points.</p>
+<p>En fait, l'insertion directe de ces points générera une courbe comme celle-ci</p>
+<div class="threejs_center"><img src="../resources/images/car-curves-before.png" style="width: 400px"></div>
+
+<p>mais nous voulons des coins plus marqués. Il semblait que si nous calculions des points supplémentaires, nous pourrions obtenir ce que nous voulons. Pour chaque paire de points, nous allons calculer un point à 10 % du chemin entre les 2 points et un autre à 90 % du chemin entre les 2 points, et passer le résultat à <a href="/docs/#api/en/extras/curves/CatmullRomCurve3"><code class="notranslate" translate="no">CatmullRomCurve3</code></a>.</p>
+<p>Cela nous donnera une courbe comme celle-ci</p>
+<div class="threejs_center"><img src="../resources/images/car-curves-after.png" style="width: 400px"></div>
+
+<p>Voici le code pour créer la courbe</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">let curve;
+let curveObject;
+{
+  const controlPoints = [
+    [1.118281, 5.115846, -3.681386],
+    [3.948875, 5.115846, -3.641834],
+    [3.960072, 5.115846, -0.240352],
+    [3.985447, 5.115846, 4.585005],
+    [-3.793631, 5.115846, 4.585006],
+    [-3.826839, 5.115846, -14.736200],
+    [-14.542292, 5.115846, -14.765865],
+    [-14.520929, 5.115846, -3.627002],
+    [-5.452815, 5.115846, -3.634418],
+    [-5.467251, 5.115846, 4.549161],
+    [-13.266233, 5.115846, 4.567083],
+    [-13.250067, 5.115846, -13.499271],
+    [4.081842, 5.115846, -13.435463],
+    [4.125436, 5.115846, -5.334928],
+    [-14.521364, 5.115846, -5.239871],
+    [-14.510466, 5.115846, 5.486727],
+    [5.745666, 5.115846, 5.510492],
+    [5.787942, 5.115846, -14.728308],
+    [-5.423720, 5.115846, -14.761919],
+    [-5.373599, 5.115846, -3.704133],
+    [1.004861, 5.115846, -3.641834],
+  ];
+  const p0 = new THREE.Vector3();
+  const p1 = new THREE.Vector3();
+  curve = new THREE.CatmullRomCurve3(
+    controlPoints.map((p, ndx) =&gt; {
+      p0.set(...p);
+      p1.set(...controlPoints[(ndx + 1) % controlPoints.length]);
+      return [
+        (new THREE.Vector3()).copy(p0),
+        (new THREE.Vector3()).lerpVectors(p0, p1, 0.1),
+        (new THREE.Vector3()).lerpVectors(p0, p1, 0.9),
+      ];
+    }).flat(),
+    true,
+  );
+  {
+    const points = curve.getPoints(250);
+    const geometry = new THREE.BufferGeometry().setFromPoints(points);
+    const material = new THREE.LineBasicMaterial({color: 0xff0000});
+    curveObject = new THREE.Line(geometry, material);
+    scene.add(curveObject);
+  }
+}
+</pre>
+<p>La première partie de ce code crée une courbe. La deuxième partie de ce code génère 250 points à partir de la courbe, puis crée un objet pour afficher les lignes formées en connectant ces 250 points.</p>
+<p>En exécutant <a href="../examples/load-gltf-car-path.html">l'exemple</a>, je n'ai pas vu la courbe. Pour la rendre visible, je l'ai fait ignorer le test de profondeur et la rendre en dernier.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">    curveObject = new THREE.Line(geometry, material);
++    material.depthTest = false;
++    curveObject.renderOrder = 1;
+</pre>
+<p>Et c'est là que j'ai découvert qu'elle était beaucoup trop petite.</p>
+<div class="threejs_center"><img src="../resources/images/car-curves-too-small.png" style="width: 498px"></div>
+
+<p>En vérifiant la hiérarchie dans Blender, j'ai découvert que l'artiste avait mis à l'échelle le nœud parent de toutes les voitures.</p>
+<div class="threejs_center"><img src="../resources/images/cars-scale-0.01.png" style="width: 342px;"></div>
+
+<p>La mise à l'échelle est mauvaise pour les applications 3D en temps réel. Elle cause toutes sortes de problèmes et finit par être une source de frustration infinie lors de la création d'applications 3D en temps réel. Les artistes ne le savent souvent pas car il est si facile de mettre à l'échelle une scène entière dans un programme d'édition 3D, mais si vous décidez de créer une application 3D en temps réel, je vous suggère de demander à vos artistes de ne jamais rien mettre à l'échelle. S'ils modifient l'échelle, ils devraient trouver un moyen d'appliquer cette échelle aux sommets afin que, lorsqu'elle arrive dans votre application, vous puissiez ignorer l'échelle.</p>
+<p>Et, pas seulement l'échelle, dans ce cas, les voitures sont tournées et décalées par leur parent, le nœud <code class="notranslate" translate="no">Cars</code>. Cela rendra difficile au moment de l'exécution de déplacer les voitures dans l'espace mondial. Pour être clair, dans ce cas, nous voulons que les voitures circulent dans l'espace mondial, c'est pourquoi ces problèmes se posent. Si quelque chose est destiné à être manipulé dans un espace local, comme la lune tournant autour de la terre, c'est moins problématique.</p>
+<p>Pour en revenir à la fonction que nous avons écrite ci-dessus pour afficher le graphe de scène, affichons la position, la rotation et l'échelle de chaque nœud.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function dumpVec3(v3, precision = 3) {
++  return `${v3.x.toFixed(precision)}, ${v3.y.toFixed(precision)}, ${v3.z.toFixed(precision)}`;
++}
+
+function dumpObject(obj, lines, isLast = true, prefix = '') {
+  const localPrefix = isLast ? '└─' : '├─';
+  lines.push(`${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]`);
++  const dataPrefix = obj.children.length
++     ? (isLast ? '  │ ' : '│ │ ')
++     : (isLast ? '    ' : '│   ');
++  lines.push(`${prefix}${dataPrefix}  pos: ${dumpVec3(obj.position)}`);
++  lines.push(`${prefix}${dataPrefix}  rot: ${dumpVec3(obj.rotation)}`);
++  lines.push(`${prefix}${dataPrefix}  scl: ${dumpVec3(obj.scale)}`);
+  const newPrefix = prefix + (isLast ? '  ' : '│ ');
+  const lastNdx = obj.children.length - 1;
+  obj.children.forEach((child, ndx) =&gt; {
+    const isLast = ndx === lastNdx;
+    dumpObject(child, lines, isLast, newPrefix);
+  });
+  return lines;
+}
+</pre>
+<p>Et le résultat de <a href="../examples/load-gltf-dump-scenegraph-extra.html">l'exécution</a></p>
+<pre class="prettyprint showlinemods notranslate lang-text" translate="no">OSG_Scene [Scene]
+  │   pos: 0.000, 0.000, 0.000
+  │   rot: 0.000, 0.000, 0.000
+  │   scl: 1.000, 1.000, 1.000
+  └─RootNode_(gltf_orientation_matrix) [Object3D]
+    │   pos: 0.000, 0.000, 0.000
+    │   rot: -1.571, 0.000, 0.000
+    │   scl: 1.000, 1.000, 1.000
+    └─RootNode_(model_correction_matrix) [Object3D]
+      │   pos: 0.000, 0.000, 0.000
+      │   rot: 0.000, 0.000, 0.000
+      │   scl: 1.000, 1.000, 1.000
+      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
+        │   pos: 0.000, 0.000, 0.000
+        │   rot: 1.571, 0.000, 0.000
+        │   scl: 1.000, 1.000, 1.000
+        └─RootNode [Object3D]
+          │   pos: 0.000, 0.000, 0.000
+          │   rot: 0.000, 0.000, 0.000
+          │   scl: 1.000, 1.000, 1.000
+          ├─Cars [Object3D]
+*          │ │   pos: -369.069, -90.704, -920.159
+*          │ │   rot: 0.000, 0.000, 0.000
+*          │ │   scl: 1.000, 1.000, 1.000
+          │ ├─CAR_03_1 [Object3D]
+          │ │ │   pos: 22.131, 14.663, -475.071
+          │ │ │   rot: -3.142, 0.732, 3.142
+          │ │ │   scl: 1.500, 1.500, 1.500
+          │ │ └─CAR_03_1_World_ap_0 [Mesh]
+          │ │       pos: 0.000, 0.000, 0.000
+          │ │       rot: 0.000, 0.000, 0.000
+          │ │       scl: 1.000, 1.000, 1.000
+</pre>
+<p>Cela nous montre que l'<code class="notranslate" translate="no">Cars</code> dans la scène originale a vu sa rotation et son échelle supprimées et appliquées à ses enfants. Cela suggère que soit l'exportateur utilisé pour créer le fichier .GLTF a fait un travail spécial ici, soit plus probablement l'artiste a exporté une version différente du fichier que le fichier .blend correspondant, ce qui explique pourquoi les choses ne correspondent pas.</p>
+<p>La morale de l'histoire est que j'aurais probablement dû télécharger le fichier .blend et exporter moi-même. Avant d'exporter, j'aurais dû inspecter tous les nœuds principaux et supprimer toute transformation.</p>
+<p>Tous ces nœuds en haut</p>
+<pre class="prettyprint showlinemods notranslate lang-text" translate="no">OSG_Scene [Scene]
+  │   pos: 0.000, 0.000, 0.000
+  │   rot: 0.000, 0.000, 0.000
+  │   scl: 1.000, 1.000, 1.000
+  └─RootNode_(gltf_orientation_matrix) [Object3D]
+    │   pos: 0.000, 0.000, 0.000
+    │   rot: -1.571, 0.000, 0.000
+    │   scl: 1.000, 1.000, 1.000
+    └─RootNode_(model_correction_matrix) [Object3D]
+      │   pos: 0.000, 0.000, 0.000
+      │   rot: 0.000, 0.000, 0.000
+      │   scl: 1.000, 1.000, 1.000
+      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
+        │   pos: 0.000, 0.000, 0.000
+        │   rot: 1.571, 0.000, 0.000
+        │   scl: 1.000, 1.000, 1.000
+</pre>
+<p>sont également un gaspillage.</p>
+<p>Idéalement, la scène devrait se composer d'un seul nœud "racine" sans position, rotation ou échelle. Au moment de l'exécution, je pourrais alors retirer tous les enfants de cette racine et les faire appartenir à la scène elle-même. Il pourrait y avoir des enfants de la racine comme "Cars" qui m'aideraient à trouver toutes les voitures, mais idéalement, il n'aurait pas non plus de translation, rotation ou échelle afin que je puisse rattacher les voitures à la scène avec un minimum de travail.</p>
+<p>En tout cas, la solution la plus rapide, même si ce n'est peut-être pas la meilleure, est de simplement ajuster l'objet que nous utilisons pour visualiser la courbe.</p>
+<p>Voici ce que j'ai obtenu au final.</p>
+<p>D'abord, j'ai ajusté la position de la courbe et trouvé des valeurs qui semblaient fonctionner. Je l'ai ensuite cachée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const points = curve.getPoints(250);
+  const geometry = new THREE.BufferGeometry().setFromPoints(points);
+  const material = new THREE.LineBasicMaterial({color: 0xff0000});
+  curveObject = new THREE.Line(geometry, material);
++  curveObject.scale.set(100, 100, 100);
++  curveObject.position.y = -621;
++  curveObject.visible = false;
+  material.depthTest = false;
+  curveObject.renderOrder = 1;
+  scene.add(curveObject);
+}
+</pre>
+<p>Ensuite, j'ai écrit du code pour déplacer les voitures le long de la courbe. Pour chaque voiture, nous choisissons une position de 0 à 1 le long de la courbe et calculons un point dans l'espace mondial en utilisant l'<code class="notranslate" translate="no">curveObject</code> pour transformer le point. Nous choisissons ensuite un autre point légèrement plus loin sur la courbe. Nous définissons l'orientation de la voiture en utilisant <code class="notranslate" translate="no">lookAt</code> et plaçons la voiture au point médian entre les 2 points.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// créer 2 Vector3 que nous pouvons utiliser pour les calculs de chemin
+const carPosition = new THREE.Vector3();
+const carTarget = new THREE.Vector3();
+
+function render(time) {
+  ...
+
+-  for (const car of cars) {
+-    car.rotation.y = time;
+-  }
+
++  {
++    const pathTime = time * .01;
++    const targetOffset = 0.01;
++    cars.forEach((car, ndx) =&gt; {
++      // un nombre entre 0 et 1 pour espacer uniformément les voitures
++      const u = pathTime + ndx / cars.length;
++
++      // obtenir le premier point
++      curve.getPointAt(u % 1, carPosition);
++      carPosition.applyMatrix4(curveObject.matrixWorld);
++
++      // obtenir un deuxième point légèrement plus loin sur la courbe
++      curve.getPointAt((u + targetOffset) % 1, carTarget);
++      carTarget.applyMatrix4(curveObject.matrixWorld);
++
++      // placer la voiture au premier point (temporairement)
++      car.position.copy(carPosition);
++      // orienter la voiture vers le deuxième point
++      car.lookAt(carTarget);
++
++      // placer la voiture entre les 2 points
++      car.position.lerpVectors(carPosition, carTarget, 0.5);
++    });
++  }
+</pre>
+<p>et quand je l'ai exécuté, j'ai découvert que pour chaque type de voiture, leur hauteur au-dessus de leurs origines n'est pas définie de manière cohérente, et j'ai donc dû décaler chacune un peu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loadedCars = root.getObjectByName('Cars');
+const fixes = [
+-  { prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },
+-  { prefix: 'CAR_03', rot: [0, Math.PI, 0], },
+-  { prefix: 'Car_04', rot: [0, Math.PI, 0], },
++  { prefix: 'Car_08', y: 0,  rot: [Math.PI * .5, 0, Math.PI * .5], },
++  { prefix: 'CAR_03', y: 33, rot: [0, Math.PI, 0], },
++  { prefix: 'Car_04', y: 40, rot: [0, Math.PI, 0], },
+];
+
+root.updateMatrixWorld();
+for (const car of loadedCars.children.slice()) {
+  const fix = fixes.find(fix =&gt; car.name.startsWith(fix.prefix));
+  const obj = new THREE.Object3D();
+  car.getWorldPosition(obj.position);
+-  car.position.set(0, 0, 0);
++  car.position.set(0, fix.y, 0);
+  car.rotation.set(...fix.rot);
+  obj.add(car);
+  scene.add(obj);
+  cars.push(obj);
+}
+</pre>
+<p>Et le résultat.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-gltf-animated-cars.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-gltf-animated-cars.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Pas mal pour quelques minutes de travail.</p>
+<p>La dernière chose que je voulais faire est d'activer les ombres.</p>
+<p>Pour ce faire, j'ai pris tout le code GUI de l'exemple d'ombres <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> dans <a href="shadows.html">l'article sur les ombres</a> et l'ai collé dans notre dernier code.</p>
+<p>Ensuite, après le chargement, nous devons activer les ombres sur tous les objets.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const gltfLoader = new GLTFLoader();
+  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {
+    const root = gltf.scene;
+    scene.add(root);
+
++    root.traverse((obj) =&gt; {
++      if (obj.castShadow !== undefined) {
++        obj.castShadow = true;
++        obj.receiveShadow = true;
++      }
++    });
+</pre>
+<p>J'ai ensuite passé près de 4 heures à essayer de comprendre pourquoi les helpers d'ombre ne fonctionnaient pas. C'était parce que j'avais oublié d'activer les ombres avec</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">renderer.shadowMap.enabled = true;
+</pre>
+<p>😭</p>
+<p>J'ai ensuite ajusté les valeurs jusqu'à ce que la caméra d'ombre de notre <code class="notranslate" translate="no">DirectionLight</code> ait un frustum qui couvrait toute la scène. Voici les paramètres avec lesquels j'ai fini.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const color = 0xFFFFFF;
+  const intensity = 1;
+  const light = new THREE.DirectionalLight(color, intensity);
++  light.castShadow = true;
+*  light.position.set(-250, 800, -850);
+*  light.target.position.set(-550, 40, -450);
+
++  light.shadow.bias = -0.004;
++  light.shadow.mapSize.width = 2048;
++  light.shadow.mapSize.height = 2048;
+
+  scene.add(light);
+  scene.add(light.target);
++  const cam = light.shadow.camera;
++  cam.near = 1;
++  cam.far = 2000;
++  cam.left = -1500;
++  cam.right = 1500;
++  cam.top = 1500;
++  cam.bottom = -1500;
+...
+</pre>
+<p>et j'ai défini la couleur de fond en bleu clair.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+-scene.background = new THREE.Color('black');
++scene.background = new THREE.Color('#DEFEFF');
+</pre>
+<p>Et ... les ombres</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-gltf-shadows.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-gltf-shadows.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que parcourir ce projet a été utile et a montré de bons exemples de résolution de certains des problèmes liés au chargement d'un fichier avec un graphe de scène.</p>
+<p>Une chose intéressante est qu'en comparant le fichier .blend au fichier .gltf, le fichier .blend a plusieurs lumières mais elles ne sont pas des lumières après avoir été chargées dans la scène. Un fichier .GLTF est juste un fichier JSON, vous pouvez donc facilement regarder à l'intérieur. Il se compose de plusieurs tableaux de choses et chaque élément dans un tableau est référencé par index ailleurs. Bien qu'il y ait des extensions en cours de développement, elles soulignent un problème commun à presque tous les formats 3D. <strong>Ils ne peuvent jamais couvrir tous les cas</strong>.</p>
+<p>Il y a toujours un besoin de plus de données. Par exemple, nous avons exporté manuellement un chemin pour les voitures à suivre. Idéalement, cette information aurait pu être dans le fichier .GLTF, mais pour ce faire, nous aurions besoin d'écrire notre propre exportateur et de marquer d'une manière ou d'une autre les nœuds pour la façon dont nous voulons qu'ils soient exportés, ou utiliser un schéma de nommage ou quelque chose de similaire pour obtenir les données de l'outil que nous utilisons pour créer les données dans notre application.</p>
+<p>Tout cela est laissé comme un exercice pour le lecteur.</p>
 
         </div>
       </div>

+ 583 - 5
manual/fr/load-obj.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Loading a .OBJ File</title>
+    <title>Charger un fichier .OBJ</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Loading a .OBJ File">
+    <meta name="twitter:title" content="Three.js – Charger un fichier .OBJ">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,590 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Loading a .OBJ File</h1>
+        <h1>Charger un fichier .OBJ</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/load-obj.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>L'une des choses les plus courantes que les gens veulent faire avec three.js
+est de charger et d'afficher des modèles 3D. Un format courant est le format 3D
+.OBJ, alors essayons d'en charger un.</p>
+<p>En cherchant sur internet, j'ai trouvé <a href="https://www.blendswap.com/blends/view/69174">ce modèle 3D de moulin à vent sous licence CC-BY-NC 3.0</a> par <a href="https://www.blendswap.com/user/ahedov">ahedov</a>.</p>
+<div class="threejs_center"><img src="../resources/images/windmill-obj.jpg"></div>
+
+<p>J'ai téléchargé le fichier .blend à partir de ce site, je l'ai chargé dans <a href="https://blender.org">Blender</a>
+et l'ai exporté en tant que fichier .OBJ.</p>
+<div class="threejs_center"><img style="width: 827px;" src="../resources/images/windmill-export-as-obj.jpg"></div>
+
+<blockquote>
+<p>Note : Si vous n'avez jamais utilisé Blender, vous pourriez être surpris
+par le fait que Blender fait les choses différemment de presque tous les autres
+programmes que vous avez utilisés. Sachez simplement que vous pourriez avoir besoin de
+prendre le temps de lire quelques bases sur la navigation dans l'interface utilisateur de Blender.</p>
+<p>Permettez-moi d'ajouter également que les programmes 3D en général sont d'énormes bêtes avec
+des milliers de fonctionnalités. Ce sont parmi les logiciels les plus compliqués qui existent.
+Lorsque j'ai appris 3D Studio Max en 1996, j'ai lu 70% du manuel
+de 600 pages en y consacrant quelques heures par jour pendant environ 3 semaines. Cela a
+porté ses fruits, car lorsque j'ai appris Maya quelques années plus tard, certaines des leçons
+apprises auparavant étaient applicables à Maya. Alors, sachez simplement que si vous
+voulez vraiment pouvoir utiliser un logiciel 3D pour créer des assets 3D
+ou pour modifier des assets existants, inscrivez-le à votre emploi du temps et réservez
+du temps pour vraiment suivre quelques leçons.</p>
+</blockquote>
+<p>Dans tous les cas, j'ai utilisé ces options d'exportation</p>
+<div class="threejs_center"><img style="width: 239px;" src="../resources/images/windmill-export-options.jpg"></div>
+
+<p>Essayons de l'afficher !</p>
+<p>Je suis parti de l'exemple d'éclairage directionnel de
+<a href="lights.html">l'article sur les lumières</a> et je l'ai combiné avec
+l'exemple d'éclairage hémisphérique, j'ai donc fini avec une
+<a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> et une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>. J'ai également supprimé tout ce qui concerne l'interface utilisateur
+pour régler les lumières. J'ai également supprimé le cube et la sphère
+qui étaient ajoutés à la scène.</p>
+<p>À partir de là, la première chose à faire est d'inclure le chargeur <a href="/docs/#examples/loaders/OBJLoader"><code class="notranslate" translate="no">OBJLoader</code></a>
+dans notre script.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';
+</pre>
+<p>Ensuite, pour charger le fichier .OBJ, nous créons une instance de <a href="/docs/#examples/loaders/OBJLoader"><code class="notranslate" translate="no">OBJLoader</code></a>,
+lui passons l'URL de notre fichier .OBJ, et ajoutons un rappel qui
+ajoute le modèle chargé à notre scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const objLoader = new OBJLoader();
+  objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {
+    scene.add(root);
+  });
+}
+</pre>
+<p>Si nous exécutons cela, que se passe-t-il ?</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-no-materials.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-no-materials.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Eh bien, c'est presque ça, mais nous obtenons des erreurs concernant les matériaux,
+car nous n'avons pas donné de matériaux à la scène et les fichiers .OBJ n'ont pas de
+paramètres de matériaux. </p>
+<p>Le chargeur .OBJ peut recevoir un
+objet de paires nom / matériau. Lorsqu'il charge le fichier .OBJ,
+pour chaque nom de matériau qu'il trouve, il cherchera le matériau correspondant
+dans la carte des matériaux définie sur le chargeur. S'il trouve un
+matériau correspondant au nom, il utilisera ce matériau. Sinon,
+il utilisera le matériau par défaut du chargeur.</p>
+<p>Parfois, les fichiers .OBJ sont accompagnés d'un fichier .MTL qui définit
+les matériaux. Dans notre cas, l'exportateur a également créé un fichier .MTL.
+Le format .MTL est du texte brut (ASCII), il est donc facile à examiner.
+En le regardant ici</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no"># Blender MTL File: 'windmill_001.blend'
+# Material Count: 2
+
+newmtl Material
+Ns 0.000000
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.000000 0.000000 0.000000
+Ke 0.000000 0.000000 0.000000
+Ni 1.000000
+d 1.000000
+illum 1
+map_Kd windmill_001_lopatky_COL.jpg
+map_Bump windmill_001_lopatky_NOR.jpg
+
+newmtl windmill
+Ns 0.000000
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.000000 0.000000 0.000000
+Ke 0.000000 0.000000 0.000000
+Ni 1.000000
+d 1.000000
+illum 1
+map_Kd windmill_001_base_COL.jpg
+map_Bump windmill_001_base_NOR.jpg
+map_Ns windmill_001_base_SPEC.jpg
+</pre>
+<p>Nous pouvons voir qu'il y a 2 matériaux référençant 5 textures jpg,
+mais où sont les fichiers de texture ?</p>
+<div class="threejs_center"><img style="width: 757px;" src="../resources/images/windmill-exported-files.png"></div>
+
+<p>Tout ce que nous avons obtenu était un fichier .OBJ et un fichier .MTL.</p>
+<p>Au moins pour ce modèle, il s'avère que les textures sont intégrées
+dans le fichier .blend que nous avons téléchargé. Nous pouvons demander à Blender
+d'exporter ces fichiers en choisissant <strong>Fichier->Données externes->Désintégrer tout en fichiers (File->External Data->Unpack All Into Files)</strong></p>
+<div class="threejs_center"><img style="width: 828px;" src="../resources/images/windmill-export-textures.jpg"></div>
+
+<p>puis en choisissant <strong>Écrire les fichiers dans le répertoire actuel (Write Files to Current Directory)</strong></p>
+<div class="threejs_center"><img style="width: 828px;" src="../resources/images/windmill-overwrite.jpg"></div>
+
+<p>Cela finit par écrire les fichiers dans le même dossier que le fichier .blend,
+dans un sous-dossier appelé <strong>textures</strong>.</p>
+<div class="threejs_center"><img style="width: 758px;" src="../resources/images/windmill-exported-texture-files.png"></div>
+
+<p>Maintenant que les textures sont disponibles, nous pouvons charger le fichier .MTL.</p>
+<p>D'abord, nous devons inclure le <a href="/docs/#examples/loaders/MTLLoader"><code class="notranslate" translate="no">MTLLoader</code></a> ;</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';
++import {MTLLoader} from 'three/addons/loaders/MTLLoader.js';
+</pre>
+<p>Ensuite, nous chargeons d'abord le fichier .MTL. Une fois le chargement terminé,
+nous ajoutons les matériaux tout juste chargés au <a href="/docs/#examples/loaders/OBJLoader"><code class="notranslate" translate="no">OBJLoader</code></a> lui-même via la méthode <code class="notranslate" translate="no">setMaterials</code>,
+puis nous chargeons le fichier .OBJ.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
++  const mtlLoader = new MTLLoader();
++  mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {
++    mtl.preload();
++    objLoader.setMaterials(mtl);
+    objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {
+      scene.add(root);
+    });
++  });
+}
+</pre>
+<p>Et si nous essayons cela...</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-materials.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-materials.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Notez que si vous faites tourner le modèle, vous verrez le tissu du moulin disparaître</p>
+<div class="threejs_center"><img style="width: 528px;" src="../resources/images/windmill-missing-cloth.jpg"></div>
+
+<p>Nous devons rendre le matériau des pales double face,
+ce que nous avons vu dans <a href="materials.html">l'article sur les matériaux</a>.
+Il n'y a pas de moyen facile de corriger cela dans le fichier .MTL.
+Spontanément, je peux penser à 3 façons de corriger cela.</p>
+<ol>
+<li><p>Parcourir tous les matériaux après les avoir chargés et les rendre tous double face.</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const mtlLoader = new MTLLoader();
+ mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {
+   mtl.preload();
+   for (const material of Object.values(mtl.materials)) {
+     material.side = THREE.DoubleSide;
+   }
+   ...
+</pre><p>Cette solution fonctionne, mais idéalement, nous ne voulons que les matériaux qui ont besoin
+d'être double face soient double face, car dessiner en double face
+est plus lent qu'en simple face.</p>
+</li>
+<li><p>Définir manuellement un matériau spécifique</p>
+<p>En regardant dans le fichier .MTL, il y a 2 matériaux. L'un s'appelle <code class="notranslate" translate="no">"windmill"</code>
+et l'autre s'appelle <code class="notranslate" translate="no">"Material"</code>. Par essais et erreurs, j'ai découvert
+que les pales utilisent le matériau appelé <code class="notranslate" translate="no">"Material"</code>, nous pourrions donc le définir
+spécifiquement </p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const mtlLoader = new MTLLoader();
+ mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {
+   mtl.preload();
+   mtl.materials.Material.side = THREE.DoubleSide;
+   ...
+</pre></li>
+<li><p>Réalisant que le fichier .MTL est limité, nous pourrions simplement ne pas l'utiliser
+et créer nous-mêmes les matériaux à la place.</p>
+<p>Dans ce cas, nous devrions rechercher l'objet <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> après
+avoir chargé le fichier obj.</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {
+   const materials = {
+     Material: new THREE.MeshPhongMaterial({...}),
+     windmill: new THREE.MeshPhongMaterial({...}),
+   };
+   root.traverse(node =&gt; {
+     const material = materials[node.material?.name];
+     if (material) {
+       node.material = material;
+     }
+   })
+   scene.add(root);
+ });
+</pre></li>
+</ol>
+<p>À vous de choisir laquelle vous préférez. 1 est la plus simple. 3 est la plus flexible.
+2 est entre les deux. Pour l'instant, je vais choisir la 2.</p>
+<p>Et avec cette modification, vous devriez toujours voir le tissu sur les pales
+en regardant par derrière, mais il y a un autre problème. Si nous zoomons de près,
+nous voyons que les choses deviennent pixellisées.</p>
+<div class="threejs_center"><img style="width: 700px;" src="../resources/images/windmill-blocky.jpg"></div>
+
+<p>Que se passe-t-il ?</p>
+<p>En regardant les textures, il y a 2 textures étiquetées NOR pour carte NORmale.
+Et en les regardant, elles ressemblent à des cartes normales. Les cartes normales sont généralement
+violettes, tandis que les cartes de relief sont noires et blanches. Les cartes normales représentent
+la direction de la surface, tandis que les cartes de relief représentent la hauteur de
+la surface.</p>
+<div class="threejs_center"><img style="width: 256px;" src="../examples/resources/models/windmill/windmill_001_base_NOR.jpg"></div>
+
+<p>En regardant <a href="https://github.com/mrdoob/three.js/blob/1a560a3426e24bbfc9ca1f5fb0dfb4c727d59046/examples/js/loaders/MTLLoader.js#L432">le code source du MTLLoader</a>,
+il s'attend au mot-clé <code class="notranslate" translate="no">norm</code> pour les cartes normales, alors éditons le fichier .MTL</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no"># Blender MTL File: 'windmill_001.blend'
+# Material Count: 2
+
+newmtl Material
+Ns 0.000000
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.000000 0.000000 0.000000
+Ke 0.000000 0.000000 0.000000
+Ni 1.000000
+d 1.000000
+illum 1
+map_Kd windmill_001_lopatky_COL.jpg
+-map_Bump windmill_001_lopatky_NOR.jpg
++norm windmill_001_lopatky_NOR.jpg
+
+newmtl windmill
+Ns 0.000000
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.000000 0.000000 0.000000
+Ke 0.000000 0.000000 0.000000
+Ni 1.000000
+d 1.000000
+illum 1
+map_Kd windmill_001_base_COL.jpg
+-map_Bump windmill_001_base_NOR.jpg
++norm windmill_001_base_NOR.jpg
+map_Ns windmill_001_base_SPEC.jpg
+</pre>
+<p>et maintenant, lorsque nous le chargerons, il utilisera les cartes normales comme cartes normales
+et nous pourrons voir l'arrière des pales.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-materials-fixed.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-materials-fixed.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Chargeons un fichier différent.</p>
+<p>En cherchant sur internet, j'ai trouvé ce modèle 3D de moulin à vent sous licence <a href="https://creativecommons.org/licenses/by-nc/4.0/">CC-BY-NC</a> réalisé par <a href="http://www.gerzi.ch/">Roger Gerzner / GERIZ.3D Art</a>.</p>
+<div class="threejs_center"><img src="../resources/images/windmill-obj-2.jpg"></div>
+
+<p>Il avait déjà une version .OBJ disponible. Chargeons-le (notez que j'ai retiré le chargeur .MTL pour l'instant)</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-  objLoader.load('resources/models/windmill/windmill.obj', ...
++  objLoader.load('resources/models/windmill-2/windmill.obj', ...
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-wat.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-wat.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Hmmm, rien n'apparaît. Quel est le problème ? Quelle taille a le modèle ?
+Nous pouvons demander à THREE.js quelle taille a le modèle et essayer de régler notre
+caméra automatiquement.</p>
+<p>Tout d'abord, nous pouvons demander à THREE.js de calculer une boîte qui contient la scène
+que nous venons de charger et de demander sa taille et son centre</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">objLoader.load('resources/models/windmill_2/windmill.obj', (root) =&gt; {
+  scene.add(root);
+
++  const box = new THREE.Box3().setFromObject(root);
++  const boxSize = box.getSize(new THREE.Vector3()).length();
++  const boxCenter = box.getCenter(new THREE.Vector3());
++  console.log(boxSize);
++  console.log(boxCenter);
+</pre>
+<p>En regardant dans <a href="debugging-javascript.html">la console JavaScript</a>, je vois</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">size 2123.6499788469982
+center p {x: -0.00006103515625, y: 770.0909731090069, z: -3.313507080078125}
+</pre>
+<p>Notre caméra n'affiche actuellement qu'environ 100 unités, avec <code class="notranslate" translate="no">near</code> à 0.1 et <code class="notranslate" translate="no">far</code> à 100.
+Notre plan de sol ne mesure que 40 unités de large, donc en gros, ce modèle de moulin à vent est si grand, 2000 unités,
+qu'il entoure notre caméra et que toutes ses parties sont en dehors de notre frustum.</p>
+<div class="threejs_center"><img style="width: 280px;" src="../resources/images/camera-inside-windmill.svg"></div>
+
+<p>Nous pourrions corriger cela manuellement, mais nous pourrions aussi faire en sorte que la caméra cadre automatiquement notre scène.
+Essayons cela. Nous pouvons ensuite utiliser la boîte que nous venons de calculer pour ajuster les paramètres de la caméra afin de
+visualiser toute la scène. Notez qu'il n'y a pas de <em>bonne</em> réponse
+sur l'endroit où placer la caméra. Nous pourrions être face à la scène depuis n'importe quelle direction et à n'importe quelle
+altitude, il faudra donc choisir quelque chose.</p>
+<p>Comme nous l'avons vu dans <a href="cameras.html">l'article sur les caméras</a>, la caméra définit un frustum.
+Ce frustum est défini par le champ de vision (<code class="notranslate" translate="no">fov</code>) et les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Nous
+voulons savoir, étant donné le champ de vision actuel de la caméra, à quelle distance la caméra
+doit se trouver pour que la boîte contenant la scène s'inscrive dans le frustum, en supposant que le frustum
+s'étende à l'infini. En d'autres termes, supposons que <code class="notranslate" translate="no">near</code> est 0.00000001 et <code class="notranslate" translate="no">far</code> est l'infini.</p>
+<p>Puisque nous connaissons la taille de la boîte et que nous connaissons le champ de vision, nous avons ce triangle</p>
+<div class="threejs_center"><img style="width: 600px;" src="../resources/images/camera-fit-scene.svg"></div>
+
+<p>Vous pouvez voir à gauche la caméra et le frustum bleu qui se projette devant elle.
+Nous venons de calculer la boîte qui contient le moulin à vent. Nous devons
+calculer à quelle distance la caméra doit se trouver de la boîte pour que celle-ci
+apparaisse à l'intérieur du frustum.</p>
+<p>En utilisant la trigonométrie de base des triangles rectangles et <a href="https://www.google.com/search?q=SOHCAHTOA">SOHCAHTOA</a>,
+étant donné que nous connaissons le champ de vision pour le frustum et que nous connaissons la taille de la boîte, nous pouvons calculer la <em>distance</em>.</p>
+<div class="threejs_center"><img style="width: 600px;" src="../resources/images/field-of-view-camera.svg"></div>
+
+<p>Sur la base de ce diagramme, la formule pour calculer la distance est</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">distance = halfSizeToFitOnScreen / tangent(halfFovY)
+</pre>
+<p>Traduisons cela en code. D'abord, créons une fonction qui calculera <code class="notranslate" translate="no">distance</code>, puis déplacera la
+caméra de <code class="notranslate" translate="no">distance</code> unités à partir du centre de la boîte. Nous dirigerons
+ensuite la caméra vers le <code class="notranslate" translate="no">center</code> de la boîte.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
+  const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
+  const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);
+  const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
+
+  // calcule un vecteur unitaire qui pointe dans la direction où se trouve actuellement la caméra par rapport au centre de la boîte
+  const direction = (new THREE.Vector3()).subVectors(camera.position, boxCenter).normalize();
+
+  // déplace la caméra vers une position située à distance unités du centre,
+  // dans la même direction où se trouvait déjà la caméra par rapport au centre
+  camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
+
+  // choisit des valeurs near et far pour le frustum qui
+  // contiendront la boîte.
+  camera.near = boxSize / 100;
+  camera.far = boxSize * 100;
+
+  camera.updateProjectionMatrix();
+
+  // oriente la caméra pour qu'elle regarde le centre de la boîte
+  camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
+}
+</pre>
+<p>Nous passons 2 tailles. Le <code class="notranslate" translate="no">boxSize</code> et le <code class="notranslate" translate="no">sizeToFitOnScreen</code>. Si nous passions simplement <code class="notranslate" translate="no">boxSize</code>
+et l'utilisions comme <code class="notranslate" translate="no">sizeToFitOnScreen</code>, alors le calcul ferait en sorte que la boîte s'insère parfaitement dans
+le frustum. Nous voulons un peu d'espace supplémentaire au-dessus et en dessous, nous passerons donc une taille
+légèrement plus grande.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const objLoader = new OBJLoader();
+  objLoader.load('resources/models/windmill_2/windmill.obj', (root) =&gt; {
+    scene.add(root);
++    // calcule la boîte qui contient tout ce qui se trouve
++    // à partir de la racine et en dessous
++    const box = new THREE.Box3().setFromObject(root);
++
++    const boxSize = box.getSize(new THREE.Vector3()).length();
++    const boxCenter = box.getCenter(new THREE.Vector3());
++
++    // positionne la caméra pour encadrer la boîte
++    frameArea(boxSize * 1.2, boxSize, boxCenter, camera);
++
++    // met à jour les contrôles OrbitControls pour gérer la nouvelle taille
++    controls.maxDistance = boxSize * 10;
++    controls.target.copy(boxCenter);
++    controls.update();
+  });
+}
+</pre>
+<p>Vous pouvez voir ci-dessus que nous passons <code class="notranslate" translate="no">boxSize * 1.2</code> pour nous donner 20% d'espace supplémentaire
+au-dessus et en dessous de la boîte lorsque nous essayons de l'insérer dans le frustum. Nous avons également mis à jour les
+<a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> afin que la caméra orbite autour du centre
+de la scène.</p>
+<p>Maintenant si nous essayons cela, nous obtenons...</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-auto-camera.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-auto-camera.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Cela fonctionne presque. Utilisez la souris pour faire pivoter la caméra et vous
+devriez voir le moulin à vent. Le problème est que le moulin à vent est grand et que le centre de la boîte se trouve à environ (0, 770, 0).
+Ainsi, lorsque nous déplaçons la caméra de sa position de départ (0, 10, 20) à <code class="notranslate" translate="no">distance</code> unités du centre
+dans la direction où se trouve la caméra par rapport au centre, cela déplace la caméra presque droit vers le bas,
+sous le moulin à vent.</p>
+<div class="threejs_center"><img style="width: 360px;" src="../resources/images/computed-camera-position.svg"></div>
+
+<p>Changeons cela pour nous déplacer latéralement à partir du centre de la boîte, dans la direction
+où se trouve la caméra par rapport au centre. Tout ce que nous devons faire pour cela
+est de mettre à zéro la composante <code class="notranslate" translate="no">y</code> du vecteur allant de la boîte à la caméra.
+Ensuite, lorsque nous normalisons ce vecteur, il deviendra un vecteur parallèle au plan XZ.
+En d'autres termes, parallèle au sol.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-// calcule un vecteur unitaire qui pointe dans la direction où se trouve actuellement la caméra par rapport au centre de la boîte
+-// from the center of the box
+-const direction = (new THREE.Vector3()).subVectors(camera.position, boxCenter).normalize();
++// calcule un vecteur unitaire qui pointe dans la direction où se trouve actuellement la caméra
++// dans le plan xz par rapport au centre de la boîte
++const direction = (new THREE.Vector3())
++    .subVectors(camera.position, boxCenter)
++    .multiply(new THREE.Vector3(1, 0, 1))
++    .normalize();
+</pre>
+<p>Si vous regardez le bas du moulin à vent, vous verrez un petit carré.
+C'est notre plan de sol.</p>
+<div class="threejs_center"><img style="width: 365px;" src="../resources/images/tiny-ground-plane.jpg"></div>
+
+<p>Il ne mesure que 40x40 unités et est donc beaucoup trop petit par rapport au moulin à vent.
+Étant donné que le moulin à vent mesure plus de 2000 unités, changeons la taille du plan de sol
+pour quelque chose de plus approprié. Nous devons également ajuster la répétition, sinon notre damier
+sera si fin que nous ne pourrons même pas le voir à moins de zoomer très très près.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const planeSize = 40;
++const planeSize = 4000;
+
+const loader = new THREE.TextureLoader();
+const texture = loader.load('resources/images/checker.png');
+texture.wrapS = THREE.RepeatWrapping;
+texture.wrapT = THREE.RepeatWrapping;
+texture.magFilter = THREE.NearestFilter;
+-const repeats = planeSize / 2;
++const repeats = planeSize / 200;
+texture.repeat.set(repeats, repeats);
+</pre>
+<p>et maintenant nous pouvons voir ce moulin à vent</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-auto-camera-xz.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-auto-camera-xz.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Ajoutons à nouveau les matériaux. Comme précédemment, il y a un fichier .MTL qui référence
+des textures, mais en regardant les fichiers, je constate rapidement un problème.</p>
+<pre class="prettyprint showlinemods notranslate lang-shell" translate="no"> $ ls -l windmill
+ -rw-r--r--@ 1 gregg  staff       299 May 20  2009 windmill.mtl
+ -rw-r--r--@ 1 gregg  staff    142989 May 20  2009 windmill.obj
+ -rw-r--r--@ 1 gregg  staff  12582956 Apr 19  2009 windmill_diffuse.tga
+ -rw-r--r--@ 1 gregg  staff  12582956 Apr 20  2009 windmill_normal.tga
+ -rw-r--r--@ 1 gregg  staff  12582956 Apr 19  2009 windmill_spec.tga
+</pre>
+<p>Il y a des fichiers TARGA (.tga) et ils sont gigantesques !</p>
+<p>THREE.js a en fait un chargeur TGA, mais il est discutable de l'utiliser pour la plupart des cas d'utilisation.
+Si vous créez une visionneuse où vous voulez permettre aux utilisateurs de voir des fichiers 3D aléatoires qu'ils
+trouvent sur internet, alors peut-être, juste peut-être, que vous voudrez charger des fichiers TGA. (<a href="#loading-scenes">*</a>)</p>
+<p>Un problème avec les fichiers TGA est qu'ils ne peuvent pas être bien compressés du tout.
+TGA ne prend en charge qu'une compression très simple, et en regardant ci-dessus, nous pouvons voir que les fichiers ne sont pas du tout
+compressés, car les chances qu'ils aient tous exactement la même taille sont extrêmement faibles.
+De plus, ils font 12 mégaoctets chacun !!!</p>
+<p>Si nous utilisions ces fichiers, l'utilisateur devrait télécharger 36 Mo
+pour voir le moulin à vent.</p>
+<p>Un autre problème avec TGA est que le navigateur lui-même ne les prend pas en charge, donc le chargement
+sera probablement plus lent que le chargement de formats pris en charge comme .JPG et .PNG</p>
+<p>Je suis presque sûr que pour nos besoins, les convertir en .JPG sera la meilleure option.
+En regardant à l'intérieur, je vois qu'ils ont 3 canaux chacun, RGB, il n'y a pas de canal alpha. JPG
+ne prend en charge que 3 canaux, c'est donc une bonne correspondance. JPG prend également en charge la compression
+avec perte, ce qui nous permet de rendre les fichiers beaucoup plus petits à télécharger</p>
+<p>En chargeant les fichiers, ils étaient chacun de 2048x2048. Cela m'a semblé un gâchis,
+mais bien sûr, cela dépend de votre cas d'utilisation. Je les ai mis chacun en 1024x1024
+et les ai enregistrés avec un réglage de qualité de 50% dans Photoshop. Obtenir une liste de fichiers</p>
+<pre class="prettyprint showlinemods notranslate lang-shell" translate="no"> $ ls -l ../threejs.org/manual/examples/resources/models/windmill
+ -rw-r--r--@ 1 gregg  staff     299 May 20  2009 windmill.mtl
+ -rw-r--r--@ 1 gregg  staff  142989 May 20  2009 windmill.obj
+ -rw-r--r--@ 1 gregg  staff  259927 Nov  7 18:37 windmill_diffuse.jpg
+ -rw-r--r--@ 1 gregg  staff   98013 Nov  7 18:38 windmill_normal.jpg
+ -rw-r--r--@ 1 gregg  staff  191864 Nov  7 18:39 windmill_spec.jpg
+</pre>
+<p>Nous sommes passés de 36 Mo à 0,55 Mo ! Bien sûr, l'artiste pourrait ne pas être satisfait
+de cette compression, alors assurez-vous de le consulter pour discuter des compromis.</p>
+<p>Maintenant, pour utiliser le fichier .MTL, nous devons l'éditer pour qu'il référence les fichiers .JPG
+au lieu des fichiers .TGA. Heureusement, c'est un simple fichier texte, il est donc facile à éditer</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no">newmtl blinn1SG
+Ka 0.10 0.10 0.10
+
+Kd 0.00 0.00 0.00
+Ks 0.00 0.00 0.00
+Ke 0.00 0.00 0.00
+Ns 0.060000
+Ni 1.500000
+d 1.000000
+Tr 0.000000
+Tf 1.000000 1.000000 1.000000
+illum 2
+-map_Kd windmill_diffuse.tga
++map_Kd windmill_diffuse.jpg
+
+-map_Ks windmill_spec.tga
++map_Ks windmill_spec.jpg
+
+-map_bump windmill_normal.tga
+-bump windmill_normal.tga
++map_bump windmill_normal.jpg
++bump windmill_normal.jpg
+</pre>
+<p>Maintenant que le fichier .MTL pointe vers des textures de taille raisonnable, nous devons le charger.
+Nous allons donc faire comme nous l'avons fait ci-dessus : charger d'abord les matériaux,
+puis les définir sur l'<a href="/docs/#examples/loaders/OBJLoader"><code class="notranslate" translate="no">OBJLoader</code></a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
++  const mtlLoader = new MTLLoader();
++  mtlLoader.load('resources/models/windmill_2/windmill-fixed.mtl', (mtl) =&gt; {
++    mtl.preload();
++    const objLoader = new OBJLoader();
++    objLoader.setMaterials(mtl);
+    objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {
+      root.updateMatrixWorld();
+      scene.add(root);
+      // calcule la boîte qui contient tout ce qui se trouve
+      // à partir de la racine et en dessous
+      const box = new THREE.Box3().setFromObject(root);
+
+      const boxSize = box.getSize(new THREE.Vector3()).length();
+      const boxCenter = box.getCenter(new THREE.Vector3());
+
+      // positionne la caméra pour encadrer la boîte
+      frameArea(boxSize * 1.2, boxSize, boxCenter, camera);
+
+      // met à jour les contrôles Trackball pour gérer la nouvelle taille
+      controls.maxDistance = boxSize * 10;
+      controls.target.copy(boxCenter);
+      controls.update();
+    });
++  });
+}
+</pre>
+<p>Avant d'essayer, j'ai rencontré quelques problèmes que, plutôt que de montrer un échec,
+je vais simplement passer en revue.</p>
+<p>Problème n°1 : Le MTLLoader de three crée des matériaux qui multiplient la couleur diffuse du matériau par la carte de texture diffuse.</p>
+<p>C'est une fonctionnalité utile, mais en regardant le fichier .MTL ci-dessus, la ligne</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no">Kd 0.00 0.00 0.00
+</pre>
+<p>définit la couleur diffuse à 0. Carte de texture * 0 = noir ! Il est possible que l'outil de modélisation
+utilisé pour créer le moulin à vent n'ait pas multiplié la carte de texture diffuse par la couleur diffuse.
+C'est pourquoi cela a fonctionné pour les artistes qui ont créé ce moulin à vent.</p>
+<p>Pour corriger cela, nous pouvons changer la ligne en</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no">Kd 1.00 1.00 1.00
+</pre>
+<p>car Carte de Texture * 1 = Carte de Texture.</p>
+<p>Problème n°2 : La couleur spéculaire est également noire</p>
+<p>La ligne qui commence par <code class="notranslate" translate="no">Ks</code> spécifie la couleur spéculaire. Il est probable que le logiciel de modélisation
+utilisé pour créer le moulin à vent ait fait quelque chose de similaire à ce qu'il a fait avec les cartes diffuses,
+c'est-à-dire qu'il a utilisé la couleur de la carte spéculaire pour les reflets spéculaires.
+Three.js utilise uniquement le canal rouge d'une carte spéculaire comme entrée pour déterminer la quantité
+de couleur spéculaire à refléter, mais three a toujours besoin d'une couleur spéculaire définie.</p>
+<p>Comme ci-dessus, nous pouvons corriger cela en éditant le fichier .MTL comme ceci.</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no">-Ks 0.00 0.00 0.00
++Ks 1.00 1.00 1.00
+</pre>
+<p>Problème n°3 : Le fichier <code class="notranslate" translate="no">windmill_normal.jpg</code> est une carte normale et non une carte de relief.</p>
+<p>Tout comme ci-dessus, il suffit d'éditer le fichier .MTL</p>
+<pre class="prettyprint showlinemods notranslate lang-mtl" translate="no">-map_bump windmill_normal.jpg
+-bump windmill_normal.jpg
++norm windmill_normal.jpg
+</pre>
+<p>Compte tenu de tout cela, si nous l'essayons maintenant, il devrait se charger avec les matériaux.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/load-obj-materials-windmill2.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/load-obj-materials-windmill2.html" target="_blank">cliquez ici pour ouvrir dans une nouvelle fenêtre</a>
+</div>
+
+<p></p>
+<p>Le chargement de modèles rencontre souvent ce genre de problèmes. Les problèmes courants incluent :</p>
+<ul>
+<li><p>Nécessité de connaître la taille</p>
+<p>Ci-dessus, nous avons fait en sorte que la caméra essaie de cadrer la scène, mais ce n'est pas toujours la chose appropriée à faire. Généralement, la chose la plus appropriée à faire est de créer vos propres modèles ou de télécharger les modèles, de les charger dans un logiciel 3D et d'examiner leur échelle et de l'ajuster si nécessaire.</p>
+</li>
+<li><p>Orientation incorrecte</p>
+<p>THREE.js utilise généralement Y vers le haut. Certains logiciels de modélisation utilisent par défaut Z vers le haut, d'autres Y vers le haut. Certains sont configurables.
+Si vous rencontrez ce cas où vous chargez un modèle et qu'il est sur le côté. Vous pouvez soit modifier votre code pour faire pivoter le modèle après le chargement (non recommandé), soit charger le modèle dans votre logiciel de modélisation préféré ou utiliser des outils en ligne de commande pour faire pivoter l'objet dans l'orientation dont vous avez besoin, tout comme vous éditeriez une image pour votre site web plutôt que de la télécharger et d'appliquer du code pour l'ajuster. Blender a même des options lors de l'exportation pour changer l'orientation.</p>
+</li>
+<li><p>Pas de fichier .MTL ou matériaux incorrects ou paramètres incompatibles</p>
+<p>Ci-dessus, nous avons utilisé un fichier .MTL qui nous a aidés à charger des matériaux, mais il y a eu des problèmes. Nous avons édité manuellement le fichier .MTL pour les corriger.
+Il est également courant de regarder à l'intérieur du fichier .OBJ pour voir quels matériaux il contient, ou de charger le fichier .OBJ dans THREE.js et de parcourir la scène pour afficher tous les matériaux. Ensuite, modifiez le code pour créer des matériaux personnalisés et les attribuer là où cela est approprié, soit en créant un objet paire nom/matériau à passer au chargeur au lieu de charger le fichier .MTL, SOIT, après le chargement de la scène, en parcourant la scène et en corrigeant les choses.</p>
+</li>
+<li><p>Textures trop grandes</p>
+<p>La plupart des modèles 3D sont créés pour l'architecture, les films et publicités, ou les jeux. Pour l'architecture et les films, personne ne se soucie vraiment de la taille des textures puisque. Pour les jeux, les gens s'en soucient car les jeux ont une mémoire limitée, mais la plupart des jeux s'exécutent localement. Cependant, sur les pages web, vous voulez charger le plus rapidement possible, et vous devez donc regarder les textures et essayer de les rendre aussi petites que possible tout en conservant un bon rendu. En fait, pour le premier moulin à vent, nous aurions probablement dû faire quelque chose concernant les textures. Elles totalisent actuellement 10 Mo !!!</p>
+<p>Rappelez-vous également,
+comme nous l'avons mentionné dans <a href="textures.html">l'article sur les textures</a>, que
+les textures prennent de la mémoire. Ainsi, un JPG de 50 Ko qui s'étend à 4096x4096 se téléchargera
+rapidement mais prendra toujours une énorme quantité de mémoire.</p>
+</li>
+</ul>
+<p>La dernière chose que je voulais montrer est de faire tourner les moulins à vent. Malheureusement,
+les fichiers .OBJ n'ont pas de hiérarchie. Cela signifie que toutes les parties de chaque
+moulin à vent sont fondamentalement considérées comme un seul maillage. Vous ne pouvez pas faire tourner les
+pales du moulin car elles ne sont pas séparées du reste du bâtiment.</p>
+<p>C'est l'une des principales raisons pour lesquelles le .OBJ n'est pas vraiment un bon format.
+Si je devais deviner, la raison pour laquelle il est plus courant que d'autres formats
+est qu'il est simple et, comme il ne prend pas en charge de nombreuses fonctionnalités, il fonctionne le plus souvent.
+Surtout si vous créez quelque chose de statique comme une image architecturale
+et qu'il n'y a pas besoin d'animer quoi que ce soit, ce n'est pas une mauvaise façon d'intégrer des
+éléments statiques dans une scène.</p>
+<p>Ensuite, nous allons essayer <a href="load-gltf.html">le chargement d'une scène gLTF</a>.
+Le format gLTF prend en charge beaucoup plus de fonctionnalités.</p>
 
         </div>
       </div>

+ 160 - 0
manual/fr/loading-3d-models.html

@@ -0,0 +1,160 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Chargement de modèles 3D</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Chargement de modèles 3D">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Chargement de modèles 3D</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p>
+            Les modèles 3D sont disponibles dans des centaines de formats de fichiers, chacun ayant des objectifs différents, des fonctionnalités variées et une complexité variable. Bien que
+            <a href="https://github.com/mrdoob/three.js/tree/dev/examples/jsm/loaders" target="_blank" rel="noopener">
+            three.js propose de nombreux chargeurs</a>, choisir le bon format et le bon flux de travail vous fera gagner du temps et vous évitera des frustrations plus tard. Certains formats sont difficiles à manipuler, inefficaces pour les expériences en temps réel, ou simplement pas entièrement pris en charge à l'heure actuelle.
+          </p>
+        
+          <p>
+            Ce guide propose un flux de travail recommandé pour la plupart des utilisateurs, ainsi que des suggestions sur ce qu'il faut essayer si les choses ne se passent pas comme prévu.
+          </p>
+        
+          <h2>Avant de commencer</h2>
+        
+          <p>
+            Si vous débutez avec l'exécution d'un serveur local, commencez par
+            <a href="installation.html">Installation</a>
+            d'abord. De nombreuses erreurs courantes lors de la visualisation de modèles 3D peuvent être évitées en hébergeant correctement les fichiers.
+          </p>
+        
+          <h2>Flux de travail recommandé</h2>
+        
+          <p>
+            Dans la mesure du possible, nous recommandons l'utilisation de glTF (GL Transmission Format). Les versions
+            <small>.GLB</small> et <small>.GLTF</small> du format sont bien prises en charge. Étant donné que glTF est axé sur la diffusion d'assets en temps réel, il est compact à transmettre et rapide à charger. Les fonctionnalités incluent les maillages, les matériaux, les textures, les peaux, les squelettes, les cibles de déformation (morph targets), les animations, les lumières et les caméras.
+          </p>
+        
+          <p>
+            Des fichiers glTF du domaine public sont disponibles sur des sites comme
+            <a href="https://sketchfab.com/models?features=downloadable&sort_by=-likeCount&type=models" target="_blank" rel="noopener">
+            Sketchfab</a>, ou divers outils incluent l'exportation glTF :
+          </p>
+        
+          <ul>
+            <li><a href="https://www.blender.org/" target="_blank" rel="noopener">Blender</a> par la Blender Foundation</li>
+            <li><a href="https://www.allegorithmic.com/products/substance-painter" target="_blank" rel="noopener">Substance Painter</a> par Allegorithmic</li>
+            <li><a href="https://www.foundry.com/products/modo" target="_blank" rel="noopener">Modo</a> par Foundry</li>
+            <li><a href="https://www.marmoset.co/toolbag/" target="_blank" rel="noopener">Toolbag</a> par Marmoset</li>
+            <li><a href="https://www.sidefx.com/products/houdini/" target="_blank" rel="noopener">Houdini</a> par SideFX</li>
+            <li><a href="https://labs.maxon.net/?p=3360" target="_blank" rel="noopener">Cinema 4D</a> par MAXON</li>
+            <li><a href="https://github.com/KhronosGroup/COLLADA2GLTF" target="_blank" rel="noopener">COLLADA2GLTF</a> par le Khronos Group</li>
+            <li><a href="https://github.com/facebookincubator/FBX2glTF" target="_blank" rel="noopener">FBX2GLTF</a> par Facebook</li>
+            <li><a href="https://github.com/AnalyticalGraphicsInc/obj2gltf" target="_blank" rel="noopener">OBJ2GLTF</a> par Analytical Graphics Inc</li>
+            <li>&hellip;et <a href="http://github.khronos.org/glTF-Project-Explorer/" target="_blank" rel="noopener">bien d'autres encore</a></li>
+          </ul>
+        
+          <p>
+            Si vos outils préférés ne prennent pas en charge glTF, envisagez de demander l'exportation glTF aux auteurs, ou de poster sur
+            <a href="https://github.com/KhronosGroup/glTF/issues/1051" target="_blank" rel="noopener">le fil de discussion de la feuille de route glTF</a>.
+          </p>
+        
+          <p>
+            Lorsque glTF n'est pas une option, des formats populaires tels que FBX, OBJ ou COLLADA sont également disponibles et régulièrement mis à jour.
+          </p>
+        
+          <h2>Chargement</h2>
+        
+          <p>
+            Seuls quelques chargeurs (par exemple `ObjectLoader`) sont inclus par défaut avec three.js — les autres doivent être ajoutés individuellement à votre application.
+          </p>
+        
+<pre class="prettyprint notranslate lang-js" translate="no">
+import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
+</pre>
+
+          <p>
+            Une fois qu'un chargeur a été importé, vous êtes prêt à ajouter un modèle à votre scène. La syntaxe varie selon les différents chargeurs — lorsque vous utilisez un autre format, consultez les exemples et la documentation de ce chargeur. Pour glTF, l'utilisation avec des scripts globaux serait :
+          </p>
+        
+<pre class="prettyprint notranslate lang-js" translate="no">
+const loader = new GLTFLoader();
+
+loader.load( 'path/to/model.glb', function ( gltf ) {
+
+  scene.add( gltf.scene );
+
+}, undefined, function ( error ) {
+
+  console.error( error );
+
+} );
+</pre>
+        
+          <h2>Dépannage</h2>
+        
+          <p>
+            Vous avez passé des heures à modéliser un chef-d'œuvre artisanal, vous le chargez dans la page web, et — oh non ! 😭 Il est déformé, mal coloré, ou manque entièrement. Commencez par ces étapes de dépannage :
+          </p>
+        
+          <ol>
+            <li>
+              Vérifiez la console JavaScript pour les erreurs, et assurez-vous d'avoir utilisé une fonction de rappel `onError` lors de l'appel à `.load()` pour journaliser le résultat.
+            </li>
+            <li>
+              Visualisez le modèle dans une autre application. Pour glTF, des visionneuses par glisser-déposer sont disponibles pour
+              <a href="https://gltf-viewer.donmccurdy.com/" target="_blank" rel="noopener">three.js</a> et
+              <a href="https://sandbox.babylonjs.com/" target="_blank" rel="noopener">babylon.js</a>. Si le modèle
+              apparaît correctement dans une ou plusieurs applications,
+              <a href="https://github.com/mrdoob/three.js/issues/new" target="_blank" rel="noopener">signalez un bug à three.js</a>.
+              Si le modèle ne peut être affiché dans aucune application, nous vous encourageons fortement à signaler un bug à l'application utilisée pour créer le modèle.
+            </li>
+            <li>
+              Essayez de mettre à l'échelle le modèle vers le haut ou vers le bas par un facteur de 1000. De nombreux modèles sont mis à l'échelle différemment, et les grands modèles peuvent ne pas apparaître si la caméra est à l'intérieur du modèle.
+            </li>
+            <li>
+              Essayez d'ajouter et de positionner une source de lumière. Le modèle peut être caché dans l'obscurité.
+            </li>
+            <li>
+              Recherchez les demandes de texture échouées dans l'onglet réseau, comme
+              `"C:\\Path\To\Model\texture.jpg"`. Utilisez plutôt des chemins relatifs à votre
+              modèle, tels que `images/texture.jpg` — cela peut nécessiter d'éditer le fichier modèle dans un éditeur de texte.
+            </li>
+          </ol>
+        
+          <h2>Demander de l'aide</h2>
+        
+          <p>
+            Si vous avez suivi le processus de dépannage ci-dessus et que votre modèle ne fonctionne toujours pas, la bonne approche pour demander de l'aide vous permettra d'obtenir une solution plus rapidement. Posez une question sur le
+            <a href="https://discourse.threejs.org/" target="_blank" rel="noopener">forum three.js</a> et, dans la mesure du possible,
+            incluez votre modèle (ou un modèle plus simple présentant le même problème) dans tous les formats dont vous disposez. Incluez suffisamment d'informations pour que quelqu'un d'autre puisse reproduire le problème rapidement — idéalement, une démo en direct.
+          </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 10 - 5
manual/fr/material-table.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Material Feature Table</title>
+    <title>Tableau des fonctionnalités des matériaux</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Material Feature Table">
+    <meta name="twitter:title" content="Three.js – Tableau des fonctionnalités des matériaux">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,17 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Material Feature Table</h1>
+        <h1>Tableau des fonctionnalités des matériaux</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/material-table.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Les matériaux les plus courants dans three.js sont les matériaux Mesh. Voici un tableau montrant quels matériaux prennent en charge quelles fonctionnalités.</p>
+<div>
+<div id="material-table" class="threejs_center"></div>
+<script type="module" src="../resources/threejs-material-table.js"></script>
+<link rel="stylesheet" href="../resources/threejs-material-table.css">
+</div>
+
 
         </div>
       </div>

+ 129 - 74
manual/fr/materials.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Les matériaux</title>
+    <title>Matériaux</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,48 +22,54 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Les matériaux</h1>
+        <h1>Matériaux</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js dont
-le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous devriez commencer par lui.</p>
-<p>Three.js fournit plusieurs types de matériaux : ils définissent comment les objets apparaîtront dans la scène et par conséquent, la sélection de vos matériaux dépend fortement dans le choix de ce que vous voulez afficher.</p>
-<p>Il existe deux façons de définir la plupart des propriétés des matériaux. La première façon est de les définir lors de la création du matériau (constructeur), comme nous l'avons déjà vu :</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le
+premier article est <a href="fundamentals.html">les bases de three.js</a>. Si
+vous ne l'avez pas encore lu et que vous débutez avec three.js, vous pourriez
+vouloir commencer par là.</p>
+<p>Three.js propose plusieurs types de matériaux.
+Ils définissent comment les objets apparaîtront dans la scène.
+Les matériaux que vous utilisez dépendent vraiment de ce que vous essayez
+d'accomplir.</p>
+<p>Il existe 2 manières de définir la plupart des propriétés des matériaux. L'une
+au moment de la création, ce que nous avons déjà vu.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial({
-  color: 0xFF0000,    // red (can also use a CSS color string here)
+  color: 0xFF0000,    // rouge (peut aussi utiliser une chaîne de couleur CSS ici)
   flatShading: true,
 });
 </pre>
-<p>La seconde façon se fait après le constructeur :</p>
+<p>L'autre est après la création</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial();
-material.color.setHSL(0, 1, .5);  // red
+material.color.setHSL(0, 1, .5);  // rouge
 material.flatShading = true;
 </pre>
-<p>Notez qu'il y a plusieurs façons de paramétrer la propriété <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> :</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">material.color.set(0x00FFFF);    // same as CSS's #RRGGBB style
-material.color.set(cssString);   // any CSS color, eg 'purple', '#F32',
+<p>notez que les propriétés de type <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> peuvent être définies de plusieurs manières.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">material.color.set(0x00FFFF);    // comme le style #RRGGBB de CSS
+material.color.set(cssString);   // n'importe quelle couleur CSS, par exemple 'purple', '#F32',
                                  // 'rgb(255, 127, 64)',
                                  // 'hsl(180, 50%, 25%)'
-material.color.set(someColor)    // some other THREE.Color
-material.color.setHSL(h, s, l)   // where h, s, and l are 0 to 1
-material.color.setRGB(r, g, b)   // where r, g, and b are 0 to 1
+material.color.set(someColor)    // une autre THREE.Color
+material.color.setHSL(h, s, l)   // où h, s et l sont de 0 à 1
+material.color.setRGB(r, g, b)   // où r, g et b sont de 0 à 1
 </pre>
-<p>Pour le constructeur, vous pouvez passer, soit un nombre hexadécimal, soit une chaîne de caractères au format CSS :</p>
+<p>Et au moment de la création, vous pouvez passer soit un nombre hexadécimal, soit une chaîne CSS</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const m1 = new THREE.MeshBasicMaterial({color: 0xFF0000});         // rouge
 const m2 = new THREE.MeshBasicMaterial({color: 'red'});            // rouge
 const m3 = new THREE.MeshBasicMaterial({color: '#F00'});           // rouge
 const m4 = new THREE.MeshBasicMaterial({color: 'rgb(255,0,0)'});   // rouge
 const m5 = new THREE.MeshBasicMaterial({color: 'hsl(0,100%,50%)'}); // rouge
 </pre>
-<p>Examinons l'ensemble des matériaux de Three.js</p>
-<p>Le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> n'est pas affecté par la lumière.
-Le <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> calcule la lumière sur chaque sommets (vertices) de l'objet, alors que <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> calculera la lumière sur chaque pixel des faces de l'objet et prendra également en compte les reflets spéculaires.</p>
+<p>Passons donc en revue l'ensemble des matériaux de three.js.</p>
+<p>Le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> n'est pas affecté par les lumières.</p>
+<p>Le <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> calcule l'éclairage uniquement aux sommets, contrairement au <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> qui calcule l'éclairage à chaque pixel. Le <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>
+prend également en charge les reflets spéculaires.</p>
 <div class="spread">
   <div>
     <div data-diagram="MeshBasicMaterial"></div>
-    <div class="code">Basic</div>
+    <div class="code">Basique</div>
   </div>
   <div>
     <div data-diagram="MeshLambertMaterial"></div>
@@ -87,7 +93,7 @@ Le <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslat
 </div>
 <div class="threejs_center code">modèles low-poly avec les mêmes matériaux</div>
 
-<p>Le paramètre <code class="notranslate" translate="no">shininess</code> du <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> détermine la <em>brillance</em> de la surbrillance spéculaire. La valeur par défaut est 30.</p>
+<p>Le paramètre <code class="notranslate" translate="no">shininess</code> du <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> détermine la <em>brillance</em> du reflet spéculaire. Par défaut, il est de 30.</p>
 <div class="spread">
   <div>
     <div data-diagram="MeshPhongMaterialShininess0"></div>
@@ -103,14 +109,14 @@ Le <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslat
   </div>
 </div>
 
-<p>Notez que définir la propriété <code class="notranslate" translate="no">emissive</code> sur une couleur sur un
-<a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ou un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> et régler la propriété <code class="notranslate" translate="no">color</code> sur noir
-(et <code class="notranslate" translate="no">shininess</code> à 0 pour Phong) finit par ressembler au <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
+<p>Notez que définir la propriété <code class="notranslate" translate="no">emissive</code> sur une couleur pour un
+<a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ou un <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> et définir la <code class="notranslate" translate="no">color</code> en noir
+(et <code class="notranslate" translate="no">shininess</code> à 0 pour phong) finit par ressembler exactement au <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
 <div class="spread">
   <div>
     <div data-diagram="MeshBasicMaterialCompare"></div>
     <div class="code">
-      <div>Basic</div>
+      <div>Basique</div>
       <div>color: 'purple'</div>
     </div>
   </div>
@@ -133,40 +139,69 @@ Le <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslat
   </div>
 </div>
 
-<p>Pourquoi Three.js propose trois matériaux similaires si au final <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> peut faire les mêmes choses que <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> et <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ? La raison est simple : le matériau le plus sophistiqué nécessite plus de puissance de la part du GPU. Sur un GPU plus lent comme sur un téléphone mobile, vous souhaitez peut-être améliorer les performances en utilisant un des matériaux moins gourmand en calculs GPU. Il en découle que si vous n'avez pas besoin de fonctionnalités supplémentaires, alors il vaut mieux privilégier le matériau le plus simple. Si vous n'avez pas besoin d'éclairage et de la surbrillance spéculaire alors utilisez le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
+<p>Pourquoi avoir les 3 alors que <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> peut faire les mêmes choses que <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>
+et <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ? La raison est que le matériau le plus sophistiqué
+nécessite plus de puissance GPU pour être dessiné. Sur un GPU plus lent,
+comme celui d'un téléphone portable, vous pourriez vouloir réduire la
+puissance GPU nécessaire pour dessiner votre scène en utilisant l'un des
+matériaux moins complexes. Il s'ensuit également que si vous n'avez pas
+besoin des fonctionnalités supplémentaires, utilisez le matériau le plus simple.
+Si vous n'avez pas besoin de l'éclairage et des reflets spéculaires,
+utilisez le <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
 <p>Le <a href="/docs/#api/en/materials/MeshToonMaterial"><code class="notranslate" translate="no">MeshToonMaterial</code></a> est similaire au <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>
-avec une grande différence : plutôt que d'ombrager en douceur, il utilise une carte de dégradé (une texture X par 1) pour décider comment ombrager. La valeur par défaut utilise une carte de dégradé dont la luminosité est de 70 % pour les premiers 70%, puis 100 % pour la suite. Vous pouvez aussi fournir votre propre carte de dégradé. Cela peut même donner une allure de dessin animé (cartoon) sur deux teintes.</p>
+avec une grande différence. Au lieu d'ombrer en douceur, il utilise une
+carte de dégradé (une texture de taille X sur 1) pour décider comment ombrer.
+Par défaut, il utilise une carte de dégradé dont la luminosité est de 70%
+pour les premiers 70% et de 100% ensuite, mais vous pouvez fournir votre
+propre carte de dégradé. Cela donne finalement un aspect bicolore qui
+ressemble à un dessin animé.</p>
 <div class="spread">
   <div data-diagram="MeshToonMaterial"></div>
 </div>
 
-<p>Ensuite, il y a deux matériaux de <em>rendu physique</em>, souvent abrégé en PBR (<em>Physics-Based Rendering material</em>).</p>
-<p>En effet, les matériaux vus précédemment utilisent des mathématiques simples pour créer des matériaux qui semblent en 3D, mais ne réagissent pas comme dans le monde réel. Les deux matériaux PBR utilisent des mathématiques beaucoup plus complexes pour se rapprocher de ce qui se passe réellement dans le monde réel.</p>
-<p>Le premier est <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a>. Il diffère de <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> et de <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> en utilisant différents paramètres.
-<a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> a un seul paramètre <code class="notranslate" translate="no">shininess</code> alors que <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> utilise deux paramètres <code class="notranslate" translate="no">roughness</code> (rugosité) et <code class="notranslate" translate="no">metalness</code> (métallique).</p>
-<p>Pour faire simple, <a href="/docs/#api/en/materials/MeshStandardMaterial#roughness"><code class="notranslate" translate="no">roughness</code></a> est l'opposé de <code class="notranslate" translate="no">shininess</code>.
-Quelque chose qui a une rugosité élevée, comme une balle de baseball, n'a pas de reflets durs, alors que quelque chose qui n'est pas rugueux, comme une boule de billard, est très brillant. La rugosité varie de 0 à 1.</p>
+<p>Ensuite, il y a 2 matériaux basés sur le <em>rendu physique</em>. Le Rendu Basé sur
+le Physique est souvent abrégé PBR.</p>
+<p>Les matériaux ci-dessus utilisent des calculs simples pour créer des matériaux
+qui semblent en 3D, mais ils ne correspondent pas à ce qui se passe réellement
+dans le monde réel. Les 2 matériaux PBR utilisent des calculs beaucoup plus
+complexes pour se rapprocher de ce qui se passe réellement dans le monde réel.</p>
+<p>Le premier est <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a>. La plus grande différence entre
+<a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> et <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> est qu'il utilise différents paramètres.
+<a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> avait un paramètre <code class="notranslate" translate="no">shininess</code>. <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> a 2
+paramètres : <code class="notranslate" translate="no">roughness</code> et <code class="notranslate" translate="no">metalness</code>.</p>
+<p>À un niveau basique, <a href="/docs/#api/en/materials/MeshStandardMaterial#roughness"><code class="notranslate" translate="no">roughness</code></a> est l'opposé
+de <code class="notranslate" translate="no">shininess</code>. Quelque chose qui a une rugosité élevée, comme une balle de baseball, n'a pas
+de reflets durs, tandis que quelque chose qui n'est pas rugueux, comme une boule de billard,
+est très brillant. La rugosité va de 0 à 1.</p>
 <p>L'autre paramètre, <a href="/docs/#api/en/materials/MeshStandardMaterial#metalness"><code class="notranslate" translate="no">metalness</code></a>, indique
 à quel point le matériau est métallique. Les métaux se comportent différemment des non-métaux. 0
-pour le non-métal et 1 pour le métal.</p>
-<p>Voici quelques exemples de <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> avec un <code class="notranslate" translate="no">roughness</code> allant de 0 à 1
-sur la droite et un <code class="notranslate" translate="no">metalness</code> allant de 0 à 1 en descendant.</p>
+pour non-métal et 1 pour métal.</p>
+<p>Voici un échantillon rapide de <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> avec une <code class="notranslate" translate="no">roughness</code> de 0 à 1
+horizontalement et une <code class="notranslate" translate="no">metalness</code> de 0 à 1 verticalement.</p>
 <div data-diagram="MeshStandardMaterial" style="min-height: 400px"></div>
 
-<p>Le <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a> est le même que le <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> mais il ajoute un paramètre <code class="notranslate" translate="no">clearcoat</code> (vernis) qui va de 0 à 1 pour savoir quelle couche vernis brillant appliquer. Et un paramètre <code class="notranslate" translate="no">clearCoatRoughness</code> qui spécifie à quel point la couche de vernis brillant est rugueuse.</p>
-<p>Voici la même grille que ci-dessus, mais avec les paramètres <code class="notranslate" translate="no">clearcoat</code> et <code class="notranslate" translate="no">clearCoatRoughness</code> en plus.</p>
+<p>Le <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a> est le même que le <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> mais il
+ajoute un paramètre <code class="notranslate" translate="no">clearcoat</code> qui va de 0 à 1 pour déterminer la
+quantité de couche de vernis brillant à appliquer, et un paramètre
+<code class="notranslate" translate="no">clearCoatRoughness</code> qui spécifie la rugosité de la couche brillante.</p>
+<p>Voici la même grille de <code class="notranslate" translate="no">roughness</code> par <code class="notranslate" translate="no">metalness</code> que ci-dessus, mais avec
+les paramètres <code class="notranslate" translate="no">clearcoat</code> et <code class="notranslate" translate="no">clearCoatRoughness</code>.</p>
 <div data-diagram="MeshPhysicalMaterial" style="min-height: 400px"></div>
 
-<p>Voici la liste des divers matériaux standards rangés du plus rapide au plus lent :
+<p>Les différents matériaux standard progressent du plus rapide au plus lent :
 <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> ➡
-<a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a>. Les matériaux les plus longs à calculer créent des scènes plus réalistes, mais vous devrez peut-être également concevoir votre code pour utiliser les matériaux plus rapides à calculer pour des machines mobiles ou de faible puissance.</p>
-<p>Il existe trois matériaux qui ont des utilisations spéciales.</p>
-
-<p><a href="/docs/#api/en/materials/ShadowMaterial"><code class="notranslate" translate="no">ShadowMaterial</code></a>
-est utilisé pour obtenir les données créées à partir des ombres (sujet que nous n'avons pas encore couvert), mais nous l'utiliserons dans cet article traitant des <a href="shadows.html">ombres</a>.</p>
-<p>Le <a href="/docs/#api/en/materials/MeshDepthMaterial"><code class="notranslate" translate="no">MeshDepthMaterial</code></a> restitue la profondeur de chaque pixel où les pixels
-négatifs <a href="/docs/#api/en/cameras/PerspectiveCamera#near"><code class="notranslate" translate="no">near</code></a> sont à 0 et les négatifs <a href="/docs/#api/en/cameras/PerspectiveCamera#far"><code class="notranslate" translate="no">far</code></a> sont à 1.
-Certains effets spéciaux peuvent utiliser ces données que nous aborderons plus tard.</p>
+<a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a>. Les matériaux plus lents
+peuvent créer des scènes plus réalistes, mais vous pourriez avoir besoin de
+concevoir votre code pour utiliser les matériaux plus rapides sur les machines
+à faible puissance ou les appareils mobiles.</p>
+<p>Il existe 3 matériaux qui ont des utilisations spéciales. <a href="/docs/#api/en/materials/ShadowMaterial"><code class="notranslate" translate="no">ShadowMaterial</code></a>
+est utilisé pour obtenir les données créées par les ombres. Nous n'avons pas
+encore abordé les ombres. Lorsque ce sera le cas, nous utiliserons ce matériau
+pour jeter un coup d'œil à ce qui se passe en coulisse.</p>
+<p>Le <a href="/docs/#api/en/materials/MeshDepthMaterial"><code class="notranslate" translate="no">MeshDepthMaterial</code></a> affiche la profondeur de chaque pixel où
+les pixels au <a href="/docs/#api/en/cameras/PerspectiveCamera#near"><code class="notranslate" translate="no">near</code></a> négatif de la caméra sont 0 et au <a href="/docs/#api/en/cameras/PerspectiveCamera#far"><code class="notranslate" translate="no">far</code></a> négatif sont 1.
+Certains effets spéciaux peuvent utiliser ces données, que nous aborderons
+ultérieurement.</p>
 <div class="spread">
   <div>
     <div data-diagram="MeshDepthMaterial"></div>
@@ -174,26 +209,30 @@ Certains effets spéciaux peuvent utiliser ces données que nous aborderons plus
 </div>
 
 <p>Le <a href="/docs/#api/en/materials/MeshNormalMaterial"><code class="notranslate" translate="no">MeshNormalMaterial</code></a> vous montrera les <em>normales</em> de la géométrie.
-Les <em>Normales</em> sont la direction d'un triangle ou d'un pixel particulier.
-<a href="/docs/#api/en/materials/MeshNormalMaterial"><code class="notranslate" translate="no">MeshNormalMaterial</code></a> dessine les normales de l'espace de vue (les normales par rapport à la caméra).</p>
-<p><span style="background: red;" class="color">x rouge</span>,
+Les <em>normales</em> sont la direction vers laquelle pointe un triangle ou un pixel particulier.
+<a href="/docs/#api/en/materials/MeshNormalMaterial"><code class="notranslate" translate="no">MeshNormalMaterial</code></a> dessine les normales de l'espace de vue (les normales relatives à la caméra).
+<span style="background: red;" class="color">x est rouge</span>,
 <span style="background: green;" class="dark-color">y est vert</span>, et
-<span style="background: blue;" class="dark-color">z est bleu</span> donc les choses tournées vers la droite seront <span style="background: #FF7F7F;" class="color">roses</span>,
-ceux vers la gauche seront <span style="background: #007F7F;" class="dark-color">aqua</span>,
-vers le haut <span style="background: #7FFF7F;" class="color">vert clair</span>,
-vers le bas <span style="background: #7F007F;" class="dark-color">violet</span>,
-et vers l'écran <span style="background: #7F7FFF;" class="color">lavande</span>.</p>
+<span style="background: blue;" class="dark-color">z est bleu</span> donc les choses orientées
+vers la droite seront <span style="background: #FF7F7F;" class="color">roses</span>,
+vers la gauche seront <span style="background: #007F7F;" class="dark-color">aqua</span>,
+vers le haut seront <span style="background: #7FFF7F;" class="color">vert clair</span>,
+vers le bas seront <span style="background: #7F007F;" class="dark-color">violettes</span>,
+et vers l'écran seront <span style="background: #7F7FFF;" class="color">lavande</span>.</p>
 <div class="spread">
   <div>
     <div data-diagram="MeshNormalMaterial"></div>
   </div>
 </div>
 
-<p><a href="/docs/#api/en/materials/ShaderMaterial"><code class="notranslate" translate="no">ShaderMaterial</code></a> permet de créer des matériaux personnalisés à l'aide du système de shader de Three.js. <a href="/docs/#api/en/materials/RawShaderMaterial"><code class="notranslate" translate="no">RawShaderMaterial</code></a> permet de créer des shaders entièrement personnalisés sans l'aide de Three.js. Ces deux sujets sont vastes et seront traités plus tard.</p>
-<p>La plupart des matériaux partagent un ensemble de paramètres, tous définis par <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>.
-<a href="/docs/#api/en/materials/Material">Voir la documentation</a> pour chacun d'eux, mais passons, ici, en revue deux des propriétés les plus utilisées.</p>
-<p><a href="/docs/#api/en/materials/Material#flatShading"><code class="notranslate" translate="no">flatShading</code></a>:
-si l'objet a l'air à facettes ou lisse. Par défaut = <code class="notranslate" translate="no">false</code>.</p>
+<p><a href="/docs/#api/en/materials/ShaderMaterial"><code class="notranslate" translate="no">ShaderMaterial</code></a> sert à créer des matériaux personnalisés à l'aide du système de shaders de three.js.
+<a href="/docs/#api/en/materials/RawShaderMaterial"><code class="notranslate" translate="no">RawShaderMaterial</code></a> sert à créer des shaders entièrement personnalisés sans aide de three.js.
+Ces deux sujets sont vastes et seront abordés plus tard.</p>
+<p>La plupart des matériaux partagent un ensemble de paramètres tous définis par <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>.
+<a href="/docs/#api/en/materials/Material">Consultez la documentation</a>
+pour les voir tous, mais passons en revue deux des propriétés les plus couramment utilisées.</p>
+<p><a href="/docs/#api/en/materials/Material#flatShading"><code class="notranslate" translate="no">flatShading</code></a> :
+si l'objet semble facetté ou lisse. défaut = <code class="notranslate" translate="no">false</code>.</p>
 <div class="spread">
   <div>
     <div data-diagram="smoothShading"></div>
@@ -205,9 +244,12 @@ si l'objet a l'air à facettes ou lisse. Par défaut = <code class="notranslate"
   </div>
 </div>
 
-<p><a href="/docs/#api/en/materials/Material#side"><code class="notranslate" translate="no">side</code></a>: quel côté montrer. La valeur par défaut est <code class="notranslate" translate="no">THREE.FrontSide</code>.
-Les autres options sont <code class="notranslate" translate="no">THREE.BackSide</code> et <code class="notranslate" translate="no">THREE.DoubleSide</code> (des deux côtés).
-La plupart des objets 3D dessinés dans Three.js sont probablement des solides opaques, il n'est donc pas nécessaire de dessiner les faces arrières (c'est-à-dire les côtés tournés vers l'intérieur du solide). La raison la plus courante de définir le côté, est pour les plans et les objets non solides où il est courant de voir leurs faces arrières.</p>
+<p><a href="/docs/#api/en/materials/Material#side"><code class="notranslate" translate="no">side</code></a> : quels côtés des triangles afficher. La valeur par défaut est <code class="notranslate" translate="no">THREE.FrontSide</code>.
+Les autres options sont <code class="notranslate" translate="no">THREE.BackSide</code> et <code class="notranslate" translate="no">THREE.DoubleSide</code> (les deux côtés).
+La plupart des objets 3D dessinés dans three.js sont probablement des solides opaques, donc les faces arrière
+(les faces tournées vers l'intérieur du solide) n'ont pas besoin d'être dessinées. La raison la plus courante
+de définir <code class="notranslate" translate="no">side</code> est pour les plans ou d'autres objets non solides où il est
+courant de voir les faces arrière des triangles.</p>
 <p>Voici 6 plans dessinés avec <code class="notranslate" translate="no">THREE.FrontSide</code> et <code class="notranslate" translate="no">THREE.DoubleSide</code>.</p>
 <div class="spread">
   <div>
@@ -220,25 +262,38 @@ La plupart des objets 3D dessinés dans Three.js sont probablement des solides o
   </div>
 </div>
 
-<p>Il y a vraiment beaucoup de choses à considérer avec les matériaux et il nous reste encore beaucoup à en dire. En particulier, nous avons jusqu'ici ignoré les textures, qui utilisent toute une série d'options. Avant de couvrir le domaine des textures, nous devons faire une pause et aborder <a href="setup.html">la configuration de votre environnement de développement</a></p>
+<p>Il y a vraiment beaucoup de choses à considérer avec les matériaux et nous en avons encore beaucoup
+à voir. En particulier, nous avons largement ignoré les textures qui ouvrent tout un éventail
+d'options. Cependant, avant d'aborder les textures, nous devons faire une pause et
+<a href="setup.html">configurer votre environnement de développement</a>.</p>
 <div class="threejs_bottombar">
 <h3>material.needsUpdate</h3>
 <p>
-Ce sujet affecte rarement la plupart des applications Three.js, mais juste pour information
-Three.js applique les paramètres de matériau lorsqu'un matériau est utilisé, où "utilisé" signifie "quelque chose est rendu qui utilise le matériau".
-Certains paramètres de matériau ne sont appliqués qu'une seule fois, car leur modification nécessite beaucoup de travail de la part de Three.js.
-Dans ces cas, vous devez définir <code class="notranslate" translate="no">material.needsUpdate = true</code> pour dire à Three.js d'appliquer vos modifications matérielles. Les paramètres les plus courants qui vous obligent à définir <code class="notranslate" translate="no">needsUpdate</code> si vous modifiez les paramètres après avoir utilisé le matériau sont :
+Ce sujet affecte rarement la plupart des applications three.js, mais juste pour information...
+Three.js applique les paramètres des matériaux lorsqu'un matériau est utilisé, où "utilisé"
+signifie "quelque chose qui est rendu utilise le matériau". Certains paramètres de matériau ne sont
+appliqués qu'une seule fois, car leur modification nécessite beaucoup de travail de la part de three.js.
+Dans ces cas, vous devez définir <code class="notranslate" translate="no">material.needsUpdate = true</code> pour indiquer à
+three.js d'appliquer vos modifications de matériau. Les paramètres les plus courants
+qui nécessitent de définir <code class="notranslate" translate="no">needsUpdate</code> si vous modifiez les paramètres après avoir
+utilisé le matériau sont :
 </p>
 <ul>
   <li><code class="notranslate" translate="no">flatShading</code></li>
-  <li>ajouter ou supprimer une texture
+  <li>ajout ou suppression d'une texture
     <p>
-    Changer une texture est possible, mais si vous voulez passer de, aucune texture à l'utilisation d'une texture, ou l'inverse, vous devrez définir <code class="notranslate" translate="no">needsUpdate = true</code>.
+    Changer une texture est acceptable, mais si vous souhaitez passer de l'absence de texture
+    à l'utilisation d'une texture, ou de l'utilisation d'une texture à l'absence de texture,
+    alors vous devez définir <code class="notranslate" translate="no">needsUpdate = true</code>.
     </p>
-    <p>Si vous souhaitez supprimer une texture, il est préférable de la remplacer par une texture blanche de 1 pixel de côté.</p>
+    <p>Dans le cas où l'on passe d'une texture à l'absence de texture, il est souvent
+    simplement préférable d'utiliser une texture blanche de 1x1 pixel.</p>
   </li>
 </ul>
-<p>Comme mentionné ci-dessus, la plupart des applications ne rencontrent jamais ces problèmes. La plupart des applications ne basculent pas entre l'ombrage plat et l'ombrage non plat. La plupart des applications utilisent également des textures ou une couleur unie pour un matériau donné, elles passent rarement de l'une à l'autre.
+<p>Comme mentionné ci-dessus, la plupart des applications ne rencontrent jamais ces problèmes. La plupart des applications
+ne basculent pas entre un ombrage plat et non plat. La plupart des applications
+utilisent également des textures ou une couleur unie pour un matériau donné ; elles basculent rarement
+de l'utilisation de l'un à l'utilisation de l'autre.
 </p>
 </div>
 
@@ -256,4 +311,4 @@ Dans ces cas, vous devez définir <code class="notranslate" translate="no">mater
 
 
 
-</body></html>
+</body></html>

+ 96 - 0
manual/fr/matrix-transformations.html

@@ -0,0 +1,96 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Transformations matricielles</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Transformations matricielles">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Transformations matricielles</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+
+          <p>
+            Three.js utilise des `matrices` pour encoder les transformations 3D : translations (position), rotations et mises à l'échelle. Chaque instance d'`Object3D` possède une `matrix` qui stocke la position, la rotation et l'échelle de cet objet. Cette page décrit comment mettre à jour la transformation d'un objet.
+            </p>
+
+            <h2>Propriétés de commodité et `matrixAutoUpdate`</h2>
+
+            <p>
+              Il existe deux manières de mettre à jour la transformation d'un objet :
+            </p>
+            <ol>
+              <li>
+                Modifiez les propriétés `position`, `quaternion` et `scale` de l'objet, et laissez three.js recalculer la matrice de l'objet à partir de ces propriétés :
+<pre class="prettyprint notranslate lang-js" translate="no">
+object.position.copy( start_position );
+object.quaternion.copy( quaternion );
+</pre>
+                Par défaut, la propriété `matrixAutoUpdate` est définie sur true, et la matrice sera automatiquement recalculée.
+                Si l'objet est statique, ou si vous souhaitez contrôler manuellement le moment où le recalcul se produit, de meilleures performances peuvent être obtenues en définissant la propriété sur false :
+<pre class="prettyprint notranslate lang-js" translate="no">
+object.matrixAutoUpdate = false;
+</pre>
+                Et après avoir modifié une propriété, mettez à jour la matrice manuellement :
+<pre class="prettyprint notranslate lang-js" translate="no">
+object.updateMatrix();
+</pre>
+              </li>
+              <li>
+                Modifiez la matrice de l'objet directement. La classe `Matrix4` dispose de différentes méthodes pour modifier la matrice :
+<pre class="prettyprint notranslate lang-js" translate="no">
+object.matrix.setRotationFromQuaternion( quaternion );
+object.matrix.setPosition( start_position );
+object.matrixAutoUpdate = false;
+</pre>
+                Notez que `matrixAutoUpdate` <em>doit</em> être défini sur `false` dans ce cas, et vous devez vous assurer de <em>ne pas</em> appeler `updateMatrix`. Appeler `updateMatrix` écrasera les modifications manuelles apportées à la matrice, en recalculant la matrice à partir de `position`, `scale`, etc.
+              </li>
+            </ol>
+
+            <h2>Matrices de l'objet et du monde</h2>
+            <p>
+            La matrice d'un objet stocke la transformation de l'objet <em>par rapport</em> à son parent ; pour obtenir la transformation de l'objet en coordonnées <em>du monde</em>, vous devez accéder à la matrice du monde de l'objet.
+            </p>
+            <p>
+            Lorsque la transformation du parent ou de l'enfant change, vous pouvez demander la mise à jour de la matrice du monde de l'objet enfant en appelant `object.updateMatrixWorld()`.
+            </p>
+            <p>
+            Un objet peut être transformé via `applyMatrix4()`. Note : En coulisses, cette méthode repose sur `Matrix4.decompose()`, et toutes les matrices ne sont pas décomposables de cette manière. Par exemple, si un objet a un parent avec une mise à l'échelle non uniforme, la matrice du monde de l'objet peut ne pas être décomposable, et cette méthode pourrait ne pas être appropriée.
+            </p>
+
+            <h2>Rotation et Quaternion</h2>
+            <p>
+            Three.js propose deux manières de représenter les rotations 3D : les angles d'Euler et les Quaternions, ainsi que des méthodes pour convertir entre les deux. Les angles d'Euler sont sujets à un problème appelé « verrouillage de cardan » (gimbal lock), où certaines configurations peuvent perdre un degré de liberté (empêchant l'objet de tourner autour d'un axe). Pour cette raison, les rotations d'objets sont <em>toujours</em> stockées dans le quaternion de l'objet.
+            </p>
+            <p>
+            Les versions précédentes de la librairie incluaient une propriété `useQuaternion` qui, lorsqu'elle était définie sur false, entraînait le calcul de la matrice de l'objet à partir d'un angle d'Euler. Cette pratique est obsolète --- à la place, vous devriez utiliser la méthode `object.setRotationFromEuler()`, qui mettra à jour le quaternion.
+            </p>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 615 - 5
manual/fr/multiple-scenes.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Multiple Canvases Multiple Scenes</title>
+    <title>Plusieurs Canvases, Plusieurs Scènes</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Multiple Canvases Multiple Scenes">
+    <meta name="twitter:title" content="Three.js – Plusieurs Canvases, Plusieurs Scènes">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,622 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Multiple Canvases Multiple Scenes</h1>
+        <h1>Plusieurs Canvases, Plusieurs Scènes</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/multiple-scenes.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Une question courante est de savoir comment utiliser THREE.js avec plusieurs canvases.
+Disons que vous voulez faire un site de commerce électronique ou que vous voulez créer
+une page avec beaucoup de diagrammes 3D. À première vue, cela semble facile.
+Faites simplement un canvas partout où vous voulez un diagramme. Pour chaque canvas,
+créez un <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a>.</p>
+<p>Vous découvrirez rapidement, cependant, que vous rencontrez des problèmes.</p>
+<ol>
+<li><p>Le navigateur limite le nombre de contextes WebGL que vous pouvez avoir.</p>
+<p>Typiquement, cette limite est d'environ 8. Dès que vous créez
+le 9ème contexte, le plus ancien sera perdu.</p>
+</li>
+<li><p>Les ressources WebGL ne peuvent pas être partagées entre les contextes</p>
+<p>Cela signifie que si vous voulez charger un modèle de 10 Mo dans 2 canvases
+et que ce modèle utilise 20 Mo de textures, votre modèle de 10 Mo devra
+être chargé deux fois et vos textures seront également chargées
+deux fois. Rien ne peut être partagé entre les contextes. Cela
+signifie également que les choses doivent être initialisées deux fois, les shaders compilés deux fois,
+etc. Cela empire à mesure qu'il y a plus de canvases.</p>
+</li>
+</ol>
+<p>Alors, quelle est la solution ?</p>
+<p>La solution est un canvas qui remplit la zone d'affichage en arrière-plan et un autre élément pour représenter chaque canvas "virtuel". Nous créons un seul <a href="/docs/#api/en/constants/Renderer"><code class="notranslate" translate="no">Renderer</code></a>, puis une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> pour chaque canvas virtuel. Nous vérifierons ensuite les positions des éléments de canvas virtuels et s'ils sont à l'écran, nous demanderons à THREE.js de dessiner leur scène à l'endroit correct.</p>
+<p>Avec cette solution, il n'y a qu'un seul canvas, nous résolvons donc les problèmes 1
+et 2 ci-dessus. Nous ne rencontrerons pas la limite de contextes WebGL car nous
+n'utiliserons qu'un seul contexte. Nous ne rencontrerons pas non plus les problèmes
+de partage pour les mêmes raisons.</p>
+<p>Commençons par un exemple simple avec seulement 2 scènes. D'abord, nous allons
+créer le HTML</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
+&lt;p&gt;
+  &lt;span id="box" class="diagram left"&gt;&lt;/span&gt;
+  J'aime les boîtes. Les cadeaux viennent dans des boîtes.
+  Quand je trouve une nouvelle boîte, je suis toujours impatient de découvrir ce qu'il y a dedans.
+&lt;/p&gt;
+&lt;p&gt;
+  &lt;span id="pyramid" class="diagram right"&gt;&lt;/span&gt;
+  Quand j'étais enfant, je rêvais de partir en expédition à l'intérieur d'une pyramide
+  et de trouver un tombeau inconnu rempli de momies et de trésors.
+&lt;/p&gt;
+</pre>
+<p>Ensuite, nous pouvons configurer le CSS peut-être comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  display: block;
+  z-index: -1;
+}
+.diagram {
+  display: inline-block;
+  width: 5em;
+  height: 3em;
+  border: 1px solid black;
+}
+.left {
+  float: left;
+  margin-right: .25em;
+}
+.right {
+  float: right;
+  margin-left: .25em;
+}
+</pre>
+<p>Nous configurons le canvas pour qu'il remplisse l'écran et nous définissons son <code class="notranslate" translate="no">z-index</code> à
+-1 pour qu'il apparaisse derrière les autres éléments. Nous devons également spécifier une sorte de largeur et de hauteur
+pour nos éléments de canvas virtuels puisqu'il n'y a rien à l'intérieur
+pour leur donner une taille.</p>
+<p>Maintenant, nous allons créer 2 scènes, chacune avec une lumière et une caméra.
+À une scène, nous ajouterons un cube et à l'autre une sphère.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeScene(elem) {
+  const scene = new THREE.Scene();
+
+  const fov = 45;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 5;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 2;
+  camera.position.set(0, 1, 2);
+  camera.lookAt(0, 0, 0);
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    scene.add(light);
+  }
+
+  return {scene, camera, elem};
+}
+
+function setupScene1() {
+  const sceneInfo = makeScene(document.querySelector('#box'));
+  const geometry = new THREE.BoxGeometry(1, 1, 1);
+  const material = new THREE.MeshPhongMaterial({color: 'red'});
+  const mesh = new THREE.Mesh(geometry, material);
+  sceneInfo.scene.add(mesh);
+  sceneInfo.mesh = mesh;
+  return sceneInfo;
+}
+
+function setupScene2() {
+  const sceneInfo = makeScene(document.querySelector('#pyramid'));
+  const radius = .8;
+  const widthSegments = 4;
+  const heightSegments = 2;
+  const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
+  const material = new THREE.MeshPhongMaterial({
+    color: 'blue',
+    flatShading: true,
+  });
+  const mesh = new THREE.Mesh(geometry, material);
+  sceneInfo.scene.add(mesh);
+  sceneInfo.mesh = mesh;
+  return sceneInfo;
+}
+
+const sceneInfo1 = setupScene1();
+const sceneInfo2 = setupScene2();
+</pre>
+<p>Et ensuite, nous allons créer une fonction pour rendre chaque scène
+uniquement si l'élément est à l'écran. Nous pouvons indiquer à THREE.js
+de ne rendre qu'une partie du canvas en activant le test <em>scissor</em>
+avec <a href="/docs/#api/en/constants/Renderer.setScissorTest"><code class="notranslate" translate="no">Renderer.setScissorTest</code></a>, puis en définissant à la fois le scissor et le viewport avec <a href="/docs/#api/en/constants/Renderer.setViewport"><code class="notranslate" translate="no">Renderer.setViewport</code></a> et <a href="/docs/#api/en/constants/Renderer.setScissor"><code class="notranslate" translate="no">Renderer.setScissor</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function renderSceneInfo(sceneInfo) {
+  const {scene, camera, elem} = sceneInfo;
+
+  // obtenir la position relative à la zone d'affichage de cet élément
+  const {left, right, top, bottom, width, height} =
+      elem.getBoundingClientRect();
+
+  const isOffscreen =
+      bottom &lt; 0 ||
+      top &gt; renderer.domElement.clientHeight ||
+      right &lt; 0 ||
+      left &gt; renderer.domElement.clientWidth;
+
+  if (isOffscreen) {
+    return;
+  }
+
+  camera.aspect = width / height;
+  camera.updateProjectionMatrix();
+
+  const positiveYUpBottom = canvasRect.height - bottom;
+  renderer.setScissor(left, positiveYUpBottom, width, height);
+  renderer.setViewport(left, positiveYUpBottom, width, height);
+
+  renderer.render(scene, camera);
+}
+</pre>
+<p>Et ensuite, notre fonction de rendu commencera par effacer l'écran,
+puis rendra chaque scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  resizeRendererToDisplaySize(renderer);
+
+  renderer.setScissorTest(false);
+  renderer.clear(true, true);
+  renderer.setScissorTest(true);
+
+  sceneInfo1.mesh.rotation.y = time * .1;
+  sceneInfo2.mesh.rotation.y = time * .1;
+
+  renderSceneInfo(sceneInfo1);
+  renderSceneInfo(sceneInfo2);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Et voici le résultat</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/multiple-scenes-v1.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/multiple-scenes-v1.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Vous pouvez voir où se trouve le premier <code class="notranslate" translate="no">&lt;span&gt;</code>, il y a un cube rouge, et où se trouve le deuxième <code class="notranslate" translate="no">span</code>, il y a une sphère bleue.</p>
+<h2 id="syncing-up">Synchronisation</h2>
+<p>Le code ci-dessus fonctionne, mais il y a un petit problème.
+Si vos scènes sont compliquées ou si, pour une raison quelconque,
+le rendu prend trop de temps, la position des scènes
+dessinées dans le canvas sera en décalage par rapport au reste de la page.</p>
+<p>Si nous donnons une bordure à chaque zone </p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">.diagram {
+  display: inline-block;
+  width: 5em;
+  height: 3em;
++  border: 1px solid black;
+}
+</pre>
+<p>Et nous définissons le fond de chaque scène</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
++scene.background = new THREE.Color('red');
+</pre>
+<p>Et si nous <a href="../examples/multiple-scenes-v2.html" target="_blank">faisons défiler rapidement de haut en bas</a>, nous verrons le problème. Voici une animation du défilement ralenti par 10.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/multi-view-skew.gif"></div>
+
+<p>Nous pouvons passer à une méthode différente qui présente un compromis différent. Nous allons changer le CSS du canvas de <code class="notranslate" translate="no">position: fixed</code> à <code class="notranslate" translate="no">position: absolute</code>. </p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
+-  position: fixed;
++  position: absolute;
+</pre>
+<p>Ensuite, nous définirons la transformation du canvas pour le déplacer afin
+que le haut du canvas soit au niveau du haut de la partie
+de la page actuellement défilée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  ...
+
+  const transform = `translateY(${window.scrollY}px)`;
+  renderer.domElement.style.transform = transform;
+</pre>
+<p><code class="notranslate" translate="no">position: fixed</code> empêchait le canvas de défiler du tout
+tandis que le reste de la page défilait par-dessus. <code class="notranslate" translate="no">position: absolute</code>
+permettra au canvas de défiler avec le reste de la page, ce qui signifie
+que ce que nous dessinons restera avec la page pendant le défilement,
+même si nous sommes trop lents à rendre. Lorsque nous aurons enfin l'occasion de rendre,
+nous déplacerons le canvas pour qu'il corresponde à l'endroit où la page
+a été défilée, puis nous referons le rendu. Cela signifie que seuls les bords
+de la fenêtre montreront des morceaux non rendus pendant un instant, mais <a href="../examples/multiple-scenes-v2.html" target="_blank">le contenu
+au milieu de la page devrait correspondre</a> et ne pas glisser. Voici une vue
+des résultats de la nouvelle méthode ralentie par 10.</p>
+<div class="threejs_center"><img class="border" src="../resources/images/multi-view-fixed.gif"></div>
+
+<h2 id="making-it-more-generic">Généraliser le code</h2>
+<p>Maintenant que nous avons fait fonctionner plusieurs scènes, rendons cela un peu plus générique.</p>
+<p>Nous pourrions faire en sorte que la fonction de rendu principale, celle qui gère le canvas, contienne simplement une liste d'éléments et leur fonction de rendu associée. Pour chaque élément, elle vérifierait si l'élément est à l'écran et, si oui, appellerait la fonction de rendu correspondante. De cette manière, nous aurions un système générique où les scènes individuelles ne sont pas vraiment conscientes d'être rendues dans un espace plus petit.</p>
+<p>Voici la fonction de rendu principale</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sceneElements = [];
+function addScene(elem, fn) {
+  sceneElements.push({elem, fn});
+}
+
+function render(time) {
+  time *= 0.001;
+
+  resizeRendererToDisplaySize(renderer);
+
+  renderer.setScissorTest(false);
+  renderer.setClearColor(clearColor, 0);
+  renderer.clear(true, true);
+  renderer.setScissorTest(true);
+
+  const transform = `translateY(${window.scrollY}px)`;
+  renderer.domElement.style.transform = transform;
+
+  for (const {elem, fn} of sceneElements) {
+    // obtenir la position relative à la zone d'affichage de cet élément
+    const rect = elem.getBoundingClientRect();
+    const {left, right, top, bottom, width, height} = rect;
+
+    const isOffscreen =
+        bottom &lt; 0 ||
+        top &gt; renderer.domElement.clientHeight ||
+        right &lt; 0 ||
+        left &gt; renderer.domElement.clientWidth;
+
+    if (!isOffscreen) {
+      const positiveYUpBottom = renderer.domElement.clientHeight - bottom;
+      renderer.setScissor(left, positiveYUpBottom, width, height);
+      renderer.setViewport(left, positiveYUpBottom, width, height);
+
+      fn(time, rect);
+    }
+  }
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Vous pouvez voir qu'elle boucle sur <code class="notranslate" translate="no">sceneElements</code>, qui est censé être un tableau d'objets, chacun ayant une propriété <code class="notranslate" translate="no">elem</code> et <code class="notranslate" translate="no">fn</code>.</p>
+<p>Elle vérifie si l'élément est à l'écran. Si c'est le cas, elle appelle <code class="notranslate" translate="no">fn</code> et lui passe l'heure actuelle et son rectangle.</p>
+<p>Maintenant, le code de configuration pour chaque scène s'ajuste simplement pour s'ajouter à la liste des scènes</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const elem = document.querySelector('#box');
+  const {scene, camera} = makeScene();
+  const geometry = new THREE.BoxGeometry(1, 1, 1);
+  const material = new THREE.MeshPhongMaterial({color: 'red'});
+  const mesh = new THREE.Mesh(geometry, material);
+  scene.add(mesh);
+  addScene(elem, (time, rect) =&gt; {
+    camera.aspect = rect.width / rect.height;
+    camera.updateProjectionMatrix();
+    mesh.rotation.y = time * .1;
+    renderer.render(scene, camera);
+  });
+}
+
+{
+  const elem = document.querySelector('#pyramid');
+  const {scene, camera} = makeScene();
+  const radius = .8;
+  const widthSegments = 4;
+  const heightSegments = 2;
+  const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
+  const material = new THREE.MeshPhongMaterial({
+    color: 'blue',
+    flatShading: true,
+  });
+  const mesh = new THREE.Mesh(geometry, material);
+  scene.add(mesh);
+  addScene(elem, (time, rect) =&gt; {
+    camera.aspect = rect.width / rect.height;
+    camera.updateProjectionMatrix();
+    mesh.rotation.y = time * .1;
+    renderer.render(scene, camera);
+  });
+}
+</pre>
+<p>Avec cela, nous n'avons plus besoin de <code class="notranslate" translate="no">sceneInfo1</code> et <code class="notranslate" translate="no">sceneInfo2</code>, et le code qui faisait pivoter les maillages est maintenant spécifique à chaque scène.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/multiple-scenes-generic.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/multiple-scenes-generic.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<h2 id="using-html-dataset">Utilisation de l'attribut dataset HTML</h2>
+<p>Une dernière chose encore plus générique que nous pouvons faire est d'utiliser l'<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset">attribut dataset HTML</a>. C'est une façon d'ajouter vos propres données à un élément HTML. Au lieu d'utiliser <code class="notranslate" translate="no">id="..."</code>, nous utiliserons <code class="notranslate" translate="no">data-diagram="..."</code> comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
+&lt;p&gt;
+-  &lt;span id="box" class="diagram left"&gt;&lt;/span&gt;
++  &lt;span data-diagram="box" class="left"&gt;&lt;/span&gt;
+  J'aime les boîtes. Les cadeaux viennent dans des boîtes.
+  Quand je trouve une nouvelle boîte, je suis toujours impatient de découvrir ce qu'il y a dedans.
+&lt;/p&gt;
+&lt;p&gt;
+-  &lt;span id="pyramid" class="diagram left"&gt;&lt;/span&gt;
++  &lt;span data-diagram="pyramid" class="right"&gt;&lt;/span&gt;
+  Quand j'étais enfant, je rêvais de partir en expédition à l'intérieur d'une pyramide
+  et de trouver un tombeau inconnu rempli de momies et de trésors.
+&lt;/p&gt;
+</pre>
+<p>Nous pouvons ensuite modifier le sélecteur CSS pour sélectionner cela</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">-.diagram
++*[data-diagram] {
+  display: inline-block;
+  width: 5em;
+  height: 3em;
+}
+</pre>
+<p>Nous allons modifier le code de configuration de la scène pour qu'il soit simplement une correspondance de noms avec des <em>fonctions d'initialisation de scène</em> qui renvoient une <em>fonction de rendu de scène</em>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sceneInitFunctionsByName = {
+  'box': () =&gt; {
+    const {scene, camera} = makeScene();
+    const geometry = new THREE.BoxGeometry(1, 1, 1);
+    const material = new THREE.MeshPhongMaterial({color: 'red'});
+    const mesh = new THREE.Mesh(geometry, material);
+    scene.add(mesh);
+    return (time, rect) =&gt; {
+      mesh.rotation.y = time * .1;
+      camera.aspect = rect.width / rect.height;
+      camera.updateProjectionMatrix();
+      renderer.render(scene, camera);
+    };
+  },
+  'pyramid': () =&gt; {
+    const {scene, camera} = makeScene();
+    const radius = .8;
+    const widthSegments = 4;
+    const heightSegments = 2;
+    const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
+    const material = new THREE.MeshPhongMaterial({
+      color: 'blue',
+      flatShading: true,
+    });
+    const mesh = new THREE.Mesh(geometry, material);
+    scene.add(mesh);
+    return (time, rect) =&gt; {
+      mesh.rotation.y = time * .1;
+      camera.aspect = rect.width / rect.height;
+      camera.updateProjectionMatrix();
+      renderer.render(scene, camera);
+    };
+  },
+};
+</pre>
+<p>Et pour initialiser, nous pouvons simplement utiliser <code class="notranslate" translate="no">querySelectorAll</code> pour trouver tous les diagrammes et appeler la fonction d'initialisation correspondante pour ce diagramme. </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">document.querySelectorAll('[data-diagram]').forEach((elem) =&gt; {
+  const sceneName = elem.dataset.diagram;
+  const sceneInitFunction = sceneInitFunctionsByName[sceneName];
+  const sceneRenderFunction = sceneInitFunction(elem);
+  addScene(elem, sceneRenderFunction);
+});
+</pre>
+<p>Pas de changement visuel, mais le code est encore plus générique.</p>
+<p></p>
+<h2 id="adding-controls-to-each-element">Ajout de Contrôles à chaque élément</h2>
+<p>Ajouter de l'interactivité, par exemple un <code class="notranslate" translate="no">TrackballControls</code>, est tout aussi simple. Nous ajoutons d'abord le script pour le contrôle.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {TrackballControls} from 'three/addons/controls/TrackballControls.js';
+</pre>
+<p>Et ensuite, nous pouvons ajouter un <code class="notranslate" translate="no">TrackballControls</code> à chaque scène en passant l'élément associé à cette scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function makeScene() {
++function makeScene(elem) {
+  const scene = new THREE.Scene();
+
+  const fov = 45;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 5;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.set(0, 1, 2);
+  camera.lookAt(0, 0, 0);
++  scene.add(camera);
+
++  const controls = new TrackballControls(camera, elem);
++  controls.noZoom = true;
++  controls.noPan = true;
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+-    scene.add(light);
++    camera.add(light);
+  }
+
+-  return {scene, camera};
++ return {scene, camera, controls};
+}
+</pre>
+<p>Vous remarquerez que nous avons ajouté la caméra à la scène et la lumière à la caméra.
+Cela rend la lumière relative à la caméra. Comme les <code class="notranslate" translate="no">TrackballControls</code>
+déplacent la caméra, c'est probablement ce que nous voulons.
+Cela permet de maintenir la lumière éclairant le côté de l'objet que nous regardons.</p>
+<p>Nous devons mettre à jour ces contrôles dans nos fonctions de rendu</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sceneInitFunctionsByName = {
+- 'box': () =&gt; {
+-    const {scene, camera} = makeScene();
++ 'box': (elem) =&gt; {
++    const {scene, camera, controls} = makeScene(elem);
+    const geometry = new THREE.BoxGeometry(1, 1, 1);
+    const material = new THREE.MeshPhongMaterial({color: 'red'});
+    const mesh = new THREE.Mesh(geometry, material);
+    scene.add(mesh);
+    return (time, rect) =&gt; {
+      mesh.rotation.y = time * .1;
+      camera.aspect = rect.width / rect.height;
+      camera.updateProjectionMatrix();
++      controls.handleResize();
++      controls.update();
+      renderer.render(scene, camera);
+    };
+  },
+-  'pyramid': () =&gt; {
+-    const {scene, camera} = makeScene();
++  'pyramid': (elem) =&gt; {
++    const {scene, camera, controls} = makeScene(elem);
+    const radius = .8;
+    const widthSegments = 4;
+    const heightSegments = 2;
+    const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
+    const material = new THREE.MeshPhongMaterial({
+      color: 'blue',
+      flatShading: true,
+    });
+    const mesh = new THREE.Mesh(geometry, material);
+    scene.add(mesh);
+    return (time, rect) =&gt; {
+      mesh.rotation.y = time * .1;
+      camera.aspect = rect.width / rect.height;
+      camera.updateProjectionMatrix();
++      controls.handleResize();
++      controls.update();
+      renderer.render(scene, camera);
+    };
+  },
+};
+</pre>
+<p>Et maintenant, si vous faites glisser les objets, ils pivoteront.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/multiple-scenes-controls.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/multiple-scenes-controls.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Ces techniques sont utilisées sur ce site même. En particulier, <a href="primitives.html">l'article sur les primitives</a> et <a href="materials.html">l'article sur les matériaux</a> utilisent cette technique pour ajouter les différents exemples tout au long de l'article.</p>
+<p>Une autre solution consisterait à rendre sur un canvas hors écran et à copier le résultat sur un canvas 2D à chaque élément.
+L'avantage de cette solution est qu'il n'y a aucune limite à la manière dont vous pouvez composer chaque zone séparée. Avec la solution précédente,
+nous avions un seul canvas en arrière-plan. Avec cette solution, nous avons des éléments HTML normaux.</p>
+<p>L'inconvénient est que c'est plus lent car une copie doit avoir lieu pour chaque zone. La lenteur dépend du navigateur
+et du GPU.</p>
+<p>Les modifications nécessaires sont assez minimes</p>
+<p>D'abord, nous allons modifier le HTML car nous n'avons plus besoin d'un canvas sur la page</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+-  &lt;canvas id="c"&gt;&lt;/canvas&gt;
+  ...
+&lt;/body&gt;
+</pre>
+<p>puis nous allons modifier le CSS</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">-#c {
+-  position: absolute;
+-  left: 0;
+-  top: 0;
+-  width: 100%;
+-  height: 100%;
+-  display: block;
+-  z-index: -1;
+-}
+canvas {
+  width: 100%;
+  height: 100%;
+  display: block;
+}
+*[data-diagram] {
+  display: inline-block;
+  width: 5em;
+  height: 3em;
+}
+</pre><p>Nous avons fait en sorte que tous les canvases remplissent leur conteneur.</p>
+<p>Maintenant, changeons le JavaScript. D'abord, nous ne recherchons plus
+le canvas. Au lieu de cela, nous en créons un. Nous activons également
+simplement le test scissor au début.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+-  const canvas = document.querySelector('#c');
++  const canvas = document.createElement('canvas');
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas, alpha: true});
++  renderer.setScissorTest(true);
+
+  ...
+</pre>
+<p>Ensuite, pour chaque scène, nous créons un contexte de rendu 2D et
+ajoutons son canvas à l'élément pour cette scène</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sceneElements = [];
+function addScene(elem, fn) {
++  // ajouter un canvas à l'élément
++  const ctx = document.createElement('canvas').getContext('2d');
++  elem.appendChild(ctx.canvas);
+-  sceneElements.push({elem, fn});
++  sceneElements.push({elem, ctx, fn});
+}
+</pre>
+<p>Ensuite, lors du rendu, si le canvas du renderer n'est pas
+assez grand pour rendre cette zone, nous augmentons sa taille.
+De même, si le canvas de cette zone n'a pas la bonne taille, nous
+changeons sa taille. Enfin, nous définissons le scissor et le viewport,
+rendons la scène pour cette zone, puis copions le résultat sur le canvas de la zone.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+-  resizeRendererToDisplaySize(renderer);
+-
+-  renderer.setScissorTest(false);
+-  renderer.setClearColor(clearColor, 0);
+-  renderer.clear(true, true);
+-  renderer.setScissorTest(true);
+-
+-  const transform = `translateY(${window.scrollY}px)`;
+-  renderer.domElement.style.transform = transform;
+
+-  for (const {elem, fn} of sceneElements) {
++  for (const {elem, fn, ctx} of sceneElements) {
+    // obtenir la position relative à la zone d'affichage de cet élément
+    const rect = elem.getBoundingClientRect();
+    const {left, right, top, bottom, width, height} = rect;
++    const rendererCanvas = renderer.domElement;
+
+    const isOffscreen =
+        bottom &lt; 0 ||
+-        top &gt; renderer.domElement.clientHeight ||
++        top &gt; window.innerHeight ||
+        right &lt; 0 ||
+-        left &gt; renderer.domElement.clientWidth;
++        left &gt; window.innerWidth;
+
+    if (!isOffscreen) {
+-      const positiveYUpBottom = renderer.domElement.clientHeight - bottom;
+-      renderer.setScissor(left, positiveYUpBottom, width, height);
+-      renderer.setViewport(left, positiveYUpBottom, width, height);
+
++      // s'assurer que le canvas du renderer est assez grand
++      if (rendererCanvas.width &lt; width || rendererCanvas.height &lt; height) {
++        renderer.setSize(width, height, false);
++      }
++
++      // s'assurer que le canvas pour cette zone est de la même taille que la zone
++      if (ctx.canvas.width !== width || ctx.canvas.height !== height) {
++        ctx.canvas.width = width;
++        ctx.canvas.height = height;
++      }
++
++      renderer.setScissor(0, 0, width, height);
++      renderer.setViewport(0, 0, width, height);
+
+      fn(time, rect);
+
++      // copier la scène rendue sur le canvas de cet élément
++      ctx.globalCompositeOperation = 'copy';
++      ctx.drawImage(
++          rendererCanvas,
++          0, rendererCanvas.height - height, width, height,  // src rect
++          0, 0, width, height);                              // dst rect
+    }
+  }
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p>Le résultat est identique</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/multiple-scenes-copy-canvas.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/multiple-scenes-copy-canvas.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Un autre avantage de cette solution est que vous pourriez potentiellement utiliser
+<a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas"><code class="notranslate" translate="no">OffscreenCanvas</code></a>
+pour rendre depuis un web worker et toujours utiliser cette technique. Malheureusement, en juillet 2020,
+<code class="notranslate" translate="no">OffscreenCanvas</code> n'est pris en charge que par Chrome.</p>
 
         </div>
       </div>

+ 1049 - 2
manual/fr/offscreencanvas.html

@@ -26,8 +26,1055 @@
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/offscreencanvas.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p><a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas"><code class="notranslate" translate="no">OffscreenCanvas</code></a>
+est une fonctionnalité de navigateur relativement nouvelle, actuellement disponible uniquement dans Chrome mais apparemment
+à venir sur d'autres navigateurs. <code class="notranslate" translate="no">OffscreenCanvas</code> permet à un web worker de rendre
+sur un canevas. C'est une façon de décharger le travail lourd, comme le rendu d'une scène 3D complexe,
+sur un web worker afin de ne pas ralentir la réactivité du navigateur. Cela
+signifie également que les données sont chargées et analysées dans le worker, ce qui réduit potentiellement les saccades pendant
+le chargement de la page.</p>
+<p>Commencer à l'utiliser est assez simple. Portons l'exemple des 3 cubes en rotation depuis <a href="responsive.html">l'article sur la réactivité</a>.</p>
+<p>En général, les workers ont leur code séparé
+dans un autre fichier script, tandis que la plupart des exemples sur ce site ont leurs
+scripts intégrés dans le fichier HTML de la page sur laquelle ils se trouvent.</p>
+<p>Dans notre cas, nous allons créer un fichier appelé <code class="notranslate" translate="no">offscreencanvas-cubes.js</code> et
+y copier tout le JavaScript depuis <a href="responsive.html">l'exemple réactif</a>. Nous apporterons ensuite
+les modifications nécessaires pour qu'il s'exécute dans un worker.</p>
+<p>Nous avons encore besoin de JavaScript dans notre fichier HTML. La première chose
+à faire est de trouver le canevas, puis de transférer son contrôle
+pour qu'il soit offscreen en appelant <code class="notranslate" translate="no">canvas.transferControlToOffscreen</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const offscreen = canvas.transferControlToOffscreen();
+
+  ...
+</pre>
+<p>Nous pouvons ensuite démarrer notre worker avec <code class="notranslate" translate="no">new Worker(pathToScript, {type: 'module'})</code>.
+et lui passer l'objet <code class="notranslate" translate="no">offscreen</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const offscreen = canvas.transferControlToOffscreen();
+  const worker = new Worker('offscreencanvas-cubes.js', {type: 'module'});
+  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
+}
+main();
+</pre>
+<p>Il est important de noter que les workers ne peuvent pas accéder au <code class="notranslate" translate="no">DOM</code>. Ils
+ne peuvent pas regarder les éléments HTML ni recevoir les événements de souris ou
+de clavier. La seule chose qu'ils peuvent généralement faire est de répondre
+aux messages qui leur sont envoyés et de renvoyer des messages à la page.</p>
+<p>Pour envoyer un message à un worker, nous appelons <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage"><code class="notranslate" translate="no">worker.postMessage</code></a> et
+lui passons 1 ou 2 arguments. Le premier argument est un objet JavaScript
+qui sera <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm">cloné</a>
+et envoyé au worker. Le second argument est un tableau optionnel
+d'objets qui font partie du premier objet et que nous voulons <em>transférer</em>
+au worker. Ces objets ne seront pas clonés. Au lieu de cela, ils seront <em>transférés</em>
+et cesseront d'exister dans la page principale. Cesser d'exister est probablement
+la mauvaise description, ils sont plutôt neutralisés. Seuls certains types d'objets
+peuvent être transférés au lieu d'être clonés. Ils incluent <code class="notranslate" translate="no">OffscreenCanvas</code>,
+donc une fois transféré, l'objet <code class="notranslate" translate="no">offscreen</code> dans la page principale devient inutile.</p>
+<p>Les workers reçoivent les messages via leur gestionnaire <code class="notranslate" translate="no">onmessage</code>. L'objet
+que nous avons passé à <code class="notranslate" translate="no">postMessage</code> arrive sur <code class="notranslate" translate="no">event.data</code> passé au gestionnaire <code class="notranslate" translate="no">onmessage</code>
+sur le worker. Le code ci-dessus déclare un <code class="notranslate" translate="no">type: 'main'</code> dans l'objet qu'il passe
+au worker. Cet objet n'a aucune signification pour le navigateur. Il est entièrement destiné
+à notre propre usage. Nous allons créer un gestionnaire qui, basé sur le <code class="notranslate" translate="no">type</code>, appelle
+une fonction différente dans le worker. Ensuite, nous pourrons ajouter des fonctions au besoin et
+les appeler facilement depuis la page principale.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const handlers = {
+  main,
+};
+
+self.onmessage = function(e) {
+  const fn = handlers[e.data.type];
+  if (typeof fn !== 'function') {
+    throw new Error('no handler for type: ' + e.data.type);
+  }
+  fn(e.data);
+};
+</pre>
+<p>Vous pouvez voir ci-dessus que nous recherchons simplement le gestionnaire basé sur le <code class="notranslate" translate="no">type</code> et que nous lui passons les <code class="notranslate" translate="no">data</code>
+qui ont été envoyées depuis la page principale.</p>
+<p>Il ne nous reste plus qu'à commencer à modifier la fonction <code class="notranslate" translate="no">main</code> que nous avons collée dans
+<code class="notranslate" translate="no">offscreencanvas-cubes.js</code> depuis <a href="responsive.html">l'article sur la réactivité</a>.</p>
+<p>Au lieu de rechercher le canevas depuis le DOM, nous le recevrons des données d'événement.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function main() {
+-  const canvas = document.querySelector('#c');
++function main(data) {
++  const {canvas} = data;
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+
+  ...
+</pre>
+<p>En gardant à l'esprit que les workers ne peuvent pas voir le DOM du tout, le premier problème
+que nous rencontrons est que <code class="notranslate" translate="no">resizeRendererToDisplaySize</code> ne peut pas lire <code class="notranslate" translate="no">canvas.clientWidth</code>
+et <code class="notranslate" translate="no">canvas.clientHeight</code> car ce sont des valeurs DOM. Voici le code original</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function resizeRendererToDisplaySize(renderer) {
+  const canvas = renderer.domElement;
+  const width = canvas.clientWidth;
+  const height = canvas.clientHeight;
+  const needResize = canvas.width !== width || canvas.height !== height;
+  if (needResize) {
+    renderer.setSize(width, height, false);
+  }
+  return needResize;
+}
+</pre>
+<p>Au lieu de cela, nous devrons envoyer les tailles au worker dès qu'elles changent.
+Ajoutons donc un état global et conservons la largeur et la hauteur à cet endroit.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const state = {
+  width: 300,  // par défaut du canevas
+  height: 150,  // par défaut du canevas
+};
+</pre>
+<p>Ensuite, ajoutons un gestionnaire <code class="notranslate" translate="no">'size'</code> pour mettre à jour ces valeurs. </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function size(data) {
++  state.width = data.width;
++  state.height = data.height;
++}
+
+const handlers = {
+  main,
++  size,
+};
+</pre>
+<p>Maintenant, nous pouvons modifier <code class="notranslate" translate="no">resizeRendererToDisplaySize</code> pour utiliser <code class="notranslate" translate="no">state.width</code> et <code class="notranslate" translate="no">state.height</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function resizeRendererToDisplaySize(renderer) {
+  const canvas = renderer.domElement;
+-  const width = canvas.clientWidth;
+-  const height = canvas.clientHeight;
++  const width = state.width;
++  const height = state.height;
+  const needResize = canvas.width !== width || canvas.height !== height;
+  if (needResize) {
+    renderer.setSize(width, height, false);
+  }
+  return needResize;
+}
+</pre>
+<p>et là où nous calculons l'aspect, nous avons besoin de changements similaires</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+-    camera.aspect = canvas.clientWidth / canvas.clientHeight;
++    camera.aspect = state.width / state.height;
+    camera.updateProjectionMatrix();
+  }
+
+  ...
+</pre>
+<p>De retour dans la page principale, nous enverrons un événement <code class="notranslate" translate="no">size</code> chaque fois que la page change de taille.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const worker = new Worker('offscreencanvas-picking.js', {type: 'module'});
+worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
+
++function sendSize() {
++  worker.postMessage({
++    type: 'size',
++    width: canvas.clientWidth,
++    height: canvas.clientHeight,
++  });
++}
++
++window.addEventListener('resize', sendSize);
++sendSize();
+</pre>
+<p>Nous l'appelons également une fois pour envoyer la taille initiale.</p>
+<p>Et avec ces quelques modifications seulement, en supposant que votre navigateur prenne entièrement en charge <code class="notranslate" translate="no">OffscreenCanvas</code>,
+cela devrait fonctionner. Avant de l'exécuter, vérifions si le navigateur prend réellement en charge
+<code class="notranslate" translate="no">OffscreenCanvas</code> et, si ce n'est pas le cas, affichons une erreur. Ajoutons d'abord du HTML pour afficher l'erreur.</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="noOffscreenCanvas" style="display:none;"&gt;
++    &lt;div&gt;no OffscreenCanvas support&lt;/div&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>et un peu de CSS pour cela</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#noOffscreenCanvas {
+    display: flex;
+    width: 100%;
+    height: 100%;
+    align-items: center;
+    justify-content: center;
+    background: red;
+    color: white;
+}
+</pre>
+<p>et ensuite nous pouvons vérifier l'existence de <code class="notranslate" translate="no">transferControlToOffscreen</code> pour voir
+si le navigateur prend en charge <code class="notranslate" translate="no">OffscreenCanvas</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
++  if (!canvas.transferControlToOffscreen) {
++    canvas.style.display = 'none';
++    document.querySelector('#noOffscreenCanvas').style.display = '';
++    return;
++  }
+  const offscreen = canvas.transferControlToOffscreen();
+  const worker = new Worker('offscreencanvas-picking.js', {type: 'module});
+  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
+
+  ...
+</pre>
+<p>et avec cela, si votre navigateur prend en charge <code class="notranslate" translate="no">OffscreenCanvas</code>, cet exemple devrait fonctionner</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/offscreencanvas.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/offscreencanvas.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>C'est formidable, mais comme tous les navigateurs ne prennent pas en charge <code class="notranslate" translate="no">OffscreenCanvas</code> pour le moment,
+modifions le code pour qu'il fonctionne à la fois avec <code class="notranslate" translate="no">OffscreenCanvas</code> et, si ce n'est pas le cas, pour qu'il revienne à l'utilisation
+du canevas dans la page principale comme d'habitude.</p>
+<blockquote>
+<p>En aparté, si vous avez besoin de OffscreenCanvas pour rendre votre page réactive, alors
+l'intérêt d'avoir un fallback n'est pas évident. Peut-être que selon si
+vous exécutez sur la page principale ou dans un worker, vous pourriez ajuster la quantité
+de travail effectué afin que lorsque vous exécutez dans un worker, vous puissiez faire plus que lorsque
+vous exécutez dans la page principale. Ce que vous faites dépend entièrement de vous.</p>
+</blockquote>
+<p>La première chose que nous devrions probablement faire est de séparer le code three.js
+du code spécifique au worker. De cette façon, nous pouvons
+utiliser le même code sur la page principale et sur le worker. En d'autres termes,
+nous aurons maintenant 3 fichiers</p>
+<ol>
+<li><p>notre fichier html.</p>
+<p><code class="notranslate" translate="no">threejs-offscreencanvas-w-fallback.html</code></p>
+</li>
+<li><p>un fichier JavaScript qui contient notre code three.js.</p>
+<p><code class="notranslate" translate="no">shared-cubes.js</code></p>
+</li>
+<li><p>notre code de support pour le worker</p>
+<p><code class="notranslate" translate="no">offscreencanvas-worker-cubes.js</code></p>
+</li>
+</ol>
+<p><code class="notranslate" translate="no">shared-cubes.js</code> et <code class="notranslate" translate="no">offscreencanvas-worker-cubes.js</code> sont essentiellement
+la séparation de notre fichier <code class="notranslate" translate="no">offscreencanvas-cubes.js</code> précédent. Nous
+copions d'abord tout le contenu de <code class="notranslate" translate="no">offscreencanvas-cubes.js</code> dans <code class="notranslate" translate="no">shared-cube.js</code>. Ensuite,
+nous renommons <code class="notranslate" translate="no">main</code> en <code class="notranslate" translate="no">init</code> car nous avons déjà une fonction <code class="notranslate" translate="no">main</code> dans notre
+fichier HTML, et nous devons exporter <code class="notranslate" translate="no">init</code> et <code class="notranslate" translate="no">state</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+
+-const state = {
++export const state = {
+  width: 300,   // par défaut du canevas
+  height: 150,  // par défaut du canevas
+};
+
+-function main(data) {
++export function init(data) {
+  const {canvas} = data;
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+</pre>
+<p>et découpons juste les parties non liées à three.js</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function size(data) {
+-  state.width = data.width;
+-  state.height = data.height;
+-}
+-
+-const handlers = {
+-  main,
+-  size,
+-};
+-
+-self.onmessage = function(e) {
+-  const fn = handlers[e.data.type];
+-  if (typeof fn !== 'function') {
+-    throw new Error('no handler for type: ' + e.data.type);
+-  }
+-  fn(e.data);
+-};
+</pre>
+<p>Ensuite, nous copions les parties que nous venons de supprimer dans <code class="notranslate" translate="no">offscreencanvas-worker-cubes.js</code>
+et importons <code class="notranslate" translate="no">shared-cubes.js</code> ainsi qu'appelons <code class="notranslate" translate="no">init</code> au lieu de <code class="notranslate" translate="no">main</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {init, state} from './shared-cubes.js';
+
+function size(data) {
+  state.width = data.width;
+  state.height = data.height;
+}
+
+const handlers = {
+-  main,
++  init,
+  size,
+};
+
+self.onmessage = function(e) {
+  const fn = handlers[e.data.type];
+  if (typeof fn !== 'function') {
+    throw new Error('no handler for type: ' + e.data.type);
+  }
+  fn(e.data);
+};
+</pre>
+<p>De même, nous devons inclure <code class="notranslate" translate="no">shared-cubes.js</code> dans la page principale</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="module"&gt;
++import {init, state} from './shared-cubes.js';
+</pre>
+<p>Nous pouvons supprimer le HTML et le CSS que nous avons ajoutés précédemment</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
+-  &lt;div id="noOffscreenCanvas" style="display:none;"&gt;
+-    &lt;div&gt;no OffscreenCanvas support&lt;/div&gt;
+-  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>et un peu de CSS pour cela</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">-#noOffscreenCanvas {
+-    display: flex;
+-    width: 100%;
+-    height: 100%;
+-    align-items: center;
+-    justify-content: center;
+-    background: red;
+-    color: white;
+-}
+</pre>
+<p>Ensuite, modifions le code dans la page principale pour appeler une fonction de démarrage ou une autre
+selon que le navigateur prend en charge <code class="notranslate" translate="no">OffscreenCanvas</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+-  if (!canvas.transferControlToOffscreen) {
+-    canvas.style.display = 'none';
+-    document.querySelector('#noOffscreenCanvas').style.display = '';
+-    return;
+-  }
+-  const offscreen = canvas.transferControlToOffscreen();
+-  const worker = new Worker('offscreencanvas-picking.js', {type: 'module'});
+-  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
++  if (canvas.transferControlToOffscreen) {
++    startWorker(canvas);
++  } else {
++    startMainPage(canvas);
++  }
+  ...
+</pre>
+<p>Nous allons déplacer tout le code que nous avions pour configurer le worker à l'intérieur de <code class="notranslate" translate="no">startWorker</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function startWorker(canvas) {
+  const offscreen = canvas.transferControlToOffscreen();
+  const worker = new Worker('offscreencanvas-worker-cubes.js', {type: 'module'});
+  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
+
+  function sendSize() {
+    worker.postMessage({
+      type: 'size',
+      width: canvas.clientWidth,
+      height: canvas.clientHeight,
+    });
+  }
+
+  window.addEventListener('resize', sendSize);
+  sendSize();
+
+  console.log('using OffscreenCanvas');
+}
+</pre>
+<p>et envoyer <code class="notranslate" translate="no">init</code> au lieu de <code class="notranslate" translate="no">main</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
++  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);
+</pre>
+<p>pour démarrer dans la page principale, nous pouvons faire ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function startMainPage(canvas) {
+  init({canvas});
+
+  function sendSize() {
+    state.width = canvas.clientWidth;
+    state.height = canvas.clientHeight;
+  }
+  window.addEventListener('resize', sendSize);
+  sendSize();
+
+  console.log('using regular canvas');
+}
+</pre>
+<p>et avec cela, notre exemple s'exécutera soit dans un OffscreenCanvas, soit il
+reviendra à s'exécuter dans la page principale.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/offscreencanvas-w-fallback.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/offscreencanvas-w-fallback.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>C'était donc relativement facile. Essayons le picking. Nous allons prendre du code de
+l'exemple <code class="notranslate" translate="no">RayCaster</code> depuis <a href="picking.html">l'article sur le picking</a>
+et le faire fonctionner offscreen.</p>
+<p>Copions le fichier <code class="notranslate" translate="no">shared-cube.js</code> vers <code class="notranslate" translate="no">shared-picking.js</code> et ajoutons les parties de picking. Nous copions le <code class="notranslate" translate="no">PickHelper</code> </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class PickHelper {
+  constructor() {
+    this.raycaster = new THREE.Raycaster();
+    this.pickedObject = null;
+    this.pickedObjectSavedColor = 0;
+  }
+  pick(normalizedPosition, scene, camera, time) {
+    // restore the color if there is a picked object
+    if (this.pickedObject) {
+      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
+      this.pickedObject = undefined;
+    }
+
+    // cast a ray through the frustum
+    this.raycaster.setFromCamera(normalizedPosition, camera);
+    // get the list of objects the ray intersected
+    const intersectedObjects = this.raycaster.intersectObjects(scene.children);
+    if (intersectedObjects.length) {
+      // pick the first object. It's the closest one
+      this.pickedObject = intersectedObjects[0].object;
+      // save its color
+      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+      // set its emissive color to flashing red/yellow
+      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+    }
+  }
+}
+
+const pickPosition = {x: 0, y: 0};
+const pickHelper = new PickHelper();
+</pre>
+<p>Nous avons mis à jour <code class="notranslate" translate="no">pickPosition</code> à partir de la souris comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
+  pickPosition.x = (pos.x / canvas.width ) *  2 - 1;
+  pickPosition.y = (pos.y / canvas.height) * -2 + 1;  // notez que nous inversons Y
+}
+window.addEventListener('mousemove', setPickPosition);
+</pre>
+<p>Un worker ne peut pas lire la position de la souris directement, donc tout comme le code de taille,
+envoyons un message avec la position de la souris. Comme pour le code de taille, nous enverrons
+la position de la souris et mettrons à jour <code class="notranslate" translate="no">pickPosition</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function size(data) {
+  state.width = data.width;
+  state.height = data.height;
+}
+
++function mouse(data) {
++  pickPosition.x = data.x;
++  pickPosition.y = data.y;
++}
+
+const handlers = {
+  init,
++  mouse,
+  size,
+};
+
+self.onmessage = function(e) {
+  const fn = handlers[e.data.type];
+  if (typeof fn !== 'function') {
+    throw new Error('no handler for type: ' + e.data.type);
+  }
+  fn(e.data);
+};
+</pre>
+<p>De retour dans notre page principale, nous devons ajouter du code pour passer la souris
+au worker ou à la page principale.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+let sendMouse;
+
+function startWorker(canvas) {
+  const offscreen = canvas.transferControlToOffscreen();
+  const worker = new Worker('offscreencanvas-worker-picking.js', {type: 'module'});
+  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);
+
++  sendMouse = (x, y) =&gt; {
++    worker.postMessage({
++      type: 'mouse',
++      x,
++      y,
++    });
++  };
+
+  function sendSize() {
+    worker.postMessage({
+      type: 'size',
+      width: canvas.clientWidth,
+      height: canvas.clientHeight,
+    });
+  }
+
+  window.addEventListener('resize', sendSize);
+  sendSize();
+
+  console.log('using OffscreenCanvas');  /* eslint-disable-line no-console */
+}
+
+function startMainPage(canvas) {
+  init({canvas});
+
++  sendMouse = (x, y) =&gt; {
++    pickPosition.x = x;
++    pickPosition.y = y;
++  };
+
+  function sendSize() {
+    state.width = canvas.clientWidth;
+    state.height = canvas.clientHeight;
+  }
+  window.addEventListener('resize', sendSize);
+  sendSize();
+
+  console.log('using regular canvas');  /* eslint-disable-line no-console */
+}
+</pre>
+<p>Ensuite, nous pouvons copier tout le code de gestion de la souris dans la page principale et
+apporter juste des modifications mineures pour utiliser <code class="notranslate" translate="no">sendMouse</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
+-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
++  sendMouse(
++      (pos.x / canvas.clientWidth ) *  2 - 1,
++      (pos.y / canvas.clientHeight) * -2 + 1);  // notez que nous inversons Y
+}
+
+function clearPickPosition() {
+  // Contrairement à la souris qui a toujours une position
+  // si l'utilisateur arrête de toucher l'écran, nous voulons
+  // arrêter le picking. Pour l'instant, nous choisissons juste une valeur
+  // peu susceptible de sélectionner quelque chose
+-  pickPosition.x = -100000;
+-  pickPosition.y = -100000;
++  sendMouse(-100000, -100000);
+}
+window.addEventListener('mousemove', setPickPosition);
+window.addEventListener('mouseout', clearPickPosition);
+window.addEventListener('mouseleave', clearPickPosition);
+
+window.addEventListener('touchstart', (event) =&gt; {
+  // prevent the window from scrolling
+  event.preventDefault();
+  setPickPosition(event.touches[0]);
+}, {passive: false});
+
+window.addEventListener('touchmove', (event) =&gt; {
+  setPickPosition(event.touches[0]);
+});
+
+window.addEventListener('touchend', clearPickPosition);
+</pre>
+<p>et avec cela, le picking devrait fonctionner avec <code class="notranslate" translate="no">OffscreenCanvas</code>.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/offscreencanvas-w-picking.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/offscreencanvas-w-picking.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Allons un peu plus loin et ajoutons les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.
+Cela sera un peu plus complexe. Les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> utilisent
+le DOM de manière assez extensive pour vérifier la souris, les événements tactiles,
+et le clavier.</p>
+<p>Contrairement à notre code jusqu'à présent, nous ne pouvons pas vraiment utiliser un objet <code class="notranslate" translate="no">state</code> global
+sans réécrire tout le code des OrbitControls pour qu'il fonctionne avec.
+Les OrbitControls prennent un <code class="notranslate" translate="no">HTMLElement</code> auquel ils attachent la plupart
+des événements DOM qu'ils utilisent. Peut-être pourrions-nous passer notre propre
+objet qui a la même surface d'API qu'un élément DOM.
+Nous n'avons besoin de prendre en charge que les fonctionnalités dont les OrbitControls ont besoin.</p>
+<p>En fouillant dans le <a href="https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js">code source des OrbitControls</a>,
+il semble que nous devions gérer les événements suivants.</p>
+<ul>
+<li>contextmenu</li>
+<li>pointerdown</li>
+<li>pointermove</li>
+<li>pointerup</li>
+<li>touchstart</li>
+<li>touchmove</li>
+<li>touchend</li>
+<li>wheel</li>
+<li>keydown</li>
+</ul>
+<p>Pour les événements de pointeur, nous avons besoin des propriétés <code class="notranslate" translate="no">ctrlKey</code>, <code class="notranslate" translate="no">metaKey</code>, <code class="notranslate" translate="no">shiftKey</code>,
+<code class="notranslate" translate="no">button</code>, <code class="notranslate" translate="no">pointerType</code>, <code class="notranslate" translate="no">clientX</code>, <code class="notranslate" translate="no">clientY</code>, <code class="notranslate" translate="no">pageX</code> et <code class="notranslate" translate="no">pageY</code>.</p>
+<p>Pour les événements keydown, nous avons besoin des propriétés <code class="notranslate" translate="no">ctrlKey</code>, <code class="notranslate" translate="no">metaKey</code>, <code class="notranslate" translate="no">shiftKey</code>
+et <code class="notranslate" translate="no">keyCode</code>.</p>
+<p>Pour l'événement wheel, nous n'avons besoin que de la propriété <code class="notranslate" translate="no">deltaY</code>.</p>
+<p>Et pour les événements tactiles, nous n'avons besoin que de <code class="notranslate" translate="no">pageX</code> et <code class="notranslate" translate="no">pageY</code> de
+la propriété <code class="notranslate" translate="no">touches</code>.</p>
+<p>Alors, créons une paire d'objets proxy. Une partie s'exécutera dans la page principale,
+capturera tous ces événements et transmettra les valeurs de propriété pertinentes
+au worker. L'autre partie s'exécutera dans le worker, recevra ces
+événements et les transmettra en utilisant des événements qui ont la même structure
+que les événements DOM originaux, de sorte que les OrbitControls ne pourront pas
+faire la différence.</p>
+<p>Voici le code pour la partie worker.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {EventDispatcher} from 'three';
+
+class ElementProxyReceiver extends EventDispatcher {
+  constructor() {
+    super();
+  }
+  handleEvent(data) {
+    this.dispatchEvent(data);
+  }
+}
+</pre>
+<p>Tout ce qu'il fait est, s'il reçoit un message, de le dispatcher.
+Il hérite de <a href="/docs/#api/en/core/EventDispatcher"><code class="notranslate" translate="no">EventDispatcher</code></a> qui fournit des méthodes comme
+<code class="notranslate" translate="no">addEventListener</code> et <code class="notranslate" translate="no">removeEventListener</code>, tout comme un élément DOM,
+donc si nous le passons aux OrbitControls, cela devrait fonctionner.</p>
+<p><code class="notranslate" translate="no">ElementProxyReceiver</code> gère 1 élément. Dans notre cas, nous n'en avons besoin que d'un,
+mais il est préférable d'anticiper, alors créons un gestionnaire pour gérer
+plus d'un.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ProxyManager {
+  constructor() {
+    this.targets = {};
+    this.handleEvent = this.handleEvent.bind(this);
+  }
+  makeProxy(data) {
+    const {id} = data;
+    const proxy = new ElementProxyReceiver();
+    this.targets[id] = proxy;
+  }
+  getProxy(id) {
+    return this.targets[id];
+  }
+  handleEvent(data) {
+    this.targets[data.id].handleEvent(data.data);
+  }
+}
+</pre>
+<p>Nous pouvons créer une instance de <code class="notranslate" translate="no">ProxyManager</code> et appeler sa méthode <code class="notranslate" translate="no">makeProxy</code>
+avec un identifiant, ce qui créera un <code class="notranslate" translate="no">ElementProxyReceiver</code> qui
+répondra aux messages avec cet identifiant.</p>
+<p>Connectons-le au gestionnaire de messages de notre worker.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const proxyManager = new ProxyManager();
+
+function start(data) {
+  const proxy = proxyManager.getProxy(data.canvasId);
+  init({
+    canvas: data.canvas,
+    inputElement: proxy,
+  });
+}
+
+function makeProxy(data) {
+  proxyManager.makeProxy(data);
+}
+
+...
+
+const handlers = {
+-  init,
+-  mouse,
++  start,
++  makeProxy,
++  event: proxyManager.handleEvent,
+   size,
+};
+
+self.onmessage = function(e) {
+  const fn = handlers[e.data.type];
+  if (typeof fn !== 'function') {
+    throw new Error('no handler for type: ' + e.data.type);
+  }
+  fn(e.data);
+};
+</pre>
+<p>Dans notre code three.js partagé, nous devons importer les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> et les configurer.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
++import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+
+export function init(data) {
+-  const {canvas} = data;
++  const {canvas, inputElement} = data;
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+
++  const controls = new OrbitControls(camera, inputElement);
++  controls.target.set(0, 0, 0);
++  controls.update();
+</pre>
+<p>Notez que nous passons notre proxy aux OrbitControls via <code class="notranslate" translate="no">inputElement</code>
+au lieu de passer le canevas comme nous le faisons dans d'autres exemples sans OffscreenCanvas.</p>
+<p>Ensuite, nous pouvons déplacer tout le code des événements de picking du fichier HTML
+vers le code three.js partagé également, tout en changeant
+<code class="notranslate" translate="no">canvas</code> en <code class="notranslate" translate="no">inputElement</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function getCanvasRelativePosition(event) {
+-  const rect = canvas.getBoundingClientRect();
++  const rect = inputElement.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
+function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
+-  sendMouse(
+-      (pos.x / canvas.clientWidth ) *  2 - 1,
+-      (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y
++  pickPosition.x = (pos.x / inputElement.clientWidth ) *  2 - 1;
++  pickPosition.y = (pos.y / inputElement.clientHeight) * -2 + 1;  // notez que nous inversons Y
+}
+
+function clearPickPosition() {
+  // Contrairement à la souris qui a toujours une position
+  // si l'utilisateur arrête de toucher l'écran, nous voulons
+  // arrêter le picking. Pour l'instant, nous choisissons juste une valeur
+  // peu susceptible de sélectionner quelque chose
+-  sendMouse(-100000, -100000);
++  pickPosition.x = -100000;
++  pickPosition.y = -100000;
+}
+
+*inputElement.addEventListener('mousemove', setPickPosition);
+*inputElement.addEventListener('mouseout', clearPickPosition);
+*inputElement.addEventListener('mouseleave', clearPickPosition);
+
+*inputElement.addEventListener('touchstart', (event) =&gt; {
+  // prevent the window from scrolling
+  event.preventDefault();
+  setPickPosition(event.touches[0]);
+}, {passive: false});
+
+*inputElement.addEventListener('touchmove', (event) =&gt; {
+  setPickPosition(event.touches[0]);
+});
+
+*inputElement.addEventListener('touchend', clearPickPosition);
+</pre>
+<p>De retour dans la page principale, nous avons besoin de code pour envoyer des messages pour
+tous les événements que nous avons énumérés ci-dessus.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">let nextProxyId = 0;
+class ElementProxy {
+  constructor(element, worker, eventHandlers) {
+    this.id = nextProxyId++;
+    this.worker = worker;
+    const sendEvent = (data) =&gt; {
+      this.worker.postMessage({
+        type: 'event',
+        id: this.id,
+        data,
+      });
+    };
+
+    // register an id
+    worker.postMessage({
+      type: 'makeProxy',
+      id: this.id,
+    });
+    for (const [eventName, handler] of Object.entries(eventHandlers)) {
+      element.addEventListener(eventName, function(event) {
+        handler(event, sendEvent);
+      });
+    }
+  }
+}
+</pre>
+<p><code class="notranslate" translate="no">ElementProxy</code> prend l'élément dont nous voulons proxifier les événements. Il
+enregistre ensuite un identifiant auprès du worker en en choisissant un et en l'envoyant
+via le message <code class="notranslate" translate="no">makeProxy</code> que nous avons configuré précédemment. Le worker créera
+un <code class="notranslate" translate="no">ElementProxyReceiver</code> et l'enregistrera avec cet identifiant.</p>
+<p>Nous avons ensuite un objet de gestionnaires d'événements à enregistrer. De cette façon,
+nous pouvons passer des gestionnaires uniquement pour les événements que nous voulons transmettre au
+worker.</p>
+<p>Lorsque nous démarrons le worker, nous créons d'abord un proxy et passons nos gestionnaires d'événements.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function startWorker(canvas) {
+  const offscreen = canvas.transferControlToOffscreen();
+  const worker = new Worker('offscreencanvas-worker-orbitcontrols.js', {type: 'module'});
+
++  const eventHandlers = {
++    contextmenu: preventDefaultHandler,
++    mousedown: mouseEventHandler,
++    mousemove: mouseEventHandler,
++    mouseup: mouseEventHandler,
++    pointerdown: mouseEventHandler,
++    pointermove: mouseEventHandler,
++    pointerup: mouseEventHandler,
++    touchstart: touchEventHandler,
++    touchmove: touchEventHandler,
++    touchend: touchEventHandler,
++    wheel: wheelEventHandler,
++    keydown: filteredKeydownEventHandler,
++  };
++  const proxy = new ElementProxy(canvas, worker, eventHandlers);
+  worker.postMessage({
+    type: 'start',
+    canvas: offscreen,
++    canvasId: proxy.id,
+  }, [offscreen]);
+  console.log('using OffscreenCanvas');  /* eslint-disable-line no-console */
+}
+</pre>
+<p>Et voici les gestionnaires d'événements. Tout ce qu'ils font est de copier une liste de propriétés
+à partir de l'événement qu'ils reçoivent. On leur passe une fonction <code class="notranslate" translate="no">sendEvent</code> à laquelle ils passent les données
+qu'ils créent. Cette fonction ajoutera l'identifiant correct et l'enverra au worker.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mouseEventHandler = makeSendPropertiesHandler([
+  'ctrlKey',
+  'metaKey',
+  'shiftKey',
+  'button',
+  'pointerType',
+  'clientX',
+  'clientY',
+  'pointerId',
+  'pageX',
+  'pageY',
+]);
+const wheelEventHandlerImpl = makeSendPropertiesHandler([
+  'deltaX',
+  'deltaY',
+]);
+const keydownEventHandler = makeSendPropertiesHandler([
+  'ctrlKey',
+  'metaKey',
+  'shiftKey',
+  'keyCode',
+]);
+
+function wheelEventHandler(event, sendFn) {
+  event.preventDefault();
+  wheelEventHandlerImpl(event, sendFn);
+}
+
+function preventDefaultHandler(event) {
+  event.preventDefault();
+}
+
+function copyProperties(src, properties, dst) {
+  for (const name of properties) {
+      dst[name] = src[name];
+  }
+}
+
+function makeSendPropertiesHandler(properties) {
+  return function sendProperties(event, sendFn) {
+    const data = {type: event.type};
+    copyProperties(event, properties, data);
+    sendFn(data);
+  };
+}
+
+function touchEventHandler(event, sendFn) {
+  // preventDefault() corrige les événements mousemove, mouseup et mousedown
+  // qui se déclenchent lors d'un simple toucher/relâcher
+  // Cela n'arrive qu'avec OffscreenCanvas
+  event.preventDefault();
+  const touches = [];
+  const data = {type: event.type, touches};
+  for (let i = 0; i &lt; event.touches.length; ++i) {
+    const touch = event.touches[i];
+    touches.push({
+      pageX: touch.pageX,
+      pageY: touch.pageY,
+      clientX: touch.clientX,
+      clientY: touch.clientY,
+    });
+  }
+  sendFn(data);
+}
+
+// Les quatre touches fléchées
+const orbitKeys = {
+  '37': true,  // left
+  '38': true,  // up
+  '39': true,  // right
+  '40': true,  // down
+};
+function filteredKeydownEventHandler(event, sendFn) {
+  const {keyCode} = event;
+  if (orbitKeys[keyCode]) {
+    event.preventDefault();
+    keydownEventHandler(event, sendFn);
+  }
+}
+</pre>
+<p>Cela semble proche de fonctionner, mais si nous l'essayons réellement, nous verrons
+que les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> ont besoin de quelques éléments supplémentaires.</p>
+<p>L'une d'elles est qu'ils appellent <code class="notranslate" translate="no">element.focus</code>. Nous n'avons pas besoin que cela se produise
+dans le worker, alors ajoutons simplement un stub.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ElementProxyReceiver extends THREE.EventDispatcher {
+  constructor() {
+    super();
+  }
+  handleEvent(data) {
+    this.dispatchEvent(data);
+  }
++  focus() {
++    // sans opération
++  }
+}
+</pre>
+<p>Une autre chose est qu'ils appellent <code class="notranslate" translate="no">event.preventDefault</code> et <code class="notranslate" translate="no">event.stopPropagation</code>.
+Nous gérons déjà cela dans la page principale, donc ceux-ci peuvent également être un noop (sans opération).</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function noop() {
++}
+
+class ElementProxyReceiver extends THREE.EventDispatcher {
+  constructor() {
+    super();
+  }
+  handleEvent(data) {
++    data.preventDefault = noop;
++    data.stopPropagation = noop;
+    this.dispatchEvent(data);
+  }
+  focus() {
+    // sans opération
+  }
+}
+</pre>
+<p>Une autre chose est qu'ils regardent <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code>. Nous
+passions la taille auparavant, mais nous pouvons mettre à jour la paire de proxies
+pour passer cela également.</p>
+<p>Dans le worker...</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ElementProxyReceiver extends THREE.EventDispatcher {
+  constructor() {
+    super();
+  }
++  get clientWidth() {
++    return this.width;
++  }
++  get clientHeight() {
++    return this.height;
++  }
++  getBoundingClientRect() {
++    return {
++      left: this.left,
++      top: this.top,
++      width: this.width,
++      height: this.height,
++      right: this.left + this.width,
++      bottom: this.top + this.height,
++    };
++  }
+  handleEvent(data) {
++    if (data.type === 'size') {
++      this.left = data.left;
++      this.top = data.top;
++      this.width = data.width;
++      this.height = data.height;
++      return;
++    }
+    data.preventDefault = noop;
+    data.stopPropagation = noop;
+    this.dispatchEvent(data);
+  }
+  focus() {
+    // sans opération
+  }
+}
+</pre>
+<p>de retour dans la page principale, nous devons envoyer la taille ainsi que les positions gauche et haut également.
+Notez qu'en l'état, nous ne gérons pas si le canevas se déplace, seulement s'il redimensionne. Si vous vouliez
+gérer le déplacement, vous devriez appeler <code class="notranslate" translate="no">sendSize</code> chaque fois que quelque chose déplace le canevas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ElementProxy {
+  constructor(element, worker, eventHandlers) {
+    this.id = nextProxyId++;
+    this.worker = worker;
+    const sendEvent = (data) =&gt; {
+      this.worker.postMessage({
+        type: 'event',
+        id: this.id,
+        data,
+      });
+    };
+
+    // register an id
+    worker.postMessage({
+      type: 'makeProxy',
+      id: this.id,
+    });
++    sendSize();
+    for (const [eventName, handler] of Object.entries(eventHandlers)) {
+      element.addEventListener(eventName, function(event) {
+        handler(event, sendEvent);
+      });
+    }
+
++    function sendSize() {
++      const rect = element.getBoundingClientRect();
++      sendEvent({
++        type: 'size',
++        left: rect.left,
++        top: rect.top,
++        width: element.clientWidth,
++        height: element.clientHeight,
++      });
++    }
++
++    window.addEventListener('resize', sendSize);
+  }
+}
+</pre>
+<p>et dans notre code three.js partagé, nous n'avons plus besoin de <code class="notranslate" translate="no">state</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-export const state = {
+-  width: 300,   // par défaut du canevas
+-  height: 150,  // par défaut du canevas
+-};
+
+...
+
+function resizeRendererToDisplaySize(renderer) {
+  const canvas = renderer.domElement;
+-  const width = state.width;
+-  const height = state.height;
++  const width = inputElement.clientWidth;
++  const height = inputElement.clientHeight;
+  const needResize = canvas.width !== width || canvas.height !== height;
+  if (needResize) {
+    renderer.setSize(width, height, false);
+  }
+  return needResize;
+}
+
+function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+-    camera.aspect = state.width / state.height;
++    camera.aspect = inputElement.clientWidth / inputElement.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+  ...
+</pre>
+<p>Quelques hacks supplémentaires. Les OrbitControls ajoutent des événements <code class="notranslate" translate="no">pointermove</code> et <code class="notranslate" translate="no">pointerup</code> à l'<code class="notranslate" translate="no">ownerDocument</code>
+de l'élément pour gérer la capture de la souris (lorsque la souris sort de la fenêtre).</p>
+<p>De plus, le code référence le <code class="notranslate" translate="no">document</code> global, mais il n'y a pas de document global
+dans un worker. </p>
+<p>Nous pouvons résoudre tout cela avec 2 hacks rapides. Dans notre code worker,
+nous allons réutiliser notre proxy pour les deux problèmes.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function start(data) {
+  const proxy = proxyManager.getProxy(data.canvasId);
++  proxy.ownerDocument = proxy; // HACK!
++  self.document = {} // HACK!
+  init({
+    canvas: data.canvas,
+    inputElement: proxy,
+  });
+}
+</pre>
+<p>Cela donnera aux <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> quelque chose à inspecter qui
+correspond à leurs attentes.</p>
+<p>Je sais que c'était un peu difficile à suivre. La version courte est la suivante :
+<code class="notranslate" translate="no">ElementProxy</code> s'exécute sur la page principale et transmet les événements DOM
+à <code class="notranslate" translate="no">ElementProxyReceiver</code> dans le worker, qui se fait passer pour un <code class="notranslate" translate="no">HTMLElement</code>
+que nous pouvons utiliser à la fois avec les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> et avec notre propre code.</p>
+<p>La dernière chose est notre fallback lorsque nous n'utilisons pas OffscreenCanvas.
+Tout ce que nous avons à faire est de passer le canevas lui-même comme notre <code class="notranslate" translate="no">inputElement</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function startMainPage(canvas) {
+-  init({canvas});
++  init({canvas, inputElement: canvas});
+  console.log('using regular canvas');
+}
+</pre>
+<p>et maintenant nous devrions avoir les OrbitControls fonctionnant avec OffscreenCanvas</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/offscreencanvas-w-orbitcontrols.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/offscreencanvas-w-orbitcontrols.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>C'est probablement l'exemple le plus compliqué sur ce site. Il est un
+peu difficile à suivre car il y a 3 fichiers impliqués pour chaque
+exemple. Le fichier HTML, le fichier worker, le code three.js partagé.</p>
+<p>J'espère que ce n'était pas trop difficile à comprendre et que cela a fourni
+des exemples utiles pour travailler avec three.js, OffscreenCanvas et les web workers.</p>
 
         </div>
       </div>

+ 455 - 5
manual/fr/optimize-lots-of-objects-animated.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Optimize Lots of Objects Animated</title>
+    <title>Optimiser de nombreux objets animés</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Optimize Lots of Objects Animated">
+    <meta name="twitter:title" content="Three.js – Optimiser de nombreux objets animés">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,462 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Optimize Lots of Objects Animated</h1>
+        <h1>Optimiser de nombreux objets animés</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/optimize-lots-of-objects-animated.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article est une continuation de <a href="optimize-lots-of-objects.html">un article sur l'optimisation de nombreux objets
+</a>. Si vous ne l'avez pas encore lu,
+veuillez le lire avant de poursuivre. </p>
+<p>Dans l'article précédent, nous avons fusionné environ 19000 cubes en une
+seule géométrie. Cela a eu l'avantage d'optimiser notre dessin
+de 19000 cubes, mais cela a eu l'inconvénient de rendre plus difficile
+le déplacement d'un cube individuel.</p>
+<p>Selon ce que nous essayons d'accomplir, il existe différentes solutions.
+Dans ce cas, affichons plusieurs ensembles de données et animons la transition entre les ensembles.</p>
+<p>La première chose à faire est d'obtenir plusieurs ensembles de données. Idéalement, nous
+pré-traiterions probablement les données hors ligne, mais dans ce cas, chargeons 2 ensembles de
+données et générons-en 2 autres.</p>
+<p>Voici notre ancien code de chargement</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
+  .then(parseData)
+  .then(addBoxes)
+  .then(render);
+</pre>
+<p>Changeons-le pour quelque chose comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">async function loadData(info) {
+  const text = await loadFile(info.url);
+  info.file = parseData(text);
+}
+
+async function loadAll() {
+  const fileInfos = [
+    {name: 'men',   hueRange: [0.7, 0.3], url: 'resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc' },
+    {name: 'women', hueRange: [0.9, 1.1], url: 'resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014ft_2010_cntm_1_deg.asc' },
+  ];
+
+  await Promise.all(fileInfos.map(loadData));
+
+  ...
+}
+loadAll();
+</pre>
+<p>Le code ci-dessus chargera tous les fichiers dans <code class="notranslate" translate="no">fileInfos</code> et une fois terminé, chaque objet
+dans <code class="notranslate" translate="no">fileInfos</code> aura une propriété <code class="notranslate" translate="no">file</code> contenant le fichier chargé. <code class="notranslate" translate="no">name</code> et <code class="notranslate" translate="no">hueRange</code>
+seront utilisés plus tard. <code class="notranslate" translate="no">name</code> sera pour un champ d'interface utilisateur. <code class="notranslate" translate="no">hueRange</code> sera utilisé pour
+choisir une plage de teintes à appliquer.</p>
+<p>Les deux fichiers ci-dessus sont apparemment le nombre d'hommes par zone et le nombre de
+femmes par zone en 2010. Notez que je n'ai aucune idée si ces données sont correctes, mais
+ce n'est pas vraiment important. L'important est de montrer différents ensembles
+de données.</p>
+<p>Générons 2 ensembles de données supplémentaires. L'un représentant les lieux où le nombre
+d'hommes est supérieur au nombre de femmes, et inversement, les lieux où
+le nombre de femmes est supérieur au nombre d'hommes. </p>
+<p>La première chose, écrivons une fonction qui, étant donné un tableau bidimensionnel
+de tableaux comme nous avions précédemment, va l'appliquer pour générer un nouveau tableau bidimensionnel
+de tableaux.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function mapValues(data, fn) {
+  return data.map((row, rowNdx) =&gt; {
+    return row.map((value, colNdx) =&gt; {
+      return fn(value, rowNdx, colNdx);
+    });
+  });
+}
+</pre>
+<p>Comme la fonction normale <code class="notranslate" translate="no">Array.map</code>, la fonction <code class="notranslate" translate="no">mapValues</code> appelle une fonction
+<code class="notranslate" translate="no">fn</code> pour chaque valeur dans le tableau de tableaux. Elle lui passe la valeur ainsi que les
+indices de ligne et de colonne.</p>
+<p>Maintenant, écrivons du code pour générer un nouveau fichier qui est une comparaison entre 2
+fichiers.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeDiffFile(baseFile, otherFile, compareFn) {
+  let min;
+  let max;
+  const baseData = baseFile.data;
+  const otherData = otherFile.data;
+  const data = mapValues(baseData, (base, rowNdx, colNdx) =&gt; {
+    const other = otherData[rowNdx][colNdx];
+      if (base === undefined || other === undefined) {
+        return undefined;
+      }
+      const value = compareFn(base, other);
+      min = Math.min(min === undefined ? value : min, value);
+      max = Math.max(max === undefined ? value : max, value);
+      return value;
+  });
+  // make a copy of baseFile and replace min, max, and data
+  // with the new data
+  return {...baseFile, min, max, data};
+}
+</pre>
+<p>Le code ci-dessus utilise <code class="notranslate" translate="no">mapValues</code> pour générer un nouvel ensemble de données qui est
+une comparaison basée sur la fonction <code class="notranslate" translate="no">compareFn</code> passée en paramètre. Il suit également
+les résultats <code class="notranslate" translate="no">min</code> et <code class="notranslate" translate="no">max</code> de la comparaison. Enfin, il crée un nouveau fichier avec
+toutes les mêmes propriétés que <code class="notranslate" translate="no">baseFile</code>, sauf avec de nouvelles valeurs pour <code class="notranslate" translate="no">min</code>, <code class="notranslate" translate="no">max</code> et <code class="notranslate" translate="no">data</code>.</p>
+<p>Ensuite, utilisons cela pour créer 2 nouveaux ensembles de données.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const menInfo = fileInfos[0];
+  const womenInfo = fileInfos[1];
+  const menFile = menInfo.file;
+  const womenFile = womenInfo.file;
+
+  function amountGreaterThan(a, b) {
+    return Math.max(a - b, 0);
+  }
+  fileInfos.push({
+    name: '&gt;50%men',
+    hueRange: [0.6, 1.1],
+    file: makeDiffFile(menFile, womenFile, (men, women) =&gt; {
+      return amountGreaterThan(men, women);
+    }),
+  });
+  fileInfos.push({
+    name: '&gt;50% women',
+    hueRange: [0.0, 0.4],
+    file: makeDiffFile(womenFile, menFile, (women, men) =&gt; {
+      return amountGreaterThan(women, men);
+    }),
+  });
+}
+</pre>
+<p>Maintenant, générons une interface utilisateur pour sélectionner parmi ces ensembles de données. Nous avons d'abord besoin
+d'un peu de HTML pour l'interface utilisateur.</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="ui"&gt;&lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>et du CSS pour le faire apparaître en haut à gauche</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#ui {
+  position: absolute;
+  left: 1em;
+  top: 1em;
+}
+#ui&gt;div {
+  font-size: 20pt;
+  padding: 1em;
+  display: inline-block;
+}
+#ui&gt;div.selected {
+  color: red;
+}
+</pre>
+<p>Ensuite, nous pouvons parcourir chaque fichier et générer un ensemble de boîtes fusionnées par
+ensemble de données, ainsi qu'un élément qui, au survol, affichera cet ensemble
+et masquera tous les autres.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// afficher les données sélectionnées, masquer les autres
+function showFileInfo(fileInfos, fileInfo) {
+  fileInfos.forEach((info) =&gt; {
+    const visible = fileInfo === info;
+    info.root.visible = visible;
+    info.elem.className = visible ? 'selected' : '';
+  });
+  requestRenderIfNotRequested();
+}
+
+const uiElem = document.querySelector('#ui');
+fileInfos.forEach((info) =&gt; {
+  const boxes = addBoxes(info.file, info.hueRange);
+  info.root = boxes;
+  const div = document.createElement('div');
+  info.elem = div;
+  div.textContent = info.name;
+  uiElem.appendChild(div);
+  div.addEventListener('mouseover', () =&gt; {
+    showFileInfo(fileInfos, info);
+  });
+});
+// afficher le premier ensemble de données
+showFileInfo(fileInfos, fileInfos[0]);
+</pre>
+<p>Une autre modification dont nous avons besoin par rapport à l'exemple précédent est de faire en sorte
+que <code class="notranslate" translate="no">addBoxes</code> accepte un <code class="notranslate" translate="no">hueRange</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function addBoxes(file) {
++function addBoxes(file, hueRange) {
+
+  ...
+
+    // calculer une couleur
+-    const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
++    const hue = THREE.MathUtils.lerp(...hueRange, amount);
+
+  ...
+</pre>
+<p>et avec cela, nous devrions pouvoir afficher 4 ensembles de données. Survolez les étiquettes
+avec la souris ou touchez-les pour changer d'ensemble.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-multiple-data-sets.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/lots-of-objects-multiple-data-sets.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Notez qu'il y a quelques points de données étranges qui ressortent vraiment. Je me demande ce qui se passe
+avec ceux-là !??! Dans tous les cas, comment animer entre ces 4 ensembles de données.</p>
+<p>Beaucoup d'idées.</p>
+<ul>
+<li><p>Faites simplement un fondu entre eux en utilisant <a href="/docs/#api/en/materials/Material.opacity"><code class="notranslate" translate="no">Material.opacity</code></a></p>
+<p>Le problème avec cette solution est que les cubes se superposent parfaitement, ce qui
+entraînera des problèmes de z-fighting. Il est possible de résoudre
+cela en changeant la fonction de profondeur et en utilisant le blending. Nous devrions
+probablement examiner cette option.</p>
+</li>
+<li><p>Agrandissez l'ensemble que nous voulons voir et réduisez les autres ensembles</p>
+<p>Parce que toutes les boîtes ont leur origine au centre de la planète,
+si nous les réduisons en dessous de 1.0, elles s'enfonceront dans la planète. Au début, cela
+semble une bonne idée, mais le problème est que toutes les boîtes de faible hauteur
+disparaîtront presque immédiatement et ne seront pas remplacées tant que le nouvel
+ensemble de données n'aura pas atteint 1.0. Cela rend la transition peu agréable.
+On pourrait peut-être résoudre cela avec un shader personnalisé sophistiqué.</p>
+</li>
+<li><p>Utiliser les Morphtargets</p>
+<p>Les Morphtargets sont un moyen de fournir plusieurs valeurs pour chaque sommet
+de la géométrie et de les <em>morpher</em> ou de les interpoler linéairement (lerp).
+Les Morphtargets sont le plus souvent utilisés pour l'animation faciale de personnages
+3D, mais ce n'est pas leur seule utilisation.</p>
+</li>
+</ul>
+<p>Essayons les morphtargets.</p>
+<p>Nous créerons toujours une géométrie pour chaque ensemble de données, mais nous extrairons
+ensuite l'attribut <code class="notranslate" translate="no">position</code> de chacun et les utiliserons comme morphtargets.</p>
+<p>Changeons d'abord <code class="notranslate" translate="no">addBoxes</code> pour qu'elle crée et retourne simplement la géométrie fusionnée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function addBoxes(file) {
++function makeBoxes(file, hueRange) {
+  const {min, max, data} = file;
+  const range = max - min;
+
+  ...
+
+-  const mergedGeometry = BufferGeometryUtils.mergeGeometries(
+-      geometries, false);
+-  const material = new THREE.MeshBasicMaterial({
+-    vertexColors: true,
+-  });
+-  const mesh = new THREE.Mesh(mergedGeometry, material);
+-  scene.add(mesh);
+-  return mesh;
++  return BufferGeometryUtils.mergeGeometries(
++     geometries, false);
+}
+</pre>
+<p>Il y a cependant une autre chose que nous devons faire ici. Les morphtargets doivent
+tous avoir exactement le même nombre de sommets. Le sommet #123 dans une cible doit
+avoir un sommet correspondant #123 dans toutes les autres cibles. Mais, tel que c'est actuellement,
+différents ensembles de données pourraient avoir des points de données sans données, donc aucune boîte ne sera
+générée pour ce point, ce qui signifierait l'absence de sommets correspondants pour un autre
+ensemble. Nous devons donc vérifier tous les ensembles de données et soit toujours générer
+quelque chose s'il y a des données dans n'importe quel ensemble, soit ne rien générer s'il
+manque des données dans n'importe quel ensemble. Faisons le second cas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function dataMissingInAnySet(fileInfos, latNdx, lonNdx) {
++  for (const fileInfo of fileInfos) {
++    if (fileInfo.file.data[latNdx][lonNdx] === undefined) {
++      return true;
++    }
++  }
++  return false;
++}
+
+-function makeBoxes(file, hueRange) {
++function makeBoxes(file, hueRange, fileInfos) {
+  const {min, max, data} = file;
+  const range = max - min;
+
+  ...
+
+  const geometries = [];
+  data.forEach((row, latNdx) =&gt; {
+    row.forEach((value, lonNdx) =&gt; {
++      if (dataMissingInAnySet(fileInfos, latNdx, lonNdx)) {
++        return;
++      }
+      const amount = (value - min) / range;
+
+  ...
+</pre>
+<p>Maintenant, nous allons changer le code qui appelait <code class="notranslate" translate="no">addBoxes</code> pour qu'il utilise <code class="notranslate" translate="no">makeBoxes</code>
+et configure les morphtargets.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+// créer la géométrie pour chaque ensemble de données
++const geometries = fileInfos.map((info) =&gt; {
++  return makeBoxes(info.file, info.hueRange, fileInfos);
++});
++
++// utiliser la première géométrie comme base
++// et ajouter toutes les géométries comme morphtargets
++const baseGeometry = geometries[0];
++baseGeometry.morphAttributes.position = geometries.map((geometry, ndx) =&gt; {
++  const attribute = geometry.getAttribute('position');
++  const name = `target${ndx}`;
++  attribute.name = name;
++  return attribute;
++});
++baseGeometry.morphAttributes.color = geometries.map((geometry, ndx) =&gt; {
++  const attribute = geometry.getAttribute('color');
++  const name = `target${ndx}`;
++  attribute.name = name;
++  return attribute;
++});
++const material = new THREE.MeshBasicMaterial({
++  vertexColors: true,
++});
++const mesh = new THREE.Mesh(baseGeometry, material);
++scene.add(mesh);
+
+const uiElem = document.querySelector('#ui');
+fileInfos.forEach((info) =&gt; {
+-  const boxes = addBoxes(info.file, info.hueRange);
+-  info.root = boxes;
+  const div = document.createElement('div');
+  info.elem = div;
+  div.textContent = info.name;
+  uiElem.appendChild(div);
+  function show() {
+    showFileInfo(fileInfos, info);
+  }
+  div.addEventListener('mouseover', show);
+  div.addEventListener('touchstart', show);
+});
+// afficher le premier ensemble de données
+showFileInfo(fileInfos, fileInfos[0]);
+</pre>
+<p>Ci-dessus, nous créons une géométrie pour chaque ensemble de données, utilisons la première comme base,
+puis obtenons un attribut <code class="notranslate" translate="no">position</code> de chaque géométrie et l'ajoutons comme
+morphtarget à la géométrie de base pour <code class="notranslate" translate="no">position</code>.</p>
+<p>Maintenant, nous devons changer la manière dont nous affichons et masquons les différents ensembles de données.
+Au lieu d'afficher ou de masquer un maillage, nous devons modifier l'influence des
+morphtargets. Pour l'ensemble de données que nous voulons voir, nous devons avoir une influence de 1,
+et pour tous ceux que nous ne voulons pas voir, nous devons avoir une influence de 0.</p>
+<p>Nous pourrions simplement les régler directement à 0 ou 1, mais si nous faisions cela, nous ne verrions aucune
+animation, cela se ferait instantanément, ce qui ne serait pas différent de ce que nous avons déjà.
+Nous pourrions également écrire du code d'animation personnalisé, ce qui serait facile,
+mais comme le globe webgl original utilise
+<a href="https://github.com/tweenjs/tween.js/">une bibliothèque d'animation</a>, utilisons la même ici.</p>
+<p>Nous devons inclure la bibliothèque</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
++import TWEEN from 'three/addons/libs/tween.module.js';
+</pre>
+<p>Et ensuite, créer un <code class="notranslate" translate="no">Tween</code> pour animer les influences.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// afficher les données sélectionnées, masquer les autres
+function showFileInfo(fileInfos, fileInfo) {
++  const targets = {};
+-  fileInfos.forEach((info) =&gt; {
++  fileInfos.forEach((info, i) =&gt; {
+    const visible = fileInfo === info;
+-    info.root.visible = visible;
+    info.elem.className = visible ? 'selected' : '';
++    targets[i] = visible ? 1 : 0;
+  });
++  const durationInMs = 1000;
++  new TWEEN.Tween(mesh.morphTargetInfluences)
++    .to(targets, durationInMs)
++    .start();
+  requestRenderIfNotRequested();
+}
+</pre>
+<p>Nous sommes également censés appeler <code class="notranslate" translate="no">TWEEN.update</code> à chaque image dans notre boucle de rendu,
+mais cela soulève un problème. « tween.js » est conçu pour un rendu continu,
+mais nous <a href="rendering-on-demand.html">rendons à la demande</a>. Nous pourrions
+passer au rendu continu, mais il est parfois agréable de ne rendre qu'à la demande
+car cela permet d'économiser l'énergie de l'utilisateur lorsque rien ne se passe,
+alors voyons si nous pouvons le faire animer à la demande.</p>
+<p>Nous allons créer un <code class="notranslate" translate="no">TweenManager</code> pour nous aider. Nous l'utiliserons pour créer les <code class="notranslate" translate="no">Tween</code>s
+et les suivre. Il aura une méthode <code class="notranslate" translate="no">update</code> qui retournera <code class="notranslate" translate="no">true</code>
+si nous devons l'appeler à nouveau, et <code class="notranslate" translate="no">false</code> si toutes les animations sont terminées.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class TweenManger {
+  constructor() {
+    this.numTweensRunning = 0;
+  }
+  _handleComplete() {
+    --this.numTweensRunning;
+    console.assert(this.numTweensRunning &gt;= 0);
+  }
+  createTween(targetObject) {
+    const self = this;
+    ++this.numTweensRunning;
+    let userCompleteFn = () =&gt; {};
+    // create a new tween and install our own onComplete callback
+    const tween = new TWEEN.Tween(targetObject).onComplete(function(...args) {
+      self._handleComplete();
+      userCompleteFn.call(this, ...args);
+    });
+    // replace the tween's onComplete function with our own
+    // so we can call the user's callback if they supply one.
+    tween.onComplete = (fn) =&gt; {
+      userCompleteFn = fn;
+      return tween;
+    };
+    return tween;
+  }
+  update() {
+    TWEEN.update();
+    return this.numTweensRunning &gt; 0;
+  }
+}
+</pre>
+<p>Pour l'utiliser, nous allons en créer un</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
++  const tweenManager = new TweenManger();
+
+  ...
+</pre>
+<p>Nous l'utiliserons pour créer nos <code class="notranslate" translate="no">Tween</code>s.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// afficher les données sélectionnées, masquer les autres
+function showFileInfo(fileInfos, fileInfo) {
+  const targets = {};
+  fileInfos.forEach((info, i) =&gt; {
+    const visible = fileInfo === info;
+    info.elem.className = visible ? 'selected' : '';
+    targets[i] = visible ? 1 : 0;
+  });
+  const durationInMs = 1000;
+-  new TWEEN.Tween(mesh.morphTargetInfluences)
++  tweenManager.createTween(mesh.morphTargetInfluences)
+    .to(targets, durationInMs)
+    .start();
+  requestRenderIfNotRequested();
+}
+</pre>
+<p>Ensuite, nous mettrons à jour notre boucle de rendu pour mettre à jour les tweens et continuer à rendre
+s'il y a encore des animations en cours.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
+  renderRequested = false;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
++  if (tweenManager.update()) {
++    requestRenderIfNotRequested();
++  }
+
+  controls.update();
+  renderer.render(scene, camera);
+}
+render();
+</pre>
+<p>Et avec cela, nous devrions pouvoir animer entre les ensembles de données.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-morphtargets.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/lots-of-objects-morphtargets.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que parcourir ceci a été utile. L'utilisation des morphtargets est une technique courante pour
+déplacer de nombreux objets. Par exemple, nous pourrions donner à chaque cube un endroit aléatoire
+dans une autre cible et morpher de là à leurs premières positions sur le globe.
+Cela pourrait être une façon intéressante de présenter le globe.</p>
+<p>Ensuite, vous pourriez être intéressé par l'ajout d'étiquettes à un globe, ce qui est abordé
+dans <a href="align-html-elements-to-3d.html">Aligner les éléments HTML sur la 3D</a>.</p>
+<p>Note : Nous pourrions essayer de simplement représenter le pourcentage d'hommes ou de femmes, ou la différence
+brute, mais compte tenu de la manière dont nous affichons les informations, des cubes qui poussent
+depuis la surface de la terre, nous préférerions que la plupart des cubes soient bas. Si nous
+utilisions l'une de ces autres comparaisons, la plupart des cubes auraient environ la moitié
+de leur hauteur maximale, ce qui ne donnerait pas une bonne visualisation. N'hésitez pas
+à changer <code class="notranslate" translate="no">amountGreaterThan</code> de <a href="/docs/#api/en/math/Math.max(a - b, 0)"><code class="notranslate" translate="no">Math.max(a - b, 0)</code></a> à quelque chose comme <code class="notranslate" translate="no">(a - b)</code>
+« différence brute » ou <code class="notranslate" translate="no">a / (a + b)</code> « pourcentage » et vous verrez ce que je veux dire.</p>
 
         </div>
       </div>

+ 473 - 4
manual/fr/optimize-lots-of-objects.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Optimize Lots of Objects</title>
+    <title>Optimiser Beaucoup d'Objets</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,13 +22,482 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Optimize Lots of Objects</h1>
+        <h1>Optimiser Beaucoup d'Objets</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/optimize-lots-of-objects.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le premier article
+est <a href="fundamentals.html">les bases de three.js</a>. Si vous ne l'avez pas encore lu
+et que vous débutez avec three.js, vous pourriez vouloir commencer par là. </p>
+<p>Il existe de nombreuses façons d'optimiser les choses pour three.js. Une méthode est souvent appelée
+<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
+plusieurs requêtes du système pour dessiner quelque chose. Dessiner 2 choses a plus
+de surcoût que d'en dessiner 1, même si les résultats sont les mêmes, donc une façon d'optimiser
+est de fusionner les maillages (meshes).</p>
+<p>Voyons un exemple où cela est une bonne solution pour un problème.
+Recréons le <a href="https://globe.chromeexperiments.com/">Globe WebGL</a>.</p>
+<p>La première chose à faire est d'obtenir des données. Le Globe WebGL a dit que les données
+qu'ils utilisent proviennent de <a href="http://sedac.ciesin.columbia.edu/gpw/">SEDAC</a>. En consultant
+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>.
+J'ai téléchargé les données avec une résolution de 60 minutes. Ensuite, j'ai examiné les données</p>
+<p>Cela ressemble à ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-txt" translate="no"> ncols         360
+ nrows         145
+ xllcorner     -180
+ yllcorner     -60
+ cellsize      0.99999999999994
+ NODATA_value  -9999
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+ 9.241768 8.790958 2.095345 -9999 0.05114867 -9999 -9999 -9999 -9999 -999...
+ 1.287993 0.4395509 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999...
+ -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
+</pre>
+<p>Il y a quelques lignes qui sont comme des paires clé/valeur suivies de lignes avec une valeur
+par point de grille, une ligne pour chaque rangée de points de données.</p>
+<p>Pour nous assurer que nous comprenons les données, essayons de les tracer en 2D.</p>
+<p>D'abord un peu de code pour charger le fichier texte</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">async function loadFile(url) {
+  const res = await fetch(url);
+  return res.text();
+}
+</pre>
+<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>
+<p>Ensuite, nous avons besoin de code pour analyser le fichier</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function parseData(text) {
+  const data = [];
+  const settings = {data};
+  let max;
+  let min;
+  // split into lines
+  text.split('\n').forEach((line) =&gt; {
+    // split the line by whitespace
+    const parts = line.trim().split(/\s+/);
+    if (parts.length === 2) {
+      // only 2 parts, must be a key/value pair
+      settings[parts[0]] = parseFloat(parts[1]);
+    } else if (parts.length &gt; 2) {
+      // more than 2 parts, must be data
+      const values = parts.map((v) =&gt; {
+        const value = parseFloat(v);
+        if (value === settings.NODATA_value) {
+          return undefined;
+        }
+        max = Math.max(max === undefined ? value : max, value);
+        min = Math.min(min === undefined ? value : min, value);
+        return value;
+      });
+      data.push(values);
+    }
+  });
+  return Object.assign(settings, {min, max});
+}
+</pre>
+<p>Le code ci-dessus renvoie un objet avec toutes les paires clé/valeur du fichier ainsi
+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
+<code class="notranslate" translate="no">max</code> trouvées dans les données.</p>
+<p>Ensuite, nous avons besoin de code pour dessiner ces données</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function drawData(file) {
+  const {min, max, data} = file;
+  const range = max - min;
+  const ctx = document.querySelector('canvas').getContext('2d');
+  // make the canvas the same size as the data
+  ctx.canvas.width = ncols;
+  ctx.canvas.height = nrows;
+  // but display it double size so it's not too small
+  ctx.canvas.style.width = px(ncols * 2);
+  ctx.canvas.style.height = px(nrows * 2);
+  // fill the canvas to dark gray
+  ctx.fillStyle = '#444';
+  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+  // draw each data point
+  data.forEach((row, latNdx) =&gt; {
+    row.forEach((value, lonNdx) =&gt; {
+      if (value === undefined) {
+        return;
+      }
+      const amount = (value - min) / range;
+      const hue = 1;
+      const saturation = 1;
+      const lightness = amount;
+      ctx.fillStyle = hsl(hue, saturation, lightness);
+      ctx.fillRect(lonNdx, latNdx, 1, 1);
+    });
+  });
+}
+
+function px(v) {
+  return `${v | 0}px`;
+}
+
+function hsl(h, s, l) {
+  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
+}
+</pre>
+<p>Et enfin, en liant le tout</p>
+<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')
+  .then(parseData)
+  .then(drawData);
+</pre>
+<p>Nous donne ce résultat</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/gpw-data-viewer.html"></iframe></div>
+  <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>
+</div>
+
+<p></p>
+<p>Donc, cela semble fonctionner. </p>
+<p>Essayons-le en 3D. En partant du code de <a href="rendering-on-demand.html">rendu à la
+demande</a>, nous allons créer une boîte par donnée dans
+le fichier.</p>
+<p>Commençons par créer une simple sphère avec une texture du monde. Voici la texture</p>
+<div class="threejs_center"><img src="../examples/resources/images/world.jpg" style="width: 600px"></div>
+
+<p>Et le code pour le mettre en place.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('resources/images/world.jpg', render);
+  const geometry = new THREE.SphereGeometry(1, 64, 32);
+  const material = new THREE.MeshBasicMaterial({map: texture});
+  scene.add(new THREE.Mesh(geometry, material));
+}
+</pre>
+<p>Notez l'appel à <code class="notranslate" translate="no">render</code> lorsque la texture a fini de charger. Nous en avons besoin
+car nous faisons du <a href="rendering-on-demand.html">rendu à la demande</a> au lieu de le faire en
+continu, nous devons donc rendre la scène une fois que la texture est chargée.</p>
+<p>Ensuite, nous devons modifier le code qui dessinait un point par point de donnée ci-dessus pour
+créer une boîte par point de donnée à la place.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
+  const {min, max, data} = file;
+  const range = max - min;
+
+  // make one box geometry
+  const boxWidth = 1;
+  const boxHeight = 1;
+  const boxDepth = 1;
+  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+  // make it so it scales away from the positive Z axis
+  geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
+
+  // these helpers will make it easy to position the boxes
+  // We can rotate the lon helper on its Y axis to the longitude
+  const lonHelper = new THREE.Object3D();
+  scene.add(lonHelper);
+  // We rotate the latHelper on its X axis to the latitude
+  const latHelper = new THREE.Object3D();
+  lonHelper.add(latHelper);
+  // The position helper moves the object to the edge of the sphere
+  const positionHelper = new THREE.Object3D();
+  positionHelper.position.z = 1;
+  latHelper.add(positionHelper);
+
+  const lonFudge = Math.PI * .5;
+  const latFudge = Math.PI * -0.135;
+  data.forEach((row, latNdx) =&gt; {
+    row.forEach((value, lonNdx) =&gt; {
+      if (value === undefined) {
+        return;
+      }
+      const amount = (value - min) / range;
+      const material = new THREE.MeshBasicMaterial();
+      const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
+      const saturation = 1;
+      const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
+      material.color.setHSL(hue, saturation, lightness);
+      const mesh = new THREE.Mesh(geometry, material);
+      scene.add(mesh);
+
+      // adjust the helpers to point to the latitude and longitude
+      lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
+      latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
+
+      // use the world matrix of the position helper to
+      // position this mesh.
+      positionHelper.updateWorldMatrix(true, false);
+      mesh.applyMatrix4(positionHelper.matrixWorld);
+
+      mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
+    });
+  });
+}
+</pre>
+<p>Le code est principalement direct par rapport à notre code de dessin de test. </p>
+<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
+ne faisions pas cela, elle s'agrandirait à partir du centre, mais nous voulons qu'elles s'éloignent de l'origine.</p>
+<div class="spread">
+  <div>
+    <div data-diagram="scaleCenter" style="height: 250px"></div>
+    <div class="code">par défaut</div>
+  </div>
+  <div>
+    <div data-diagram="scalePositiveZ" style="height: 250px"></div>
+    <div class="code">ajusté</div>
+  </div>
+</div>
+
+<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>
+comme nous l'avons vu dans les <a href="scenegraph.html">graphes de scène</a>, mais plus nous ajoutons de
+nœuds à un graphe de scène, plus cela devient lent.</p>
+<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
+<code class="notranslate" translate="no">positionHelper</code>. Nous utilisons ces objets pour calculer une position autour de la sphère
+où placer la boîte. </p>
+<div class="spread">
+  <div data-diagram="lonLatPos" style="width: 600px; height: 400px;"></div>
+</div>
+
+<p>Ci-dessus, la <span style="color: green;">barre verte</span> représente <code class="notranslate" translate="no">lonHelper</code> et
+est utilisée pour pivoter vers la longitude sur l'équateur. La <span style="color: blue;">
+barre bleue</span> représente <code class="notranslate" translate="no">latHelper</code> qui est utilisée pour pivoter vers une
+latitude au-dessus ou en dessous de l'équateur. La <span style="color: red;">sphère
+rouge</span> représente le décalage que fournit <code class="notranslate" translate="no">positionHelper</code>.</p>
+<p>Nous pourrions faire tous les calculs manuellement pour déterminer les positions sur le globe, mais
+faire les choses de cette manière laisse la majeure partie des calculs à la librairie elle-même, de sorte que nous
+n'avons pas à nous en occuper.</p>
+<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
+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>.
+Enfin, nous mettons à l'échelle le maillage à sa nouvelle position.</p>
+<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
+<code class="notranslate" translate="no">positionHelper</code> pour chaque nouvelle boîte, mais cela aurait été encore plus lent.</p>
+<p>Il y a jusqu'à 360x145 boîtes que nous allons créer. Cela représente jusqu'à 52000 boîtes.
+Comme certains points de données sont marqués comme "NO_DATA", le nombre réel de boîtes
+que nous allons créer est d'environ 19000. Si nous ajoutions 3 objets d'aide supplémentaires par boîte,
+cela représenterait près de 80000 nœuds dans le graphe de scène pour lesquels THREE.js devrait
+calculer les positions. En utilisant plutôt un seul ensemble d'aides pour positionner
+simplement les maillages, nous économisons environ 60000 opérations.</p>
+<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.
+Cela a du sens. Cela signifie simplement que la texture ou les coordonnées de texture commencent à un
+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
+pourquoi il doit être π * -0.135, c'est juste un montant qui a permis aux boîtes de s'aligner
+avec la texture.</p>
+<p>La dernière chose à faire est d'appeler notre chargeur</p>
+<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')
+  .then(parseData)
+-  .then(drawData)
++  .then(addBoxes)
++  .then(render);
+</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
+une fois puisque nous faisons du <a href="rendering-on-demand.html">rendu à la demande</a>.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <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>
+  <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>
+</div>
+
+<p></p>
+<p>Si vous essayez de faire pivoter l'exemple ci-dessus en faisant glisser la souris sur l'échantillon, vous
+remarquerez probablement que c'est lent.</p>
+<p>Nous pouvons vérifier la fréquence d'images en <a href="debugging-javascript.html">ouvrant les
+outils de développement</a> et en activant l'indicateur de fréquence d'images
+du navigateur.</p>
+<div class="threejs_center"><img src="../resources/images/bring-up-fps-meter.gif"></div>
+
+<p>Sur ma machine, je vois une fréquence d'images inférieure à 20 ips.</p>
+<div class="threejs_center"><img src="../resources/images/fps-meter.gif"></div>
+
+<p>Cela ne me semble pas très fluide et je suspecte que beaucoup de gens ont des machines
+plus lentes, ce qui rendrait la situation encore pire. Nous ferions mieux d'étudier l'optimisation.</p>
+<p>Pour ce problème particulier, nous pouvons fusionner toutes les boîtes en une seule géométrie.
+Nous dessinons actuellement environ 19000 boîtes. En les fusionnant en une seule
+géométrie, nous supprimerions 18999 opérations.</p>
+<p>Voici le nouveau code pour fusionner les boîtes en une seule géométrie.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
+  const {min, max, data} = file;
+  const range = max - min;
+
+-  // make one box geometry
+-  const boxWidth = 1;
+-  const boxHeight = 1;
+-  const boxDepth = 1;
+-  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+-  // make it so it scales away from the positive Z axis
+-  geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
+
+  // these helpers will make it easy to position the boxes
+  // We can rotate the lon helper on its Y axis to the longitude
+  const lonHelper = new THREE.Object3D();
+  scene.add(lonHelper);
+  // We rotate the latHelper on its X axis to the latitude
+  const latHelper = new THREE.Object3D();
+  lonHelper.add(latHelper);
+  // The position helper moves the object to the edge of the sphere
+  const positionHelper = new THREE.Object3D();
+  positionHelper.position.z = 1;
+  latHelper.add(positionHelper);
++  // Utilisé pour déplacer le centre de la boîte afin qu'elle s'agrandisse à partir de l'axe Z positif
++  const originHelper = new THREE.Object3D();
++  originHelper.position.z = 0.5;
++  positionHelper.add(originHelper);
+
+  const lonFudge = Math.PI * .5;
+  const latFudge = Math.PI * -0.135;
++  const geometries = [];
+  data.forEach((row, latNdx) =&gt; {
+    row.forEach((value, lonNdx) =&gt; {
+      if (value === undefined) {
+        return;
+      }
+      const amount = (value - min) / range;
+
+-      const material = new THREE.MeshBasicMaterial();
+-      const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
+-      const saturation = 1;
+-      const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
+-      material.color.setHSL(hue, saturation, lightness);
+-      const mesh = new THREE.Mesh(geometry, material);
+-      scene.add(mesh);
+
++      const boxWidth = 1;
++      const boxHeight = 1;
++      const boxDepth = 1;
++      const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+
+      // adjust the helpers to point to the latitude and longitude
+      lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
+      latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
+
+-      // use the world matrix of the position helper to
+-      // position this mesh.
+-      positionHelper.updateWorldMatrix(true, false);
+-      mesh.applyMatrix4(positionHelper.matrixWorld);
+-
+-      mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
+
++      // use the world matrix of the origin helper to
++      // position this geometry
++      positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
++      originHelper.updateWorldMatrix(true, false);
++      geometry.applyMatrix4(originHelper.matrixWorld);
++
++      geometries.push(geometry);
+    });
+  });
+
++  const mergedGeometry = BufferGeometryUtils.mergeGeometries(
++      geometries, false);
++  const material = new THREE.MeshBasicMaterial({color:'red'});
++  const mesh = new THREE.Mesh(mergedGeometry, material);
++  scene.add(mesh);
+
+}
+</pre>
+<p>Ci-dessus, nous avons supprimé le code qui modifiait le point central de la géométrie de la boîte
+et le faisons à la place en ajoutant un <code class="notranslate" translate="no">originHelper</code>. Auparavant, nous utilisions la même
+géométrie 19000 fois. Cette fois, nous créons une nouvelle géométrie pour chaque boîte et
+comme nous allons utiliser <code class="notranslate" translate="no">applyMatrix</code> pour déplacer les sommets de chaque géométrie de boîte,
+autant le faire une fois au lieu de deux.</p>
+<p>À la fin, nous passons un tableau de toutes les géométries à
+<code class="notranslate" translate="no">BufferGeometryUtils.mergeGeometries</code>, ce qui les combinera toutes
+en un seul maillage.</p>
+<p>Nous devons également inclure le <code class="notranslate" translate="no">BufferGeometryUtils</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
+</pre>
+<p>Et maintenant, du moins sur ma machine, j'obtiens 60 images par seconde</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <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>
+  <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>
+</div>
+
+<p></p>
+<p>Cela a donc fonctionné, mais comme il s'agit d'un seul maillage, nous n'obtenons qu'un seul matériau, ce qui
+signifie que nous n'avons qu'une seule couleur, alors qu'avant, nous avions une couleur différente sur chaque boîte. Nous pouvons
+y remédier en utilisant les couleurs de sommet.</p>
+<p>Les couleurs de sommet ajoutent une couleur par sommet. En réglant toutes les couleurs de chaque sommet
+de chaque boîte sur des couleurs spécifiques, chaque boîte aura une couleur différente.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const color = new THREE.Color();
+
+const lonFudge = Math.PI * .5;
+const latFudge = Math.PI * -0.135;
+const geometries = [];
+data.forEach((row, latNdx) =&gt; {
+  row.forEach((value, lonNdx) =&gt; {
+    if (value === undefined) {
+      return;
+    }
+    const amount = (value - min) / range;
+
+    const boxWidth = 1;
+    const boxHeight = 1;
+    const boxDepth = 1;
+    const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+
+    // adjust the helpers to point to the latitude and longitude
+    lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
+    latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
+
+    // use the world matrix of the origin helper to
+    // position this geometry
+    positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
+    originHelper.updateWorldMatrix(true, false);
+    geometry.applyMatrix4(originHelper.matrixWorld);
+
++    // calculer une couleur
++    const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
++    const saturation = 1;
++    const lightness = THREE.MathUtils.lerp(0.4, 1.0, amount);
++    color.setHSL(hue, saturation, lightness);
++    // obtenir les couleurs sous forme de tableau de valeurs de 0 à 255
++    const rgb = color.toArray().map(v =&gt; v * 255);
++
++    // créer un tableau pour stocker les couleurs pour chaque sommet
++    const numVerts = geometry.getAttribute('position').count;
++    const itemSize = 3;  // r, g, b
++    const colors = new Uint8Array(itemSize * numVerts);
++
++    // copier la couleur dans le tableau de couleurs pour chaque sommet
++    colors.forEach((v, ndx) =&gt; {
++      colors[ndx] = rgb[ndx % 3];
++    });
++
++    const normalized = true;
++    const colorAttrib = new THREE.BufferAttribute(colors, itemSize, normalized);
++    geometry.setAttribute('color', colorAttrib);
+
+    geometries.push(geometry);
+  });
+});
+</pre>
+<p>Le code ci-dessus recherche le nombre ou les sommets nécessaires en obtenant l'attribut <code class="notranslate" translate="no">position</code>
+de la géométrie. Nous créons ensuite un <code class="notranslate" translate="no">Uint8Array</code> pour y mettre les couleurs.
+Il ajoute ensuite cela comme un attribut en appelant <code class="notranslate" translate="no">geometry.setAttribute</code>.</p>
+<p>Enfin, nous devons dire à three.js d'utiliser les couleurs de sommet. </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mergedGeometry = BufferGeometryUtils.mergeGeometries(
+    geometries, false);
+-const material = new THREE.MeshBasicMaterial({color:'red'});
++const material = new THREE.MeshBasicMaterial({
++  vertexColors: true,
++});
+const mesh = new THREE.Mesh(mergedGeometry, material);
+scene.add(mesh);
+</pre>
+<p>Et avec cela, nous retrouvons nos couleurs</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <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>
+  <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>
+</div>
 
+<p></p>
+<p>La fusion de géométrie est une technique d'optimisation courante. Par exemple, au lieu de
+100 arbres, vous pourriez fusionner les arbres en 1 seule géométrie, un tas de roches individuelles
+en une seule géométrie de roches, une clôture de piquets individuels en un seul maillage de clôture.
+Un autre exemple dans Minecraft, il ne dessine probablement pas chaque cube individuellement,
+mais crée plutôt des groupes de cubes fusionnés et supprime également sélectivement les faces qui ne
+sont jamais visibles.</p>
+<p>Le problème avec le fait de tout transformer en un seul maillage est qu'il n'est plus facile
+de déplacer une partie qui était auparavant séparée. Cependant, selon notre cas d'utilisation,
+il existe des solutions créatives. Nous en explorerons une dans
+<a href="optimize-lots-of-objects-animated.html">un autre article</a>.</p>
+<p><canvas id="c"></canvas></p>
+<script type="module" src="../resources/threejs-lots-of-objects.js"></script>
         </div>
       </div>
     </div>

+ 358 - 5
manual/fr/picking.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Picking</title>
+    <title>Sélection</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Picking">
+    <meta name="twitter:title" content="Three.js – Sélection">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,365 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Picking</h1>
+        <h1>Sélection</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/picking.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>La <em>sélection (picking)</em> désigne le processus qui consiste à déterminer sur quel objet un utilisateur a cliqué ou touché. Il existe de nombreuses façons d'implémenter la sélection, chacune ayant ses avantages et ses inconvénients. Passons en revue les 2 méthodes les plus courantes.</p>
+<p>La méthode de <em>sélection (picking)</em> probablement la plus courante est le lancé de rayon (raycasting), ce qui signifie <em>lancer</em> un rayon à partir de la souris à travers le frustum (volume de visualisation) de la scène et calculer les objets que ce rayon intersecte. Conceptuellement, c'est très simple.</p>
+<p>D'abord, nous prendrions la position de la souris. Nous la convertirions en espace monde en appliquant la projection et l'orientation de la caméra. Nous calculerions un rayon allant du plan proche du frustum de la caméra au plan éloigné. Ensuite, pour chaque triangle de chaque objet dans la scène, nous vérifierions si ce rayon intersecte ce triangle. Si votre scène contient 1000 objets et que chaque objet a 1000 triangles, alors 1 million de triangles devront être vérifiés.</p>
+<p>Quelques optimisations incluraient de vérifier d'abord si le rayon intersecte la sphère englobante (bounding sphere) ou la boîte englobante (bounding box) d'un objet, c'est-à-dire la sphère ou la boîte qui contient l'objet entier. Si le rayon n'intersecte pas l'une d'elles, nous n'avons pas besoin de vérifier les triangles de cet objet.</p>
+<p>THREE.js fournit une classe <code class="notranslate" translate="no">RayCaster</code> qui fait exactement cela.</p>
+<p>Créons une scène avec 100 objets et essayons de les sélectionner. Nous commencerons avec un exemple tiré de <a href="responsive.html">l'article sur les pages responsives</a></p>
+<p>Quelques changements</p>
+<p>Nous allons faire de la caméra l'enfant d'un autre objet afin que nous puissions faire tourner cet autre objet et que la caméra se déplace autour de la scène comme un perche à selfie.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">*const fov = 60;
+const aspect = 2;  // L'aspect par défaut du canvas
+const near = 0.1;
+*const far = 200;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+*camera.position.z = 30;
+
+const scene = new THREE.Scene();
++scene.background = new THREE.Color('white');
+
++// Place la caméra sur un poteau (la rend enfant d'un objet)
++// afin que nous puissions faire tourner le poteau pour déplacer la caméra autour de la scène
++const cameraPole = new THREE.Object3D();
++scene.add(cameraPole);
++cameraPole.add(camera);
+</pre>
+<p>et dans la fonction <code class="notranslate" translate="no">render</code>, nous ferons tourner le poteau de la caméra.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">cameraPole.rotation.y = time * .1;
+</pre>
+<p>Mettons aussi la lumière sur la caméra pour qu'elle bouge avec elle.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-scene.add(light);
++camera.add(light);
+</pre>
+<p>Générons 100 cubes avec des couleurs aléatoires dans des positions, orientations et échelles aléatoires.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const boxWidth = 1;
+const boxHeight = 1;
+const boxDepth = 1;
+const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+
+function rand(min, max) {
+  if (max === undefined) {
+    max = min;
+    min = 0;
+  }
+  return min + (max - min) * Math.random();
+}
+
+function randomColor() {
+  return `hsl(${rand(360) | 0}, ${rand(50, 100) | 0}%, 50%)`;
+}
+
+const numObjects = 100;
+for (let i = 0; i &lt; numObjects; ++i) {
+  const material = new THREE.MeshPhongMaterial({
+    color: randomColor(),
+  });
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  cube.position.set(rand(-20, 20), rand(-20, 20), rand(-20, 20));
+  cube.rotation.set(rand(Math.PI), rand(Math.PI), 0);
+  cube.scale.set(rand(3, 6), rand(3, 6), rand(3, 6));
+}
+</pre>
+<p>Et enfin, effectuons la sélection.</p>
+<p>Créons une classe simple pour gérer la sélection</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class PickHelper {
+  constructor() {
+    this.raycaster = new THREE.Raycaster();
+    this.pickedObject = null;
+    this.pickedObjectSavedColor = 0;
+  }
+  pick(normalizedPosition, scene, camera, time) {
+    // Rétablit la couleur s'il y a un objet sélectionné
+    if (this.pickedObject) {
+      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
+      this.pickedObject = undefined;
+    }
+
+    // Lance un rayon à travers le frustum
+    this.raycaster.setFromCamera(normalizedPosition, camera);
+    // Obtient la liste des objets intersectés par le rayon
+    const intersectedObjects = this.raycaster.intersectObjects(scene.children);
+    if (intersectedObjects.length) {
+      // Sélectionne le premier objet. C'est le plus proche
+      this.pickedObject = intersectedObjects[0].object;
+      // Sauvegarde sa couleur
+      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+      // Définit sa couleur émissive sur un rouge/jaune clignotant
+      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+    }
+  }
+}
+</pre>
+<p>Vous pouvez voir que nous créons un <code class="notranslate" translate="no">RayCaster</code> et que nous pouvons ensuite appeler la fonction <code class="notranslate" translate="no">pick</code> pour lancer un rayon à travers la scène. Si le rayon touche quelque chose, nous changeons la couleur du premier objet qu'il touche.</p>
+<p>Bien sûr, nous pourrions appeler cette fonction uniquement lorsque l'utilisateur appuie sur le bouton de la souris (mouse <em>down</em>), ce qui est probablement ce que vous voulez généralement, mais pour cet exemple, nous sélectionnerons à chaque image ce qui se trouve sous la souris. Pour ce faire, nous devons d'abord suivre la position de la souris.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickPosition = {x: 0, y: 0};
+clearPickPosition();
+
+...
+
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
+  pickPosition.x = (pos.x / canvas.width ) *  2 - 1;
+  pickPosition.y = (pos.y / canvas.height) * -2 + 1;  // Note : on inverse Y
+}
+
+function clearPickPosition() {
+  // Contrairement à la souris qui a toujours une position
+  // si l'utilisateur arrête de toucher l'écran, nous voulons
+  // arrêter la sélection. Pour l'instant, nous choisissons simplement une valeur
+  // peu susceptible de sélectionner quelque chose
+  pickPosition.x = -100000;
+  pickPosition.y = -100000;
+}
+
+window.addEventListener('mousemove', setPickPosition);
+window.addEventListener('mouseout', clearPickPosition);
+window.addEventListener('mouseleave', clearPickPosition);
+</pre>
+<p>Remarquez que nous enregistrons une position de souris normalisée. Indépendamment de la taille du canvas, nous avons besoin d'une valeur qui va de -1 à gauche à +1 à droite. De même, nous avons besoin d'une valeur qui va de -1 en bas à +1 en haut.</p>
+<p>Pendant que nous y sommes, prenons également en charge les appareils mobiles.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">window.addEventListener('touchstart', (event) =&gt; {
+  // Empêche le défilement de la fenêtre
+  event.preventDefault();
+  setPickPosition(event.touches[0]);
+}, {passive: false});
+
+window.addEventListener('touchmove', (event) =&gt; {
+  setPickPosition(event.touches[0]);
+});
+
+window.addEventListener('touchend', clearPickPosition);
+</pre>
+<p>Et enfin, dans notre fonction <code class="notranslate" translate="no">render</code>, nous appelons la fonction <code class="notranslate" translate="no">pick</code> de <code class="notranslate" translate="no">PickHelper</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const pickHelper = new PickHelper();
+
+function render(time) {
+  time *= 0.001;  // Convertit en secondes ;
+
+  ...
+
++  pickHelper.pick(pickPosition, scene, camera, time);
+
+  renderer.render(scene, camera);
+
+  ...
+</pre>
+<p>et voici le résultat</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/picking-raycaster.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/picking-raycaster.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela semble fonctionner parfaitement et c'est probablement le cas pour de nombreuses utilisations, mais il y a plusieurs problèmes.</p>
+<ol>
+<li><p>C'est basé sur le CPU.</p>
+<p>JavaScript parcourt chaque objet et vérifie si le rayon intersecte la boîte ou la sphère englobante de cet objet. Si c'est le cas, JavaScript doit parcourir chaque triangle de cet objet et vérifier si le rayon intersecte le triangle.</p>
+<p>Le bon côté de cela est que JavaScript peut facilement calculer exactement où le rayon a intersecté le triangle et nous fournir ces données. Par exemple, si vous vouliez placer un marqueur là où l'intersection s'est produite.</p>
+<p>Le mauvais côté est que cela représente beaucoup de travail pour le CPU. Si vous avez des objets avec beaucoup de triangles, cela pourrait être lent.</p>
+</li>
+<li><p>Cela ne gère pas les shaders étranges ou les déplacements.</p>
+<p>Si vous avez un shader qui déforme ou morph le géométrie, JavaScript n'a aucune connaissance de cette déformation et donnera donc la mauvaise réponse. Par exemple, à ma connaissance, vous ne pouvez pas utiliser cette méthode avec des objets skinnés (avec animation par squelette).</p>
+</li>
+<li><p>Cela ne gère pas les trous transparents.</p>
+</li>
+</ol>
+<p>À titre d'exemple, appliquons cette texture aux cubes.</p>
+<div class="threejs_center"><img class="checkerboard" src="../examples/resources/images/frame.png"></div>
+
+<p>Nous allons juste apporter ces modifications</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
++const texture = loader.load('resources/images/frame.png');
+
+const numObjects = 100;
+for (let i = 0; i &lt; numObjects; ++i) {
+  const material = new THREE.MeshPhongMaterial({
+    color: randomColor(),
+    +map: texture,
+    +transparent: true,
+    +side: THREE.DoubleSide,
+    +alphaTest: 0.1,
+  });
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  ...
+</pre>
+<p>Et en exécutant cela, vous devriez rapidement voir le problème</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/picking-raycaster-transparency.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/picking-raycaster-transparency.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Essayez de sélectionner quelque chose à travers une boîte et vous ne le pouvez pas</p>
+<div class="threejs_center"><img src="../resources/images/picking-transparent-issue.jpg" style="width: 635px;"></div>
+
+<p>C'est parce que JavaScript ne peut pas facilement examiner les textures et les matériaux et déterminer si une partie de votre objet est réellement transparente ou non.</p>
+<p>Une solution à tous ces problèmes est d'utiliser la sélection basée sur le GPU. Malheureusement, bien que conceptuellement simple, elle est plus compliquée à utiliser que la méthode de lancé de rayon ci-dessus.</p>
+<p>Pour faire de la sélection par GPU, nous rendons chaque objet dans une couleur unique hors écran. Nous consultons ensuite la couleur du pixel correspondant à la position de la souris. La couleur nous indique quel objet a été sélectionné.</p>
+<p>Cela peut résoudre les problèmes 2 et 3 ci-dessus. Quant au problème 1, la vitesse, cela dépend vraiment. Chaque objet doit être dessiné deux fois. Une fois pour l'affichage normal et encore pour la sélection. Il est possible avec des solutions plus sophistiquées que les deux puissent être faites en même temps, mais nous n'allons pas essayer cela.</p>
+<p>Une chose que nous pouvons faire, cependant, puisque nous ne lirons qu'un seul pixel, est de configurer la caméra de manière à ce que seul ce pixel soit dessiné. Nous pouvons le faire en utilisant <a href="/docs/#api/en/cameras/PerspectiveCamera.setViewOffset"><code class="notranslate" translate="no">PerspectiveCamera.setViewOffset</code></a> qui nous permet de dire à THREE.js de calculer une caméra qui rend juste une partie plus petite d'un rectangle plus grand. Cela devrait faire gagner du temps.</p>
+<p>Pour effectuer ce type de sélection dans THREE.js à l'heure actuelle, il faut créer 2 scènes. L'une que nous remplirons avec nos maillages normaux. L'autre que nous remplirons avec des maillages qui utilisent notre matériau de sélection.</p>
+<p>Donc, d'abord, créez une deuxième scène et assurez-vous qu'elle se vide en noir.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+scene.background = new THREE.Color('white');
+const pickingScene = new THREE.Scene();
+pickingScene.background = new THREE.Color(0);
+</pre>
+<p>Ensuite, pour chaque cube que nous plaçons dans la scène principale, nous créons un "cube de sélection" correspondant à la même position que le cube original, le plaçons dans la <code class="notranslate" translate="no">pickingScene</code>, et définissons son matériau de manière à dessiner l'identifiant de l'objet comme sa couleur. Nous conservons également une carte des identifiants vers les objets afin que lorsque nous recherchons un identifiant plus tard, nous puissions le mapper à l'objet correspondant.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const idToObject = {};
++const numObjects = 100;
+for (let i = 0; i &lt; numObjects; ++i) {
++  const id = i + 1;
+  const material = new THREE.MeshPhongMaterial({
+    color: randomColor(),
+    map: texture,
+    transparent: true,
+    side: THREE.DoubleSide,
+    alphaTest: 0.1,
+  });
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
++  idToObject[id] = cube;
+
+  cube.position.set(rand(-20, 20), rand(-20, 20), rand(-20, 20));
+  cube.rotation.set(rand(Math.PI), rand(Math.PI), 0);
+  cube.scale.set(rand(3, 6), rand(3, 6), rand(3, 6));
+
++  const pickingMaterial = new THREE.MeshPhongMaterial({
++    emissive: new THREE.Color().setHex(id, THREE.NoColorSpace),
++    color: new THREE.Color(0, 0, 0),
++    specular: new THREE.Color(0, 0, 0),
++    map: texture,
++    transparent: true,
++    side: THREE.DoubleSide,
++    alphaTest: 0.5,
++    blending: THREE.NoBlending,
++  });
++  const pickingCube = new THREE.Mesh(geometry, pickingMaterial);
++  pickingScene.add(pickingCube);
++  pickingCube.position.copy(cube.position);
++  pickingCube.rotation.copy(cube.rotation);
++  pickingCube.scale.copy(cube.scale);
+}
+</pre>
+<p>Notez que nous faisons un usage "abusif" du <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> ici. En définissant son <code class="notranslate" translate="no">emissive</code> sur notre identifiant et les attributs <code class="notranslate" translate="no">color</code> et <code class="notranslate" translate="no">specular</code> à 0, cela finira par rendre l'identifiant uniquement là où l'alpha de la texture est supérieur à <code class="notranslate" translate="no">alphaTest</code>. Nous devons également définir <code class="notranslate" translate="no">blending</code> à <code class="notranslate" translate="no">NoBlending</code> afin que l'identifiant ne soit pas multiplié par l'alpha.</p>
+<p>Notez que l'abus du <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> pourrait ne pas être la meilleure solution car il calculera toujours toutes nos lumières lors du dessin de la scène de sélection, même si nous n'avons pas besoin de ces calculs. Une solution plus optimisée créerait un shader personnalisé qui écrit simplement l'identifiant là où l'alpha de la texture est supérieur à <code class="notranslate" translate="no">alphaTest</code>.</p>
+<p>Comme nous sélectionnons à partir de pixels au lieu de lancer des rayons, nous pouvons modifier le code qui définit la position de sélection pour utiliser simplement des pixels.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
+-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // Note : on inverse Y
++  pickPosition.x = pos.x;
++  pickPosition.y = pos.y;
+}
+</pre>
+<p>Ensuite, changeons la classe <code class="notranslate" translate="no">PickHelper</code> en <code class="notranslate" translate="no">GPUPickHelper</code>. Elle utilisera un <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a> comme nous l'avons vu dans l'<a href="rendertargets.html">article sur les render targets</a>. Notre render target ici ne fait qu'un seul pixel, 1x1.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-class PickHelper {
++class GPUPickHelper {
+  constructor() {
+-    this.raycaster = new THREE.Raycaster();
++    // Crée un render target de 1x1 pixel
++    this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);
++    this.pixelBuffer = new Uint8Array(4);
+    this.pickedObject = null;
+    this.pickedObjectSavedColor = 0;
+  }
+  pick(cssPosition, scene, camera, time) {
++    const {pickingTexture, pixelBuffer} = this;
+
+    // Rétablit la couleur s'il y a un objet sélectionné
+    if (this.pickedObject) {
+      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
+      this.pickedObject = undefined;
+    }
+
++    // Définit le décalage de vue pour représenter juste un seul pixel sous la souris
++    const pixelRatio = renderer.getPixelRatio();
++    camera.setViewOffset(
++        renderer.getContext().drawingBufferWidth,   // Largeur totale
++        renderer.getContext().drawingBufferHeight,  // Hauteur totale
++        cssPosition.x * pixelRatio | 0,             // rect x
++        cssPosition.y * pixelRatio | 0,             // rect y
++        1,                                          // rect largeur
++        1,                                          // rect hauteur
++    );
++    // Rend la scène
++    renderer.setRenderTarget(pickingTexture)
++    renderer.render(scene, camera);
++    renderer.setRenderTarget(null);
++
++    // Efface le décalage de vue pour que le rendu redevienne normal
++    camera.clearViewOffset();
++    // Lit le pixel
++    renderer.readRenderTargetPixels(
++        pickingTexture,
++        0,   // x
++        0,   // y
++        1,   // largeur
++        1,   // hauteur
++        pixelBuffer);
++
++    const id =
++        (pixelBuffer[0] &lt;&lt; 16) |
++        (pixelBuffer[1] &lt;&lt;  8) |
++        (pixelBuffer[2]      );
+
+-    // Lance un rayon à travers le frustum
+-    this.raycaster.setFromCamera(normalizedPosition, camera);
+-    // Obtient la liste des objets intersectés par le rayon
+-    const intersectedObjects = this.raycaster.intersectObjects(scene.children);
+-    if (intersectedObjects.length) {
+-      // Sélectionne le premier objet. C'est le plus proche
+-      this.pickedObject = intersectedObjects[0].object;
+
++    const intersectedObject = idToObject[id];
++    if (intersectedObject) {
++      // Sélectionne le premier objet. C'est le plus proche
++      this.pickedObject = intersectedObject;
+      // Sauvegarde sa couleur
+      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+      // Définit sa couleur émissive sur un rouge/jaune clignotant
+      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+    }
+  }
+}
+</pre>
+<p>Et ensuite, nous devons juste l'utiliser</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const pickHelper = new PickHelper();
++const pickHelper = new GPUPickHelper();
+</pre>
+<p>et lui passer la <code class="notranslate" translate="no">pickScene</code> au lieu de la <code class="notranslate" translate="no">scene</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-  pickHelper.pick(pickPosition, scene, camera, time);
++  pickHelper.pick(pickPosition, pickScene, camera, time);
+</pre>
+<p>Et maintenant, cela devrait vous permettre de sélectionner à travers les parties transparentes.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/picking-gpu.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/picking-gpu.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que cela vous donne une idée de la manière d'implémenter la sélection. Dans un futur article, nous pourrons peut-être aborder la manière de manipuler des objets avec la souris.</p>
 
         </div>
       </div>

+ 184 - 5
manual/fr/post-processing.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Post Processing</title>
+    <title>Post-traitement</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Post Processing">
+    <meta name="twitter:title" content="Three.js – Post-traitement">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,191 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Post Processing</h1>
+        <h1>Post-traitement</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/post-processing.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Le <em>post-traitement</em> fait généralement référence à l'application d'une sorte d'effet ou de filtre à une image 2D. Dans le cas de THREE.js, nous avons une scène avec un ensemble de maillages. Nous rendons cette scène en une image 2D. Normalement, cette image est rendue directement dans le canvas et affichée dans le navigateur, mais nous pouvons au lieu de cela <a href="rendertargets.html">la rendre sur une cible de rendu (render target)</a> et ensuite appliquer des effets de <em>post-traitement</em> au résultat avant de le dessiner sur le canvas. On appelle cela post-traitement parce que cela se produit après (post) le traitement principal de la scène.</p>
+<p>Des exemples de post-traitement sont les filtres de type Instagram, les filtres Photoshop, etc...</p>
+<p>THREE.js propose des classes d'exemple pour aider à mettre en place un pipeline de post-traitement. La manière dont cela fonctionne est de créer un <code class="notranslate" translate="no">EffectComposer</code> et d'y ajouter plusieurs objets <code class="notranslate" translate="no">Pass</code>. Ensuite, vous appelez <code class="notranslate" translate="no">EffectComposer.render</code> et cela rend votre scène sur une <a href="rendertargets.html">cible de rendu</a> puis applique chaque <code class="notranslate" translate="no">Pass</code>.</p>
+<p>Chaque <code class="notranslate" translate="no">Pass</code> peut être un effet de post-traitement comme l'ajout d'une vignette, le flou, l'application d'un effet de lumière (bloom), l'application d'un grain de film, le réglage de la teinte, de la saturation, du contraste, etc... et enfin le rendu du résultat sur le canvas.</p>
+<p>Il est un peu important de comprendre comment fonctionne <code class="notranslate" translate="no">EffectComposer</code>. Il crée deux <a href="rendertargets.html">cibles de rendu</a>. Appelons-les <strong>rtA</strong> et <strong>rtB</strong>.</p>
+<p>Ensuite, vous appelez <code class="notranslate" translate="no">EffectComposer.addPass</code> pour ajouter chaque pass dans l'ordre où vous voulez les appliquer. Les passes sont ensuite appliquées <em>à peu près</em> comme ceci.</p>
+<div class="threejs_center"><img src="../resources/images/threejs-postprocessing.svg" style="width: 600px"></div>
+
+<p>D'abord, la scène que vous avez passée à <code class="notranslate" translate="no">RenderPass</code> est rendue sur <strong>rtA</strong>, puis <strong>rtA</strong> est passée à la passe suivante, quelle qu'elle soit. Cette passe utilise <strong>rtA</strong> comme entrée pour faire ce qu'elle a à faire et écrit les résultats sur <strong>rtB</strong>. <strong>rtB</strong> est ensuite passé à la passe suivante qui utilise <strong>rtB</strong> comme entrée et écrit de nouveau sur <strong>rtA</strong>. Cela continue à travers toutes les passes.</p>
+<p>Chaque <code class="notranslate" translate="no">Pass</code> a 4 options de base</p>
+<h2 id="-enabled-"><code class="notranslate" translate="no">enabled</code></h2>
+<p>Indique si cette passe doit être utilisée ou non</p>
+<h2 id="-needsswap-"><code class="notranslate" translate="no">needsSwap</code></h2>
+<p>Indique s'il faut échanger <code class="notranslate" translate="no">rtA</code> et <code class="notranslate" translate="no">rtB</code> après avoir terminé cette passe</p>
+<h2 id="-clear-"><code class="notranslate" translate="no">clear</code></h2>
+<p>Indique s'il faut effacer avant de rendre cette passe</p>
+<h2 id="-rendertoscreen-"><code class="notranslate" translate="no">renderToScreen</code></h2>
+<p>Indique s'il faut rendre sur le canvas au lieu de la cible de rendu de destination actuelle. Dans la plupart des cas d'utilisation, vous ne définissez pas explicitement ce drapeau car la dernière passe de la chaîne est automatiquement rendue sur l'écran.</p>
+<p>Mettons en place un exemple de base. Nous allons commencer avec l'exemple de <a href="responsive.html">l'article sur la réactivité</a>.</p>
+<p>Pour cela, nous créons d'abord un <code class="notranslate" translate="no">EffectComposer</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const composer = new EffectComposer(renderer);
+</pre>
+<p>Ensuite, comme première passe, nous ajoutons un <code class="notranslate" translate="no">RenderPass</code> qui rendra notre scène avec notre caméra dans la première cible de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">composer.addPass(new RenderPass(scene, camera));
+</pre>
+<p>Ensuite, nous ajoutons un <code class="notranslate" translate="no">BloomPass</code>. Un <code class="notranslate" translate="no">BloomPass</code> rend son entrée sur une cible de rendu généralement plus petite et floute le résultat. Il ajoute ensuite ce résultat flouté par-dessus l'entrée originale. Cela fait <em>fleurir</em> (bloom) la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const bloomPass = new BloomPass(
+    1,    // strength
+    25,   // kernel size
+    4,    // sigma ?
+    256,  // blur render target resolution
+);
+composer.addPass(bloomPass);
+</pre>
+<p>Ensuite, nous ajoutons un <code class="notranslate" translate="no">FilmPass</code> qui dessine du bruit et des lignes de balayage par-dessus son entrée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const filmPass = new FilmPass(
+    0.5,   // intensity
+    false,  // grayscale
+);
+composer.addPass(filmPass);
+</pre>
+<p>Enfin, nous ajoutons un <code class="notranslate" translate="no">OutputPass</code> qui effectue la conversion de l'espace couleur en sRGB et un mappage tonal (tone mapping) optionnel. Cette passe est généralement la dernière de la chaîne.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const outputPass = new OutputPass();
+composer.addPass(outputPass);
+</pre>
+<p>Pour utiliser ces classes, nous devons importer un certain nombre de scripts.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js';
+import {RenderPass} from 'three/addons/postprocessing/RenderPass.js';
+import {BloomPass} from 'three/addons/postprocessing/BloomPass.js';
+import {FilmPass} from 'three/addons/postprocessing/FilmPass.js';
+import {OutputPass} from 'three/addons/postprocessing/OutputPass.js';
+</pre>
+<p>Pour pratiquement n'importe quel post-traitement, <code class="notranslate" translate="no">EffectComposer.js</code>, <code class="notranslate" translate="no">RenderPass.js</code> et <code class="notranslate" translate="no">OutputPass.js</code> sont requis.</p>
+<p>Les dernières choses que nous devons faire sont d'utiliser <code class="notranslate" translate="no">EffectComposer.render</code> au lieu de <a href="/docs/#api/en/renderers/WebGLRenderer.render"><code class="notranslate" translate="no">WebGLRenderer.render</code></a> <em>et</em> de dire à l'<code class="notranslate" translate="no">EffectComposer</code> de correspondre à la taille du canvas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function render(now) {
+-  time *= 0.001;
++let then = 0;
++function render(now) {
++  now *= 0.001;  // convertir en secondes
++  const deltaTime = now - then;
++  then = now;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
++    composer.setSize(canvas.width, canvas.height);
+  }
+
+  cubes.forEach((cube, ndx) =&gt; {
+    const speed = 1 + ndx * .1;
+-    const rot = time * speed;
++    const rot = now * speed;
+    cube.rotation.x = rot;
+    cube.rotation.y = rot;
+  });
+
+-  renderer.render(scene, camera);
++  composer.render(deltaTime);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<p><code class="notranslate" translate="no">EffectComposer.render</code> prend un <code class="notranslate" translate="no">deltaTime</code> qui est le temps en secondes depuis le rendu de la dernière frame. Il passe cela aux différents effets au cas où certains d'entre eux seraient animés. Dans ce cas, le <code class="notranslate" translate="no">FilmPass</code> est animé.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/postprocessing.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/postprocessing.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Pour changer les paramètres d'effet à l'exécution, il faut généralement définir des valeurs d'uniformes. Ajoutons une interface graphique (GUI) pour ajuster certains paramètres. Déterminer quelles valeurs vous pouvez facilement ajuster et comment les ajuster nécessite de fouiller dans le code de cet effet.</p>
+<p>En regardant à l'intérieur de <a href="https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/BloomPass.js"><code class="notranslate" translate="no">BloomPass.js</code></a>, j'ai trouvé cette ligne :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">this.combineUniforms[ 'strength' ].value = strength;
+</pre>
+<p>Nous pouvons donc définir la force (strength) en définissant</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">bloomPass.combineUniforms.strength.value = someValue;
+</pre>
+<p>De même, en regardant dans <a href="https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/FilmPass.js"><code class="notranslate" translate="no">FilmPass.js</code></a>, j'ai trouvé ces lignes :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">this.uniforms.intensity.value = intensity;
+this.uniforms.grayscale.value = grayscale;
+</pre>
+<p>Ce qui indique assez clairement comment les définir.</p>
+<p>Faisons une petite interface graphique rapide pour définir ces valeurs</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
+</pre>
+<p>et</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
+{
+  const folder = gui.addFolder('BloomPass');
+  folder.add(bloomPass.combineUniforms.strength, 'value', 0, 2).name('strength');
+  folder.open();
+}
+{
+  const folder = gui.addFolder('FilmPass');
+  folder.add(filmPass.uniforms.grayscale, 'value').name('grayscale');
+  folder.add(filmPass.uniforms.intensity, 'value', 0, 1).name('intensity');
+  folder.open();
+}
+</pre>
+<p>et maintenant nous pouvons ajuster ces paramètres</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/postprocessing-gui.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/postprocessing-gui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Ce fut une petite étape pour créer notre propre effet.</p>
+<p>Les effets de post-traitement utilisent des shaders. Les shaders sont écrits dans un langage appelé <a href="https://www.khronos.org/files/opengles_shading_language.pdf">GLSL (Graphics Library Shading Language)</a>. Passer en revue l'intégralité du langage est un sujet beaucoup trop vaste pour ces articles. Quelques ressources pour commencer seraient peut-être <a href="https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html">cet article</a> et peut-être <a href="https://thebookofshaders.com/">le Livre des Shaders</a>.</p>
+<p>Je pense qu'un exemple pour vous aider à démarrer serait utile, alors créons un simple shader de post-traitement GLSL. Nous en créerons un qui nous permette de multiplier l'image par une couleur.</p>
+<p>Pour le post-traitement, THREE.js fournit un outil utile appelé <code class="notranslate" translate="no">ShaderPass</code>. Il prend un objet avec des informations définissant un shader de vertex, un shader de fragment, et les entrées par défaut. Il gérera la configuration de la texture à lire pour obtenir les résultats de la passe précédente et l'endroit où rendre, soit sur une des cibles de rendu de l'<code class="notranslate" translate="no">EffectComposer</code>, soit sur le canvas.</p>
+<p>Voici un simple shader de post-traitement qui multiplie le résultat de la passe précédente par une couleur.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const colorShader = {
+  uniforms: {
+    tDiffuse: { value: null },
+    color:    { value: new THREE.Color(0x88CCFF) },
+  },
+  vertexShader: `
+    varying vec2 vUv;
+    void main() {
+      vUv = uv;
+      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
+    }
+  `,
+  fragmentShader: `
+    varying vec2 vUv;
+    uniform sampler2D tDiffuse;
+    uniform vec3 color;
+    void main() {
+      vec4 previousPassColor = texture2D(tDiffuse, vUv);
+      gl_FragColor = vec4(
+          previousPassColor.rgb * color,
+          previousPassColor.a);
+    }
+  `,
+};
+</pre>
+<p>Ci-dessus, <code class="notranslate" translate="no">tDiffuse</code> est le nom que <code class="notranslate" translate="no">ShaderPass</code> utilise pour passer la texture résultat de la passe précédente, donc nous en avons pratiquement toujours besoin. Nous déclarons ensuite <code class="notranslate" translate="no">color</code> comme une <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">Color</code></a> de THREE.js.</p>
+<p>Ensuite, nous avons besoin d'un shader de vertex. Pour le post-traitement, le shader de vertex montré ici est à peu près standard et n'a que rarement besoin d'être modifié. Sans entrer dans trop de détails (voir les articles liés ci-dessus), les variables <code class="notranslate" translate="no">uv</code>, <code class="notranslate" translate="no">projectionMatrix</code>, <code class="notranslate" translate="no">modelViewMatrix</code> et <code class="notranslate" translate="no">position</code> sont toutes ajoutées comme par magie par THREE.js.</p>
+<p>Enfin, nous créons un shader de fragment. Dans celui-ci, nous obtenons une couleur de pixel de la passe précédente avec cette ligne</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">vec4 previousPassColor = texture2D(tDiffuse, vUv);
+</pre>
+<p>nous la multiplions par notre couleur et définissons <code class="notranslate" translate="no">gl_FragColor</code> au résultat</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">gl_FragColor = vec4(
+    previousPassColor.rgb * color,
+    previousPassColor.a);
+</pre>
+<p>Ajoutons une simple interface graphique (GUI) pour définir les 3 valeurs de la couleur</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
+gui.add(colorPass.uniforms.color.value, 'r', 0, 4).name('red');
+gui.add(colorPass.uniforms.color.value, 'g', 0, 4).name('green');
+gui.add(colorPass.uniforms.color.value, 'b', 0, 4).name('blue');
+</pre>
+<p>Ce qui nous donne un simple effet de post-traitement qui multiplie par une couleur.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/postprocessing-custom.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/postprocessing-custom.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Comme mentionné précédemment, tous les détails sur la manière d'écrire du GLSL et des shaders personnalisés sont trop complexes pour ces articles. Si vous voulez vraiment savoir comment fonctionne WebGL lui-même, consultez <a href="https://webglfundamentals.org">ces articles</a>. Une autre excellente ressource est simplement de <a href="https://github.com/mrdoob/three.js/tree/master/examples/jsm/shaders">lire les shaders de post-traitement existants dans le dépôt THREE.js</a>. Certains sont plus compliqués que d'autres, mais si vous commencez par les plus petits, vous pourrez, je l'espère, vous faire une idée de leur fonctionnement.</p>
+<p>La plupart des effets de post-traitement dans le dépôt THREE.js ne sont malheureusement pas documentés, donc pour les utiliser, vous devrez <a href="https://github.com/mrdoob/three.js/tree/master/examples">lire les exemples</a> ou <a href="https://github.com/mrdoob/three.js/tree/master/examples/jsm/postprocessing">le code des effets eux-mêmes</a>. J'espère que ces simples exemples et l'article sur les <a href="rendertargets.html">cibles de rendu</a> vous fourniront suffisamment de contexte pour commencer.</p>
 
         </div>
       </div>

+ 182 - 157
manual/fr/prerequisites.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Pré-requis pour </title>
+    <title>Conditions préalables</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Pré-requis pour ">
+    <meta name="twitter:title" content="Three.js – Conditions préalables">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,48 +22,53 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Pré-requis pour </h1>
+        <h1>Conditions préalables</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Ces articles sont faits pour vous aider à apprendre comment utiliser three.js.
-Ils supposent que :</p>
-<ul>
-<li>vous savez programmer en Javascript ;</li>
-<li>vous savez ce que c'est qu'un <em>DOM</em>, comment écrire du HTML, ainsi que créer des éléments <em>DOM</em>
-en Javascript ;</li>
-<li>vous savez exploiter les
-<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">modules es6</a>
-que ce soit via le mot clef import ou via les balises <code class="notranslate" translate="no">&lt;script type="module"&gt;</code> ;</li>
-<li>vous avez des connaissances en CSS et savez ce que sont
-<a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors">les sélecteurs CSS</a>.</li>
-<li>vous connaissez ES5, ES6, voire ES7 ;</li>
-<li>vous savez que le navigateur n'exécute que du Javascript de façon événementiel via des fonctions de rappel (<em>callbacks</em>) ;</li>
-<li>vous savez ce qu'est une fonction de clôture (<em>closure</em>).</li>
-</ul>
-<p>Voici ci-dessous quelques rappels et notes.</p>
-<h2 id="modules-es6">Modules es6</h2>
-<p>Les modules es6 peuvent être chargé via le mot-clé <code class="notranslate" translate="no">import</code> dans un script
-ou en ligne via une balise <code class="notranslate" translate="no">&lt;script type="module"&gt;</code>. Voici un exemple des deux</p>
-<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;script type="module"&gt;
+          <p>Ces articles sont destinés à vous aider à apprendre à utiliser three.js.
+Ils supposent que vous savez programmer en JavaScript. Ils supposent
+que vous savez ce qu'est le DOM, comment écrire du code HTML et comment créer des éléments DOM
+en JavaScript. Ils supposent que vous savez utiliser
+<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">les modules es6</a>
+via import et via les balises <code class="notranslate" translate="no">&lt;script type="module"&gt;</code>. Ils supposent que vous savez utiliser les import maps.
+Ils supposent que vous connaissez un peu de CSS et que vous savez ce que
+<a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors">sont les sélecteurs CSS</a>.
+Ils supposent également que vous connaissez ES5, ES6 et peut-être un peu ES7.
+Ils supposent que vous savez que le navigateur n'exécute du JavaScript que via des événements et des callbacks.
+Ils supposent que vous savez ce qu'est une closure.</p>
+<p>Voici quelques rappels et notes</p>
+<h2 id="es6-modules">modules es6</h2>
+<p>Les modules es6 peuvent être chargés via le mot-clé <code class="notranslate" translate="no">import</code> dans un script
+ou en ligne via une balise <code class="notranslate" translate="no">&lt;script type="module"&gt;</code>. Voici un exemple</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">
+&lt;script type="importmap"&gt;
+{
+  "imports": {
+    "three": "./path/to/three.module.js",
+    "three/addons/": "./different/path/to/examples/jsm/"
+  }
+}
+&lt;/script&gt;
+
+&lt;script type="module"&gt;
 import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
 
 ...
 
 &lt;/script&gt;
 </pre>
-<p>Les chemins doivent être absolus ou relatifs. Ces derniers débutent toujours par <code class="notranslate" translate="no">./</code> ou <code class="notranslate" translate="no">../</code>,
-ce qui est différent des autres balises telles que <code class="notranslate" translate="no">&lt;img&gt;</code>, <code class="notranslate" translate="no">&lt;a&gt;</code> et les références css.</p>
-<p>Davantage de détails se trouvent à la fin de <a href="fundamentals.html">cet article</a>.</p>
-<h2 id="-document-queryselector-et-document-queryselectorall-"><code class="notranslate" translate="no">document.querySelector</code> et <code class="notranslate" translate="no">document.querySelectorAll</code></h2>
-<p>Vous pouvez utiliser <code class="notranslate" translate="no">document.querySelector</code> pour sélectionner le
-premier élément qui correspond à un sélecteur CSS.
-<code class="notranslate" translate="no">document.querySelectorAll</code> retourne tous les éléments qui correspondent
-à un sélecteur CSS.</p>
-<h2 id="-onbody-n-est-pas-n-cessaire"><code class="notranslate" translate="no">onbody</code> n'est pas nécessaire</h2>
-<p>Beaucoup de pages vielles d'il y a 20 ans utilisent</p>
+<p>Voir plus de détails au bas de <a href="fundamentals.html">cet article</a>.</p>
+<h2 id="-document-queryselector-and-document-queryselectorall-"><code class="notranslate" translate="no">document.querySelector</code> et <code class="notranslate" translate="no">document.querySelectorAll</code></h2>
+<p>Vous pouvez utiliser <code class="notranslate" translate="no">document.querySelector</code> pour sélectionner le premier élément
+qui correspond à un sélecteur CSS. <code class="notranslate" translate="no">document.querySelectorAll</code> retourne
+tous les éléments qui correspondent à un sélecteur CSS.</p>
+<h2 id="you-don-t-need-onload-">Vous n'avez pas besoin de <code class="notranslate" translate="no">onload</code></h2>
+<p>Beaucoup de pages vieilles de 20 ans utilisent du HTML comme ceci</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">&lt;body onload="somefunction()"&gt;
-</pre><p>Ce style est déprécié. Mettez vos scripts à la fin de la page.</p>
+</pre><p>Ce style est obsolète. Mettez vos scripts
+au bas de la page.</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;html&gt;
   &lt;head&gt;
     ...
@@ -72,12 +77,12 @@ premier élément qui correspond à un sélecteur CSS.
      ...
   &lt;/body&gt;
   &lt;script&gt;
-    // inline javascript
+    // javascript inline
   &lt;/script&gt;
 &lt;/html&gt;
 </pre>
 <p>ou <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script">utilisez la propriété <code class="notranslate" translate="no">defer</code></a>.</p>
-<h2 id="conna-tre-le-fonctionnement-des-cl-tures-closures-">Connaître le fonctionnement des clôtures (<em>closures</em>)</h2>
+<h2 id="know-how-closures-work">Savoir comment fonctionnent les closures</h2>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function a(v) {
   const foo = v;
   return function() {
@@ -87,58 +92,81 @@ premier élément qui correspond à un sélecteur CSS.
 
 const f = a(123);
 const g = a(456);
-console.log(f());  // affiche 123
-console.log(g());  // affiche 456
+console.log(f());  // imprime 123
+console.log(g());  // imprime 456
 </pre>
-<p>Dans le code ci-dessus, la fonction <code class="notranslate" translate="no">a</code> créé une nouvelle fonction chaque fois qu'elle est appelée.
-Cette fonction se <em>clôt</em> sur la variable <code class="notranslate" translate="no">foo</code>. Voici <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">davantage d'information</a>.</p>
-<h2 id="comprendre-le-fonctionnement-de-this-">Comprendre le fonctionnement de <code class="notranslate" translate="no">this</code></h2>
-<p><code class="notranslate" translate="no">this</code> est une variable passée automatiquement aux fonctions tout comme les arguments y sont passés.
-Une explication simple est que quand vous appelez une fonction directement comme ceci :</p>
+<p>Dans le code ci-dessus, la fonction <code class="notranslate" translate="no">a</code> crée une nouvelle fonction à chaque fois qu'elle est appelée. Cette
+fonction <em>englobe</em> la variable <code class="notranslate" translate="no">foo</code>. Voici <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">plus d'informations</a>.</p>
+<h2 id="understand-how-this-works">Comprendre comment fonctionne <code class="notranslate" translate="no">this</code></h2>
+<p><code class="notranslate" translate="no">this</code> n'est pas magique. C'est effectivement une variable qui est automatiquement passée aux fonctions, tout comme
+un argument est passé à une fonction. L'explication simple est que lorsque vous appelez une fonction directement
+comme ceci</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">somefunction(a, b, c);
-</pre><p><code class="notranslate" translate="no">this</code> prendra la valeur <code class="notranslate" translate="no">null</code> (dans le cas du mode <em>strict</em> ou d'un module) tandis que lorsque vous
-appelez une fonction via l'opérateur <code class="notranslate" translate="no">.</code> comme ceci :</p>
+</pre><p><code class="notranslate" translate="no">this</code> sera <code class="notranslate" translate="no">null</code> (en mode strict ou dans un module), alors que lorsque vous appelez une fonction via l'opérateur point <code class="notranslate" translate="no">.</code> comme ceci</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">someobject.somefunction(a, b, c);
-</pre><p><code class="notranslate" translate="no">this</code> sera une référence vers <code class="notranslate" translate="no">someobject</code>.</p>
-<p>Ce fonctionnement peut dérouter lorsqu'il est combiné avec les fonctions de rappel (<em>callbacks</em>).</p>
+</pre><p><code class="notranslate" translate="no">this</code> sera défini sur <code class="notranslate" translate="no">someobject</code>.</p>
+<p>Là où les gens se perdent, c'est avec les callbacks.</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction;
  loader.load(callback);
-</pre><p>Ceci ne fonctionne pas comme s'y attendrait une personne inexpérimentée parce que, quand
-<code class="notranslate" translate="no">loader.load</code> appelle la fonction de rappel, il n'utilise pas l'opérateur <code class="notranslate" translate="no">.</code> et donc
-par défaut <code class="notranslate" translate="no">this</code> est null (à moins que loader le fixe arbitrairement à une valeur).
-Si vous souhaitez que <code class="notranslate" translate="no">this</code> se rapporte à <code class="notranslate" translate="no">someobject</code> quand la fonction de rappelle
-est activée, vous devez dire à Javascript de le lier à la fonction.</p>
+</pre><p>ne fonctionne pas comme une personne inexpérimentée pourrait s'attendre, car lorsque
+<code class="notranslate" translate="no">loader.load</code> appelle le callback, il ne l'appelle pas avec l'opérateur point <code class="notranslate" translate="no">.</code>
+donc par défaut <code class="notranslate" translate="no">this</code> sera null (sauf si le loader le définit explicitement sur autre chose).
+Si vous voulez que <code class="notranslate" translate="no">this</code> soit <code class="notranslate" translate="no">someobject</code> lorsque le callback a lieu, vous devez
+le dire à JavaScript en le liant à la fonction.</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction.bind(someobject);
  loader.load(callback);
-</pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">Cet article peut aider à expliquer <code class="notranslate" translate="no">this</code></a>.</p>
-<h2 id="particularit-s-d-es5-es6-es7">Particularités d'ES5/ES6/ES7</h2>
-<h3 id="-var-est-d-pr-ci-privil-giez-l-usage-de-const-et-ou-let-"><code class="notranslate" translate="no">var</code> est déprécié. Privilégiez l'usage de <code class="notranslate" translate="no">const</code> et/ou <code class="notranslate" translate="no">let</code></h3>
-<p>Il n'y a <strong>PLUS AUCUNE</strong> raison d'utiliser <code class="notranslate" translate="no">var</code>. L'utiliser est dorénavant considéré
-comme une mauvaise pratique. Utilisez <code class="notranslate" translate="no">const</code> si la variable n'est jamais réaffectée,
-ce qui se passe dans la majorité des cas. Utilisez <code class="notranslate" translate="no">let</code> dans le cas où la valeur change.
-Cela aidera à éviter beaucoup de bogues.</p>
-<h3 id="utilisez-for-elem-of-collection-jamais-for-elem-in-collection-">Utilisez <code class="notranslate" translate="no">for(elem of collection)</code> jamais <code class="notranslate" translate="no">for(elem in collection)</code></h3>
-<p><code class="notranslate" translate="no">for of</code> est récent, <code class="notranslate" translate="no">for in</code> est ancien. <code class="notranslate" translate="no">for in</code> avait des problèmes résolus par <code class="notranslate" translate="no">for of</code></p>
-<p>Voici un exemple où vous pouvez itérer au travers de toutes les paires clef/valeur
-d'un objet :</p>
+</pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this"><em>Cet</em> article pourrait aider à expliquer <code class="notranslate" translate="no">this</code></a>.</p>
+<h2 id="es5-es6-es7-stuff">Éléments ES5/ES6/ES7</h2>
+<h3 id="-var-is-deprecated-use-const-and-or-let-"><code class="notranslate" translate="no">var</code> est obsolète. Utilisez <code class="notranslate" translate="no">const</code> et/ou <code class="notranslate" translate="no">let</code></h3>
+<p>Il n'y a aucune raison d'utiliser <code class="notranslate" translate="no">var</code> <strong>JAMAIS</strong> et à ce stade, il est considéré comme une mauvaise pratique
+de l'utiliser du tout. Utilisez <code class="notranslate" translate="no">const</code> si la variable ne sera jamais réassignée, ce qui est la plupart du temps.
+Utilisez <code class="notranslate" translate="no">let</code> dans les cas où la valeur change. Cela aidera à éviter des tonnes de bugs.</p>
+<h3 id="use-for-elem-of-collection-never-for-elem-in-collection-">Utilisez <code class="notranslate" translate="no">for(elem of collection)</code> jamais <code class="notranslate" translate="no">for(elem in collection)</code></h3>
+<p><code class="notranslate" translate="no">for of</code> est nouveau, <code class="notranslate" translate="no">for in</code> est ancien. <code class="notranslate" translate="no">for in</code> avait des problèmes qui sont résolus par <code class="notranslate" translate="no">for of</code></p>
+<p>Par exemple, vous pouvez itérer sur toutes les paires clé/valeur d'un objet avec</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (const [key, value] of Object.entries(someObject)) {
   console.log(key, value);
 }
 </pre>
-<h3 id="utilisez-foreach-map-et-filter-quand-c-est-utile">Utilisez <code class="notranslate" translate="no">forEach</code>, <code class="notranslate" translate="no">map</code>, et <code class="notranslate" translate="no">filter</code> quand c'est utile</h3>
-<p>Les fonctions <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach"><code class="notranslate" translate="no">forEach</code></a>,
-<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code class="notranslate" translate="no">map</code></a>, et
-<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"><code class="notranslate" translate="no">filter</code></a> ont
-été ajoutées aux tableaux (<em>arrays</em>) et sont utilisés de manière assez intensives en JavaScript moderne.</p>
-<h3 id="utiliser-l-affectation-par-d-composition-destructuring-">Utiliser l'affectation par décomposition (<em>destructuring</em>)</h3>
-<p>Soit l'objet <code class="notranslate" translate="no">const dims = {width: 300, height: 150}</code></p>
+<h3 id="use-foreach-map-and-filter-where-useful">Utilisez <code class="notranslate" translate="no">forEach</code>, <code class="notranslate" translate="no">map</code> et <code class="notranslate" translate="no">filter</code> là où c'est utile</h3>
+<p>Les tableaux ont ajouté les fonctions <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach"><code class="notranslate" translate="no">forEach</code></a>,
+<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code class="notranslate" translate="no">map</code></a> et
+<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"><code class="notranslate" translate="no">filter</code></a> et
+sont assez largement utilisés dans le JavaScript moderne.</p>
+<h3 id="use-destructuring">Utilisez la déstructuration</h3>
+<p>Supposons un objet <code class="notranslate" translate="no">const dims = {width: 300, height: 150}</code></p>
+<p>ancien code</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const width = dims.width;
+const height = dims.height;
+</pre>
+<p>nouveau code</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const {width, height} = dims;
+</pre>
+<p>La déstructuration fonctionne aussi avec les tableaux. Supposons un tableau <code class="notranslate" translate="no">const position = [5, 6, 7, 1]</code>;</p>
+<p>ancien code</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const y = position[1];
+const z = position[2];
+</pre>
+<p>nouveau code</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const [, y, z] = position;
+</pre>
+<p>La déstructuration fonctionne également dans les arguments de fonction</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const dims = {width: 300, height: 150};
+const vector = [3, 4];
+
+function lengthOfVector([x, y]) {
+  return Math.sqrt(x * x + y * y);
+}
+
+const dist = lengthOfVector(vector);  // dist = 5
+
+function area({width, height}) {
+  return width * height;
+}
+const a = area(dims);  // a = 45000
+</pre>
+<h3 id="use-object-declaration-short-cuts">Utilisez les raccourcis de déclaration d'objet</h3>
 <p>ancien code</p>
-<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const width = dims.width;
- const height = dims.height;
-</pre><p>nouveau code</p>
-<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const {width, height} = dims;
-</pre><h3 id="utilisez-les-raccourcis-pour-la-d-claration-des-objets">Utilisez les raccourcis pour la déclaration des objets</h3>
-<p>ancien code :</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
  const height = 150;
  const obj = {
@@ -149,7 +177,7 @@ d'un objet :</p>
    },
  };
 </pre>
-<p>nouveau code :</p>
+<p>nouveau code</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
  const height = 150;
  const obj = {
@@ -160,52 +188,60 @@ d'un objet :</p>
    },
  };
 </pre>
-<h3 id="utilisez-l-op-rateur-d-expansion-">Utilisez l'opérateur d'expansion <code class="notranslate" translate="no">...</code></h3>
-<p>L'opérateur d'expansion a de multiples usages. Voici un exemple :</p>
+<h3 id="use-the-rest-parameter-and-the-spread-operator-">Utilisez le paramètre rest et l'opérateur spread <code class="notranslate" translate="no">...</code></h3>
+<p>Le paramètre rest peut être utilisé pour consommer un nombre quelconque de paramètres. Exemple</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function log(className, ...args) {
    const elem = document.createElement('div');
    elem.className = className;
-   elem.textContent = [...args].join(' ');
+   elem.textContent = args.join(' ');
    document.body.appendChild(elem);
  }
 </pre>
-<p>et un autre exemple :</p>
+<p>L'opérateur spread peut être utilisé pour étendre un itérable en arguments</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const position = [1, 2, 3];
-somemesh.position.set(...position);
+someMesh.position.set(...position);
+</pre>
+<p>ou copier un tableau</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const copiedPositionArray = [...position];
+copiedPositionArray.push(4); // [1,2,3,4]
+console.log(position); // [1,2,3] position n'est pas affectée
 </pre>
-<h3 id="utilisez-class-">Utilisez <code class="notranslate" translate="no">class</code></h3>
-<p>La syntaxe pour créer des objets au comportement de classe avant ES5
-n'était connue que des programmeurs chevronnés. Depuis ES5, vous pouvez
-à présent <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">utiliser le mot-clef <code class="notranslate" translate="no">class</code></a>
+<p>ou pour fusionner des objets</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">const a = {abc: 123};
+const b = {def: 456};
+const c = {...a, ...b};  // c est maintenant {abc: 123, def: 456}
+</pre><h3 id="use-class-">Utilisez <code class="notranslate" translate="no">class</code></h3>
+<p>La syntaxe pour créer des objets de type classe avant ES5 était peu familière à la plupart
+des programmeurs. À partir d'ES5, vous pouvez maintenant <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">utiliser le mot-clé <code class="notranslate" translate="no">class</code></a>
 qui est plus proche du style C++/C#/Java.</p>
-<h3 id="comprendre-les-accesseurs-getters-et-setters-">Comprendre les accesseurs (<em>getters</em> et <em>setters</em>)</h3>
-<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get">Getters</a> et
+<h3 id="understand-getters-and-setters">Comprendre les getters et setters</h3>
+<p>Les <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get">getters</a> et
 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set">setters</a> sont
-communs dans la plupart des langages modernes. La syntaxe de type <code class="notranslate" translate="no">class</code>
-de ES5 les rend plus faciles à employer qu'avant.</p>
-<h3 id="utilisez-les-fonctions-fl-ch-es-arrow-functions-quand-c-est-appropri-">Utilisez les fonctions fléchées (<em>arrow functions</em>) quand c'est approprié</h3>
-<p>Cela est particulièrement utile avec les fonctions de rappel (<em>callbacks</em>) et les promesses (<em>promises</em>).</p>
+courants dans la plupart des langages modernes. La syntaxe <code class="notranslate" translate="no">class</code>
+d'ES5 les rend beaucoup plus faciles qu'avant ES5.</p>
+<h3 id="use-arrow-functions-where-appropriate">Utilisez les fonctions fléchées (arrow functions) là où c'est approprié</h3>
+<p>C'est particulièrement utile avec les callbacks et les promises.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">loader.load((texture) =&gt; {
   // utiliser la texture
 });
 </pre>
-<p>Les fonctions fléchées se lient avec <code class="notranslate" translate="no">this</code>. Ainsi</p>
+<p>Les fonctions fléchées lient <code class="notranslate" translate="no">this</code> au contexte dans lequel vous créez la fonction fléchée.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (args) =&gt; {/* code */};
 </pre>
 <p>est un raccourci pour</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (function(args) {/* code */}).bind(this));
 </pre>
-<h3 id="de-l-utilisation-des-promesses-ainsi-que-de-async-await">De l'utilisation des promesses ainsi que de async/await</h3>
-<p>Les promesses (<em>promises</em>) aident à l'utilisation de code asynchrone.
-Async/await aident à l'utilisation des promesses.</p>
-<p>Cela nécessiterait des développements trop long à détailler ici.
-Toutefois, vous pouvez <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">en lire davantage sur les promesses ici</a>
+<p>Voir le lien ci-dessus pour plus d'informations sur <code class="notranslate" translate="no">this</code>.</p>
+<h3 id="promises-as-well-as-async-await">Promises ainsi que async/await</h3>
+<p>Les Promises aident avec le code asynchrone. Async/await aident à
+utiliser les promises.</p>
+<p>C'est un sujet trop vaste pour être abordé ici, mais vous pouvez <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">lire sur
+les promises ici</a>
 et sur <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async/await ici</a>.</p>
-<h3 id="utilisez-les-gabarits-de-libell-s-template-literals-">Utilisez les gabarits de libellés (<em>template Literals</em>)</h3>
-<p>Les gabarits de libellés sont des chaînes de caractères délimitées par
-des accents graves au lieu d'apostrophes doubles (<em>quotes</em>).</p>
+<h3 id="use-template-literals">Utilisez les littéraux de gabarit (Template Literals)</h3>
+<p>Les littéraux de gabarit sont des chaînes utilisant des accents graves (backticks) au lieu de guillemets.</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">const foo = `this is a template literal`;
-</pre><p>Les gabarits de libellés ont deux particularités. La première est d'être multi-ligne</p>
+</pre><p>Les littéraux de gabarit ont fondamentalement 2 fonctionnalités. La première est qu'ils peuvent être multi-lignes</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = `this
 is
 a
@@ -213,10 +249,9 @@ template
 literal`;
 const bar = "this\nis\na\ntemplate\nliteral";
 </pre>
-<p>ainsi <code class="notranslate" translate="no">foo</code> et <code class="notranslate" translate="no">bar</code> ci-dessus sont similaires.</p>
-<p>L'autre particularité est que vous pouvez sortir du mode chaîne et insérer des fragments
-de code Javascript en utilisant <code class="notranslate" translate="no">${javascript-expression}</code>.
-C'est l'objet des gabarits. Par exemple :</p>
+<p><code class="notranslate" translate="no">foo</code> et <code class="notranslate" translate="no">bar</code> ci-dessus sont identiques.</p>
+<p>L'autre est que vous pouvez sortir du mode chaîne et insérer des fragments de
+JavaScript en utilisant <code class="notranslate" translate="no">${javascript-expression}</code>. C'est la partie gabarit. Exemple :</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const r = 192;
 const g = 255;
 const b = 64;
@@ -231,68 +266,58 @@ const rgbCSSColor = `rgb(${color.join(',')})`;
 const bWidth = 20;
 someElement.style.width = `${aWidth + bWidth}px`;
 </pre>
-<h1 id="apprenez-les-conventions-de-codage-javascript">Apprenez les conventions de codage JavaScript</h1>
-<p>Alors que vous êtes libre de formater votre code comme vous le souhaitez, il
-y a au moins une convention dont vous devez avoir connaissance. Les variables,
-les noms de fonctions et de méthodes sont toutes en <em>lowerCasedCamelCase</em> (c'est
-à dire que les mots formant les noms des entités sont collés les uns aux autres et leur première lettre
-est en majuscule, les autres en minuscules à l'exception de la toute première lettre du nom qui
-est également en minuscule -- NDT).
-Les constructeurs, les noms des classes sont en <em>CapitalizedCamelCase</em> (
-les mots formant les noms des entités sont collés les uns aux autres et leur première lettre
-est en majuscule, les autres en minuscules -- NDT).
-Si vous suivez cette règle, votre code ressemblera à la plupart des autres
-écrits en JavaScript. Beaucoup de <a href="https://eslint.org">linters</a>, qui sont
-des programmes vérifiant les erreurs dans votre code,
-mettrons en évidence des erreurs si vous utiliser la mauvaise casse puisqu'en
-suivant la convention ci-dessus ils sauront que ces lignes ci-dessous sont
-mauvaises :</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const v = new vector(); // évidemment une erreur si toutes les classes commencent par une majuscule.
-const v = Vector();     // évidemment une erreur si toutes les fonctions commencent par une minuscule.
+<h1 id="learn-javascript-coding-conventions-">Apprenez les conventions de codage JavaScript.</h1>
+<p>Bien que vous soyez libre de formater votre code comme bon vous semble, il existe au moins une
+convention dont vous devriez être conscient. Les variables, noms de fonctions, noms de méthodes, en
+JavaScript sont tous en lowerCasedCamelCase. Les constructeurs, les noms de classes sont
+en CapitalizedCamelCase. Si vous suivez cette règle, votre code correspondra à la plupart des autres
+codes JavaScript. Beaucoup de <a href="https://eslint.org">linters</a>, des programmes qui vérifient les erreurs évidentes dans votre code,
+vous signaleront des erreurs si vous utilisez la mauvaise casse, car en suivant la convention
+ci-dessus, ils peuvent savoir quand vous utilisez quelque chose de manière incorrecte.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const v = new vector(); // clairement une erreur si toutes les classes commencent par une majuscule
+const v = Vector();     // clairement une erreur si toutes les fonctions commencent par une minuscule.
 </pre>
-<!-- JYD -->
-<h1 id="envisagez-l-utilisation-de-visual-studio-code">Envisagez l'utilisation de Visual Studio Code</h1>
-<p>Bien sûr, vous pouvez utiliser l'éditeur de votre choix mais, si vous ne l'avez pas
-encore essayé, envisagez d'utiliser <a href="https://code.visualstudio.com/">Visual Studio Code</a>
-pour JavaScript et après l'avoir installé, <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">intégrez-y eslint</a>.
-Cela vous prendra quelques minutes à installer mais vous aidera grandement pour
-trouver les bogues de votre JavaScript.</p>
+<h1 id="consider-using-visual-studio-code">Envisagez d'utiliser Visual Studio Code</h1>
+<p>Bien sûr, utilisez l'éditeur que vous voulez, mais si vous ne l'avez pas essayé, envisagez
+d'utiliser <a href="https://code.visualstudio.com/">Visual Studio Code</a> pour JavaScript et
+après l'avoir installé, <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">configurez
+eslint</a>.
+Cela pourrait prendre quelques minutes à configurer, mais cela vous aidera énormément à trouver
+les bugs dans votre JavaScript.</p>
 <p>Quelques exemples</p>
-<p>Si vous activez <a href="https://eslint.org/docs/rules/no-undef">la règle <code class="notranslate" translate="no">no-undef</code></a>
-alors VSCode via ESLint vous avertira de l'utilisation de nombreuses variables non définies.</p>
+<p>Si vous activez <a href="https://eslint.org/docs/rules/no-undef">la règle <code class="notranslate" translate="no">no-undef</code></a> alors
+VSCode via ESLint vous avertira de nombreuses variables non définies. </p>
 <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-defined.png"></div>
 
-<p>Ci-dessous vous pouvez voir que nous avons écrit <code class="notranslate" translate="no">doTheThing</code> à la place <code class="notranslate" translate="no">doThing</code>.
-<code class="notranslate" translate="no">doThing</code> se retrouve souligné en rouge et un passage au dessus me dira que
-c'est non défini. Une erreur est donc évitée.</p>
-<p>Vous aurez des avertissements (<em>warnings</em>) en utilisant <code class="notranslate" translate="no">THREE</code> donc ajoutez <code class="notranslate" translate="no">/* global THREE */</code>
-en haut de vos fichiers JavaScript pour notifier à eslint que <code class="notranslate" translate="no">THREE</code> existe.</p>
+<p>Ci-dessus, vous pouvez voir que j'ai mal orthographié <code class="notranslate" translate="no">doTheThing</code> en <code class="notranslate" translate="no">doThing</code>. Il y a un trait ondulé rouge
+sous <code class="notranslate" translate="no">doThing</code> et en survolant, il me dit qu'il n'est pas défini. Une erreur
+évitée.</p>
+<p>Si vous utilisez des balises <code class="notranslate" translate="no">&lt;script&gt;</code> pour inclure three.js, vous recevrez des avertissements en utilisant <code class="notranslate" translate="no">THREE</code>, alors ajoutez <code class="notranslate" translate="no">/* global THREE */</code> en haut de vos
+fichiers JavaScript pour dire à eslint que <code class="notranslate" translate="no">THREE</code> existe. (ou mieux, utilisez <code class="notranslate" translate="no">import</code> 😉)</p>
 <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-a-constructor.png"></div>
 
-<p>Ci-dessus, vous pouvez voir que eslint connaît la règle que les noms commençant par
-une majuscule <code class="notranslate" translate="no">UpperCaseNames</code> sont des constructeurs et vous devez donc utiliser <code class="notranslate" translate="no">new</code>.
-Une autre erreur évitée. C'est <a href="https://eslint.org/docs/rules/new-cap">la règle <code class="notranslate" translate="no">new-cap</code> rule</a>.</p>
-<p>Il y a <a href="https://eslint.org/docs/rules/">des centaines de règles que vous pouvez activer, désactiver ou personnaliser</a>.
-Par exemple, précédemment nous avons indiquer que nous devions utiliser <code class="notranslate" translate="no">const</code> et <code class="notranslate" translate="no">let</code> à la place de <code class="notranslate" translate="no">var</code>.</p>
-<p>Ici nous avons utilisé <code class="notranslate" translate="no">var</code> et nous avons été avertis que nous devions utiliser <code class="notranslate" translate="no">let</code> ou <code class="notranslate" translate="no">const</code></p>
+<p>Ci-dessus, vous pouvez voir qu'eslint connaît la règle selon laquelle les <code class="notranslate" translate="no">UpperCaseNames</code> sont des constructeurs
+et que vous devriez donc utiliser <code class="notranslate" translate="no">new</code>. Une autre erreur détectée et évitée. C'est <a href="https://eslint.org/docs/rules/new-cap">la
+règle <code class="notranslate" translate="no">new-cap</code></a>.</p>
+<p>Il existe <a href="https://eslint.org/docs/rules/">des centaines de règles que vous pouvez activer ou désactiver ou
+personnaliser</a>. Par exemple, j'ai mentionné ci-dessus que vous
+devriez utiliser <code class="notranslate" translate="no">const</code> et <code class="notranslate" translate="no">let</code> plutôt que <code class="notranslate" translate="no">var</code>.</p>
+<p>Ici, j'ai utilisé <code class="notranslate" translate="no">var</code> et il m'a averti que je devrais utiliser <code class="notranslate" translate="no">let</code> ou <code class="notranslate" translate="no">const</code></p>
 <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-var.png"></div>
 
-<p>Ici nous avons utilisé <code class="notranslate" translate="no">let</code> mais comme la valeur de la variable ne change jamais, nous
-nous voyons suggérer l'utilisation de <code class="notranslate" translate="no">const</code>.</p>
+<p>Ici, j'ai utilisé <code class="notranslate" translate="no">let</code>, mais il a vu que je ne changeais jamais la valeur, alors il a suggéré que j'utilise <code class="notranslate" translate="no">const</code>.</p>
 <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-let.png"></div>
 
-<p>Bien sûr, si vous préférez conserver <code class="notranslate" translate="no">var</code>, vous pouvez désactiver cette règle.
-Comme écrit plus haut, nous préférons privilégier <code class="notranslate" translate="no">const</code> et <code class="notranslate" translate="no">let</code> à la place de <code class="notranslate" translate="no">var</code>
-puisqu'ils sont plus efficaces et évitent les bogues.</p>
-<p>Pour les cas où vous avez vraiment besoin d'outrepasser une règle,
-<a href="https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments">vous pouvez ajouter un commentaire pour les désactiver</a>
+<p>Bien sûr, si vous préférez continuer à utiliser <code class="notranslate" translate="no">var</code>, vous pouvez simplement désactiver cette règle.
+Comme je l'ai dit ci-dessus, je préfère utiliser <code class="notranslate" translate="no">const</code> et <code class="notranslate" translate="no">let</code> plutôt que <code class="notranslate" translate="no">var</code> car ils
+fonctionnent mieux et préviennent les bugs.</p>
+<p>Dans les cas où vous avez vraiment besoin de outrepasser une règle, <a href="https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments">vous pouvez ajouter des commentaires pour les désactiver</a>
 pour une seule ligne ou une section de code.</p>
-<h1 id="si-vous-avez-vraiment-besoin-d-assurer-le-support-de-vieux-navigateurs-utilisez-un-transpileur">Si vous avez vraiment besoin d'assurer le support de vieux navigateurs, utilisez un transpileur</h1>
-<p>La plupart des navigateurs se mettent à jour automatiquement donc utiliser les subtilités
-vues plus haut vous aiderons à être productif et éviter les bogues.
-Ceci étant dit, si vous êtes dans un projet qui doit absolument supporter des
-vieux navigateurs, il y a des <a href="https://babeljs.io">outils qui interpréterons votre code ES5/ES6/ES7
-et le transpilent en code JavaScript pre-ES5</a>.</p>
+<h1 id="if-you-really-need-to-support-legacy-browsers-use-a-transpiler">Si vous avez vraiment besoin de prendre en charge les anciens navigateurs, utilisez un transpiler</h1>
+<p>La plupart des navigateurs modernes sont mis à jour automatiquement, donc l'utilisation de toutes ces fonctionnalités vous aidera à
+être productif et à éviter les bugs. Cela dit, si vous êtes sur un projet qui doit absolument
+prendre en charge les anciens navigateurs, il existe <a href="https://babeljs.io">des outils qui prendront votre code ES5/ES6/ES7
+et le transpileront vers du JavaScript pré-ES5</a>.</p>
 
         </div>
       </div>

+ 175 - 183
manual/fr/primitives.html

@@ -26,87 +26,74 @@
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là.</p>
-<p>Three.js a un grand nombre de primitives. Les primitives
-sont généralement des formes 3D qui sont générées à l'exécution
-avec un tas de paramètres.</p>
-<p>Il est courant d'utiliser des primitives des objets comme des sphères
-pour un globe ou un tas de boîtes pour dessiner un graphique en 3D.
-Il est particulièrement courant d'utiliser des primitives  pour faire
-des expériences et se lancer dans la 3D. Pour la majorité des
-applications 3D, il est courant de demander à un artiste de faire des modèles 3D
-dans un programme de modélisation 3D comme <a href="https://blender.org">Blender</a>,
-<a href="https://www.autodesk.com/products/maya/">Maya</a> ou <a href="https://www.maxon.net/en-us/products/cinema-4d/">Cinema 4D</a>.
-Plus tard dans cette série,
-nous aborderons la conception et le chargement de données provenant de
-plusieurs programme de modélisation 3D. Pour l'instant, passons
-en revue certaines primitives disponibles.</p>
-<p>La plupart des primitives ci-dessous ont des valeurs par défaut
-pour certain ou tous leurs paramètres. Vous pouvez donc les
-utiliser en fonction de vos besoins.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js.
+Le premier article traitait des <a href="fundamentals.html">notions fondamentales</a>.
+Si vous ne l'avez pas encore lu, vous pourriez vouloir commencer par là.</p>
+<p>Three.js dispose d'un grand nombre de primitives. Les primitives
+sont généralement des formes 3D qui sont générées au moment de l'exécution
+avec un ensemble de paramètres.</p>
+<p>Il est courant d'utiliser des primitives pour des choses comme une sphère
+pour un globe ou un ensemble de boîtes pour dessiner un graphique 3D. Il est
+particulièrement courant d'utiliser des primitives pour expérimenter
+et commencer avec la 3D. Pour la majorité des applications 3D,
+il est plus courant qu'un artiste crée des modèles 3D
+dans un programme de modélisation 3D comme <a href="https://blender.org">Blender</a>
+ou <a href="https://www.autodesk.com/products/maya/">Maya</a> ou <a href="https://www.maxon.net/en-us/products/cinema-4d/">Cinema 4D</a>. Plus tard dans cette série, nous aborderons
+la création et le chargement de données à partir de plusieurs programmes de modélisation
+3D. Pour l'instant, passons en revue quelques-unes des primitives disponibles.</p>
+<p>Beaucoup des primitives ci-dessous ont des valeurs par défaut pour tout ou partie de leurs
+paramètres, de sorte que vous pouvez les utiliser plus ou moins selon vos besoins.</p>
 <div id="Diagram-BoxGeometry" data-primitive="BoxGeometry">Une Boîte</div>
-<div id="Diagram-CircleGeometry" data-primitive="CircleGeometry">Un Cercle plat</div>
+<div id="Diagram-CircleGeometry" data-primitive="CircleGeometry">Un cercle plat</div>
 <div id="Diagram-ConeGeometry" data-primitive="ConeGeometry">Un Cône</div>
 <div id="Diagram-CylinderGeometry" data-primitive="CylinderGeometry">Un Cylindre</div>
-<div id="Diagram-DodecahedronGeometry" data-primitive="DodecahedronGeometry">Un Dodécaèdre (12 côtés)</div>
-<div id="Diagram-ExtrudeGeometry" data-primitive="ExtrudeGeometry">Une forme 2D extrudée avec un biseautage optionnel. Ici, nous extrudons une forme de cœur. Notez qu'il s'agit du principe de fonctionnement pour les <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a> et les <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a>.</div>
-<div id="Diagram-IcosahedronGeometry" data-primitive="IcosahedronGeometry">Un Icosaèdre (20 côtés)</div>
-<div id="Diagram-LatheGeometry" data-primitive="LatheGeometry">Une forme généré par la rotation d'une ligne pour, par exemple, dessiner une lampe, une quille, une bougie, un bougeoir, un verre à vin, etc. Vous fournissez une silhouette en deux dimensions comme une série de points et vous indiquez ensuite à Three.js combien de subdivisions sont nécessaires en faisant tourner la silhouette autour d'un axe.</div>
-<div id="Diagram-OctahedronGeometry" data-primitive="OctahedronGeometry">Un Octaèdre (8 côtés)</div>
-<div id="Diagram-ParametricGeometry" data-primitive="ParametricGeometry">Une surface générée en fournissant à la fonction un point 2D d'une grille et retourne le point 3D correspondant.</div>
+<div id="Diagram-DodecahedronGeometry" data-primitive="DodecahedronGeometry">Un dodécaèdre (12 faces)</div>
+<div id="Diagram-ExtrudeGeometry" data-primitive="ExtrudeGeometry">Une forme 2D extrudée avec biseautage optionnel.
+Ici, nous extrudons une forme de cœur. Notez que c'est la base
+de <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a>.</div>
+<div id="Diagram-IcosahedronGeometry" data-primitive="IcosahedronGeometry">Un icosaèdre (20 faces)</div>
+<div id="Diagram-LatheGeometry" data-primitive="LatheGeometry">Une forme générée en faisant tourner une ligne. Exemples : lampes, quilles de bowling, bougies, chandeliers, verres à vin, verres à boire, etc... Vous fournissez la silhouette 2D comme une série de points, puis vous indiquez à three.js combien de subdivisions créer en faisant tourner la silhouette autour d'un axe.</div>
+<div id="Diagram-OctahedronGeometry" data-primitive="OctahedronGeometry">Un Octaèdre (8 faces)</div>
+<div id="Diagram-ParametricGeometry" data-primitive="ParametricGeometry">Une surface générée en fournissant une fonction qui prend un point 2D d'une grille et renvoie le point 3D correspondant.</div>
 <div id="Diagram-PlaneGeometry" data-primitive="PlaneGeometry">Un plan 2D</div>
-<div id="Diagram-PolyhedronGeometry" data-primitive="PolyhedronGeometry">Prend un ensemble de triangles centrés autour d'un point et les projettes sur une sphère</div>
+<div id="Diagram-PolyhedronGeometry" data-primitive="PolyhedronGeometry">Prend un ensemble de triangles centrés autour d'un point et les projette sur une sphère</div>
 <div id="Diagram-RingGeometry" data-primitive="RingGeometry">Un disque 2D avec un trou au centre</div>
-<div id="Diagram-ShapeGeometry" data-primitive="ShapeGeometry">Un tracé 2D qui se triangule</div>
-<div id="Diagram-SphereGeometry" data-primitive="SphereGeometry">une sphère</div>
-<div id="Diagram-TetrahedronGeometry" data-primitive="TetrahedronGeometry">Un tétraèdre (4 côtés)</div>
+<div id="Diagram-ShapeGeometry" data-primitive="ShapeGeometry">Un contour 2D qui est triangulé</div>
+<div id="Diagram-SphereGeometry" data-primitive="SphereGeometry">Une sphère</div>
+<div id="Diagram-TetrahedronGeometry" data-primitive="TetrahedronGeometry">Un tétraèdre (4 faces)</div>
 <div id="Diagram-TextGeometry" data-primitive="TextGeometry">Texte 3D généré à partir d'une police 3D et d'une chaîne de caractères</div>
-<div id="Diagram-TorusGeometry" data-primitive="TorusGeometry">Un tore (donut)</div>
+<div id="Diagram-TorusGeometry" data-primitive="TorusGeometry">Un tore (beignet)</div>
 <div id="Diagram-TorusKnotGeometry" data-primitive="TorusKnotGeometry">Un nœud torique</div>
-<div id="Diagram-TubeGeometry" data-primitive="TubeGeometry">Extrusion contrôlée d'un cercle le long d'un tracé</div>
-<div id="Diagram-EdgesGeometry" data-primitive="EdgesGeometry">Un objet d'aide qui prend une autre
-géométrie en entrée et génère des arêtes que si l'angle entre les faces est supérieur à un certain
-seuil. Par exemple, si vous regardez en haut de la boîte, elle montre une ligne passant par chaque
-face et montrant chaque triangle qui forme la boîte. Si vous utilisez une
-<a href="/docs/#api/en/geometries/EdgesGeometry"><code class="notranslate" translate="no">EdgesGeometry</code></a> les lignes du milieu sont supprimées. Ajustez le "thresholdAngle"
-ci-dessous et vous verrez les arêtes en dessous de ce seuil disparate.</div>
-<div id="Diagram-WireframeGeometry" data-primitive="WireframeGeometry">Génère une géométrie qui
-contient un segment de droite (2 points) par arête dans la géométrie donnée. Sans cela, il vous
-manquerait souvent des arêtes ou vous obtiendriez des arêtes supplémentaires puisque WebGL exige
-généralement 2 points par segment de ligne. Par exemple, si vous n'aviez qu'un seul triangle, il
-n'y aurait que 3 points. Si vous essayez de le dessiner en utilisant un matériau avec
-<code class="notranslate" translate="no">wireframe: true</code> vous n'obtiendrez qu'une seule ligne. Si vous passez cette géométrie
-triangulaire à un <a href="/docs/#api/en/geometries/WireframeGeometry"><code class="notranslate" translate="no">WireframeGeometry</code></a> vous obtenez une nouvelle géométrie qui comporte
-3 segments de lignes utilisant 6 points.</div>
+<div id="Diagram-TubeGeometry" data-primitive="TubeGeometry">Un cercle tracé le long d'un chemin</div>
+<div id="Diagram-EdgesGeometry" data-primitive="EdgesGeometry">Un objet d'aide qui prend une autre géométrie en entrée et génère des arêtes seulement si l'angle entre les faces est supérieur à un certain seuil. Par exemple, si vous regardez la boîte en haut, elle montre une ligne traversant chaque face, montrant chaque triangle qui compose la boîte. En utilisant un <a href="/docs/#api/en/geometries/EdgesGeometry"><code class="notranslate" translate="no">EdgesGeometry</code></a> à la place, les lignes du milieu sont supprimées. Ajustez le seuil `thresholdAngle` ci-dessous et vous verrez les arêtes en dessous de ce seuil disparaître.</div>
+<div id="Diagram-WireframeGeometry" data-primitive="WireframeGeometry">Génère une géométrie qui contient un segment de ligne (2 points) par arête dans la géométrie donnée. Sans cela, il vous manquerait souvent des arêtes ou vous obtiendriez des arêtes supplémentaires car WebGL nécessite généralement 2 points par segment de ligne. Par exemple, si vous n'aviez qu'un seul triangle, il n'y aurait que 3 points. Si vous essayiez de le dessiner en utilisant un matériau avec <code class="notranslate" translate="no">wireframe: true</code>, vous n'obtiendriez qu'une seule ligne. Passer cette géométrie de triangle à un <a href="/docs/#api/en/geometries/WireframeGeometry"><code class="notranslate" translate="no">WireframeGeometry</code></a> générera une nouvelle géométrie qui a 3 segments de ligne utilisant 6 points.</div>
 
-<p>Nous reviendrons sur la création de géométrie personnalisée dans
-<a href="custom-buffergeometry.html">un autre article</a>. Pour l'instant,
-faisons un exemple en créant chaque type de primitive. Nous
-commencerons par les <a href="responsive.html">exemples vus dans l'article précédent</a>.</p>
-<p>Mais tout d'abord, définissons un couleur de fond :</p>
+<p>Nous aborderons la création de géométries personnalisées dans <a href="custom-buffergeometry.html">un autre article</a>. Pour l'instant,
+faisons un exemple créant chaque type de primitive. Nous commencerons
+avec les <a href="responsive.html">exemples de l'article précédent</a>.</p>
+<p>Près du haut, définissons une couleur de fond</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 +scene.background = new THREE.Color(0xAAAAAA);
 </pre>
-<p>Cela indique à three.js d'utiliser un fond gris clair.</p>
-<p>La caméra doit changer de position pour que nous puissions voir tous les objets.</p>
+<p>Cela indique à three.js d'effacer avec un gris clair.</p>
+<p>La caméra doit changer de position afin que nous puissions voir tous les
+objets.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const fov = 75;
-+const fov = 40; // champ de vue (field of view)
-const aspect = 2;
-const near = 0.1; // distance minimum
++const fov = 40;
+const aspect = 2;  // the canvas default
+const near = 0.1;
 -const far = 5;
-+const far = 1000; // distance maximum
++const far = 1000;
 const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 -camera.position.z = 2;
 +camera.position.z = 120;
 </pre>
-<p>Ajoutons une fonction, <code class="notranslate" translate="no">addObject</code>, qui prend une position x, y et un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>
-et ajoute l'objet à la scène.</p>
+<p>Ajoutons une fonction, <code class="notranslate" translate="no">addObject</code>, qui prend une position x, y et un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et ajoute
+l'objet à la scène.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const objects = [];
 const spread = 15;
 
-function addObject (x, y, obj) {
+function addObject(x, y, obj) {
   obj.position.x = x * spread;
   obj.position.y = y * spread;
 
@@ -114,24 +101,27 @@ function addObject (x, y, obj) {
   objects.push(obj);
 }
 </pre>
-<p>Faisons aussi une fonction pour créer un matériau coloré aléatoire.
-Nous utiliserons une fonction de <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">Color</code></a> qui vous permet de définir
-une couleur en fonction de la teinte, de la saturation et de la luminosité.</p>
-<p>La <code class="notranslate" translate="no">hue</code> (teinte) va de 0 à 1 autour de la roue des couleurs avec
-le rouge à 0, le vert à 0,33 et le bleu à 0,66. La <code class="notranslate" translate="no">saturation</code>
-va de 0 à 1, 0 n'ayant pas de couleur et 1 étant saturé. La <code class="notranslate" translate="no">luminance</code>
-(luminosité) va de 0 à 1, 0 étant le noir, 1 le blanc et 0,5 la
-quantité maximale de la couleur. En d'autres termes, lorsque la
-<code class="notranslate" translate="no">luminance</code> va de 0,0 à 0,5, la couleur passe du noir à <code class="notranslate" translate="no">hue</code> (la teinte).
-De 0,5 à 1,0 la couleur passe de <code class="notranslate" translate="no">hue</code> au blanc.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function createMaterial () {
+<p>Créons également une fonction pour créer un matériau de couleur aléatoire.
+Nous utiliserons une fonctionnalité de <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">Color</code></a>
+qui vous permet de définir une couleur
+basée sur la teinte, la saturation et la luminance.</p>
+<p><code class="notranslate" translate="no">hue</code> (teinte) va de 0 à 1 autour de la roue chromatique avec
+le rouge à 0, le vert à 0.33 et le bleu à 0.66. <code class="notranslate" translate="no">saturation</code>
+va de 0 à 1, 0 n'ayant pas de couleur et 1 étant
+la plus saturée. <code class="notranslate" translate="no">luminance</code> va de 0 à 1
+avec 0 étant le noir, 1 étant le blanc et 0.5 étant
+la quantité maximale de couleur. En d'autres termes,
+lorsque la <code class="notranslate" translate="no">luminance</code> passe de 0.0 à 0.5, la couleur
+passe du noir à la <code class="notranslate" translate="no">hue</code> (teinte). De 0.5 à 1.0,
+la couleur passe de la <code class="notranslate" translate="no">hue</code> (teinte) au blanc.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function createMaterial() {
   const material = new THREE.MeshPhongMaterial({
     side: THREE.DoubleSide,
   });
 
-  const hue = Math.random(); // teinte
+  const hue = Math.random();
   const saturation = 1;
-  const luminance = .5; // luminosité
+  const luminance = .5;
   material.color.setHSL(hue, saturation, luminance);
 
   return material;
@@ -139,53 +129,54 @@ De 0,5 à 1,0 la couleur passe de <code class="notranslate" translate="no">hue</
 </pre>
 <p>Nous avons également passé <code class="notranslate" translate="no">side: THREE.DoubleSide</code> au matériau.
 Cela indique à three de dessiner les deux côtés des triangles
-qui constituent une forme. Pour un solide comme une sphère
-ou un cube, il n'y a généralement pas de raison de dessiner les
-côtés arrières des triangles car ils sont tous tournés ver l'intérieur
-de la forme. Dans notre cas, cependant, nous dessinons des objets
-comme la <a href="/docs/#api/en/geometries/PlaneGeometry"><code class="notranslate" translate="no">PlaneGeometry</code></a> ou la <a href="/docs/#api/en/geometries/ShapeGeometry"><code class="notranslate" translate="no">ShapeGeometry</code></a>
-qui sont bidimensionnnels et n'ont donc pas d'intérieur.
-Sans le paramètre <code class="notranslate" translate="no">side: THREE.DoubleSide</code> ils disparaîtraient
-quand on regarderait leur dos.</p>
-<p>Notons qu'il est plus rapide de dessiner quand on ne met <strong>pas</strong>
-<code class="notranslate" translate="no">side: THREE.DoubleSide</code>, donc l'idéal serait de ne le mettre que sur
-les matériaux qui en ont vraiment besoin, mais pour cet exemple, nous
-dessinons peu d'objets, donc il n'y a pas de raisons de s'en inquiéter.</p>
-<p>Faisons une fonction, <code class="notranslate" translate="no">addSolidGeometry</code>, qui
-reçoit une géométrie et crée un matériau coloré
-aléatoire via <code class="notranslate" translate="no">createMaterial</code> et l'ajoute à la
-scène via <code class="notranslate" translate="no">addObject</code>.</p>
+qui composent une forme. Pour une forme solide comme une sphère
+ou un cube, il n'y a généralement aucune raison de dessiner les
+côtés arrière des triangles car ils font tous face à l'intérieur de la
+forme. Dans notre cas cependant, nous dessinons quelques éléments
+comme le <a href="/docs/#api/en/geometries/PlaneGeometry"><code class="notranslate" translate="no">PlaneGeometry</code></a> et le <a href="/docs/#api/en/geometries/ShapeGeometry"><code class="notranslate" translate="no">ShapeGeometry</code></a>
+qui sont bidimensionnels et n'ont donc pas d'intérieur. Sans
+définir <code class="notranslate" translate="no">side: THREE.DoubleSide</code>, ils disparaîtraient
+en regardant leurs côtés arrière.</p>
+<p>Je dois noter qu'il est plus rapide de dessiner lorsque l'on ne définit <strong>pas</strong>
+<code class="notranslate" translate="no">side: THREE.DoubleSide</code>, donc idéalement nous ne le définirions que sur
+les matériaux qui en ont vraiment besoin, mais dans ce cas, nous
+ne dessinons pas trop, donc il n'y a pas beaucoup de raison de
+s'en soucier.</p>
+<p>Créons une fonction, <code class="notranslate" translate="no">addSolidGeometry</code>, à laquelle
+nous passons une géométrie, et elle crée un matériau de couleur aléatoire
+via <code class="notranslate" translate="no">createMaterial</code> et l'ajoute à la scène
+via <code class="notranslate" translate="no">addObject</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addSolidGeometry(x, y, geometry) {
   const mesh = new THREE.Mesh(geometry, createMaterial());
   addObject(x, y, mesh);
 }
 </pre>
-<p>Nous pouvons maintenant l'utiliser pour la majorité des primitives que nous créons.
-Par exemple, la création d'une boîte :</p>
+<p>Maintenant, nous pouvons l'utiliser pour la majorité des primitives que nous créons.
+Par exemple, pour créer une boîte</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
-  const width = 8; // largeur
-  const height = 8; // hauteur
-  const depth = 8; // profondeur
+  const width = 8;
+  const height = 8;
+  const depth = 8;
   addSolidGeometry(-2, -2, new THREE.BoxGeometry(width, height, depth));
 }
 </pre>
-<p>Si vous regardez dans le code ci-dessous, vous verrez une section similaire pour chaque type de géométrie.</p>
+<p>Si vous regardez le code ci-dessous, vous verrez une section similaire pour chaque type de géométrie.</p>
 <p>Voici le résultat :</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/primitives.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/primitives.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/primitives.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
 <p>Il y a quelques exceptions notables au modèle ci-dessus.
-La plus grande est probablement le <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a>. Il doit charger
-des données de police en 3D avant de pouvoir générer un maillage pour le texte.
-Ces données se chargent de manière asynchrone, nous devons donc attendre
-qu'elles soient chargées avant d'essayer de créer la géométrie. En "promettant"
-le chargement des polices, nous pouvons faciliter la tâche.
-Une créons un <a href="/docs/#api/en/loaders/FontLoader"><code class="notranslate" translate="no">FontLoader</code></a> et une fonction <code class="notranslate" translate="no">loadFont</code> qui retourne
-une promesse, qui une fois résolue, nous donnera la police. Nous créons
-une fonction <code class="notranslate" translate="no">async</code> appelée <code class="notranslate" translate="no">doit</code> (fais le) et chargeons la police en utilisant <code class="notranslate" translate="no">await</code> (attends).
+La plus importante est probablement la <a href="/docs/#api/en/geometries/TextGeometry"><code class="notranslate" translate="no">TextGeometry</code></a>. Elle nécessite de charger
+les données de police 3D avant de pouvoir générer un maillage pour le texte.
+Ces données se chargent de manière asynchrone, nous devons donc attendre qu'elles
+soient chargées avant d'essayer de créer la géométrie. En "promisifiant"
+le chargement de la police, nous pouvons rendre les choses beaucoup plus faciles.
+Nous créons un <a href="/docs/#api/en/loaders/FontLoader"><code class="notranslate" translate="no">FontLoader</code></a>, puis une fonction <code class="notranslate" translate="no">loadFont</code> qui renvoie
+une promesse qui, une fois résolue, nous donnera la police. Nous créons ensuite
+une fonction <code class="notranslate" translate="no">async</code> appelée <code class="notranslate" translate="no">doit</code> et chargeons la police en utilisant <code class="notranslate" translate="no">await</code>.
 Et enfin, nous créons la géométrie et appelons <code class="notranslate" translate="no">addObject</code> pour l'ajouter à la scène.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const loader = new FontLoader();
@@ -197,7 +188,7 @@ Et enfin, nous créons la géométrie et appelons <code class="notranslate" tran
   }
 
   async function doit() {
-    const font = await loadFont('../resources/threejs/fonts/helvetiker_regular.typeface.json');  /* threejs.org: url */
+    const font = await loadFont('resources/threejs/fonts/helvetiker_regular.typeface.json');  /* threejs.org : URL */
     const geometry = new TextGeometry('three.js', {
       font: font,
       size: 3.0,
@@ -221,66 +212,62 @@ Et enfin, nous créons la géométrie et appelons <code class="notranslate" tran
 }
 </pre>
 <p>Il y a une autre différence. Nous voulons faire tourner le texte autour de son
-centre, mais par défaut three.js crée le texte de tel sorte que son centre de rotation
-se trouve sur le bord gauche. Pour contourner ce problème, nous pouvons demander à
-three.js de calculer une boite englobant la géométrie.
-Nous pouvons alors appeler la méthode <code class="notranslate" translate="no">getCenter</code> de cette boite
-et lui passer la position du maillage de notre objet. La méthode
-<code class="notranslate" translate="no">getCenter</code> copie le centre de la boite dans la position.
-Elle renvoie également l'objet position afin que nous puissions appeler
-<code class="notranslate" translate="no">multiplyScalar(-1)</code> pour positionner l'objet entier de tel sorte que son
-centre de rotation soit positionné au centre de l'objet.</p>
-<p>Si nous appelons juste <code class="notranslate" translate="no">addSolidGeometry</code> comme dans les
-exemples précédents, il s'établirait une position
-qui ne serait pas correcte. Donc, dans ce cas, nous créons un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>
-qui est un nœud standard pour les scènes three.js. <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>
-hérite également de <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> (confère l'article
-<a href="scenegraph.html">comment un graphe de scène fonctionne</a>).
+centre, mais par défaut, three.js crée le texte de manière à ce que son centre de rotation
+soit sur le bord gauche. Pour contourner ce problème, nous pouvons demander à three.js de calculer la
+boîte englobante (bounding box) de la géométrie. Nous pouvons ensuite appeler la méthode <code class="notranslate" translate="no">getCenter</code>
+de la boîte englobante et lui passer l'objet position de notre maillage.
+<code class="notranslate" translate="no">getCenter</code> copie le centre de la boîte dans la position.
+Elle renvoie également l'objet position afin que nous puissions appeler <code class="notranslate" translate="no">multiplyScalar(-1)</code>
+pour positionner l'objet entier de sorte que son centre de rotation
+soit au centre de l'objet.</p>
+<p>Si nous appelions simplement <code class="notranslate" translate="no">addSolidGeometry</code> comme avec les exemples précédents,
+cela redéfinirait la position, ce qui n'est pas bon.
+Donc, dans ce cas, nous créons un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> qui
+est le nœud standard pour le graphe de scène de three.js. <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>
+est également hérité de <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Nous aborderons <a href="scenegraph.html">le fonctionnement du graphe de scène
+dans un autre article</a>.
 Pour l'instant, il suffit de savoir que,
-comme les nœuds DOM, les enfants sont placés de façon relative par rapport à leur parent.
-En créant un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et en faisant de notre maillage (mesh) un
-enfant de celui-ci nous pouvons positionner l'<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> où nous
-voulons tout en conservant le décalage central que nous avons
-fixé précédemment.</p>
-<p>Si nous ne faisions pas cela, le texte serait alors décentré !</p>
+comme les nœuds DOM, les enfants sont dessinés par rapport à leur parent.
+En créant un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et en faisant de notre maillage un enfant de celui-ci,
+nous pouvons positionner l'<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> où nous voulons tout en
+conservant le décalage central que nous avons défini précédemment.</p>
+<p>Si nous ne faisions pas cela, le texte tournerait de manière décentrée.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/primitives-text.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/primitives-text.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/primitives-text.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Notons que celui de gauche ne tourne pas autour de son centre
-alors que celui de droite le fait.</p>
-<p>Les autres exceptions sont les exemples de 2 lignes pour la <a href="/docs/#api/en/geometries/EdgesGeometry"><code class="notranslate" translate="no">EdgesGeometry</code></a>
-et la <a href="/docs/#api/en/geometries/WireframeGeometry"><code class="notranslate" translate="no">WireframeGeometry</code></a>. Au lieu d'appeler <code class="notranslate" translate="no">addSolidGeometry</code> ils appellent
-<code class="notranslate" translate="no">addLineGeometry</code> dont le code ressemble à :</p>
+<p>Notez que celui de gauche ne tourne pas autour de son centre
+tandis que celui de droite le fait.</p>
+<p>Les autres exceptions sont les 2 exemples basés sur des lignes pour <a href="/docs/#api/en/geometries/EdgesGeometry"><code class="notranslate" translate="no">EdgesGeometry</code></a>
+et <a href="/docs/#api/en/geometries/WireframeGeometry"><code class="notranslate" translate="no">WireframeGeometry</code></a>. Au lieu d'appeler <code class="notranslate" translate="no">addSolidGeometry</code>, elles appellent
+<code class="notranslate" translate="no">addLineGeometry</code> qui ressemble à ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addLineGeometry(x, y, geometry) {
   const material = new THREE.LineBasicMaterial({color: 0x000000});
   const mesh = new THREE.LineSegments(geometry, material);
   addObject(x, y, mesh);
 }
 </pre>
-<p>Cette fonction crée un <a href="/docs/#api/en/materials/LineBasicMaterial"><code class="notranslate" translate="no">LineBasicMaterial</code></a> noir et crée ensuite un objet <a href="/docs/#api/en/objects/LineSegments"><code class="notranslate" translate="no">LineSegments</code></a>
-qui est enveloppé par le <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> qui permet à three de savoir que vous
-affichez des segments de droite (2 points par segment).</p>
-<p>Chacune des primitives a plusieurs paramètres que vous pouvez passer à la création
-et il est préférable que vous <a href="https://threejs.org/docs/">regardez la documentation</a>
-de tous ces paramètres par vous même plutôt que de la répéter dans ce document.
-Vous pouvez également cliquer sur les liens ci-dessus à côté de chaque
-forme pour accéder directement à la documentation correspondante.</p>
-<p>Il y a une paire de classe qui ne correspond pas vraiment aux modèles ci-dessus. Il s'agit des
-classes <a href="/docs/#api/en/materials/PointsMaterial"><code class="notranslate" translate="no">PointsMaterial</code></a> et  <a href="/docs/#api/en/objects/Points"><code class="notranslate" translate="no">Points</code></a>. Les <a href="/docs/#api/en/objects/Points"><code class="notranslate" translate="no">Points</code></a> sont comme les <a href="/docs/#api/en/objects/LineSegments"><code class="notranslate" translate="no">LineSegments</code></a> ci-dessus en
-ce sens qu'ils prennent une <code class="notranslate" translate="no">Geometry</code> ou une <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> mais dessinent des points à chaque
-sommet au lieu de lignes.
+<p>Elle crée un <a href="/docs/#api/en/materials/LineBasicMaterial"><code class="notranslate" translate="no">LineBasicMaterial</code></a> noir et crée ensuite un objet <a href="/docs/#api/en/objects/LineSegments"><code class="notranslate" translate="no">LineSegments</code></a>
+qui est un wrapper pour <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> et aide three à savoir que vous rendez
+des segments de ligne (2 points par segment).</p>
+<p>Chacune des primitives possède plusieurs paramètres que vous pouvez passer lors de sa création
+et il est préférable de <a href="https://threejs.org/docs/">consulter la documentation</a> pour les voir tous plutôt que
+de les répéter ici. Vous pouvez également cliquer sur les liens ci-dessus à côté de chaque forme
+pour accéder directement à la documentation de cette forme.</p>
+<p>Il existe une autre paire de classes qui ne correspondent pas vraiment aux modèles ci-dessus. Ce sont
+les classes <a href="/docs/#api/en/materials/PointsMaterial"><code class="notranslate" translate="no">PointsMaterial</code></a> et <a href="/docs/#api/en/objects/Points"><code class="notranslate" translate="no">Points</code></a>. <a href="/docs/#api/en/objects/Points"><code class="notranslate" translate="no">Points</code></a> est similaire à <a href="/docs/#api/en/objects/LineSegments"><code class="notranslate" translate="no">LineSegments</code></a> ci-dessus en ce sens qu'elle prend une
+<a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> mais dessine des points à chaque sommet au lieu de lignes.
 Pour l'utiliser, vous devez également lui passer un <a href="/docs/#api/en/materials/PointsMaterial"><code class="notranslate" translate="no">PointsMaterial</code></a> qui
-prend une taille (<a href="/docs/#api/en/materials/PointsMaterial#size"><code class="notranslate" translate="no">size</code></a>) pour la grosseur des points.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const radius = 7; // rayon
+prend un paramètre <a href="/docs/#api/en/materials/PointsMaterial#size"><code class="notranslate" translate="no">size</code></a> pour définir la taille des points.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const radius = 7;
 const widthSegments = 12;
 const heightSegments = 8;
 const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
 const material = new THREE.PointsMaterial({
     color: 'red',
-    size: 0.2, // en unités du monde
+    size: 0.2,     // in world units
 });
 const points = new THREE.Points(geometry, material);
 scene.add(points);
@@ -289,14 +276,13 @@ scene.add(points);
 <div data-diagram="Points"></div>
 </div>
 
-<p>Vous pouvez désactiver l'option <a href="/docs/#api/en/materials/PointsMaterial#sizeAttenuation"><code class="notranslate" translate="no">sizeAttenuation</code></a> en la réglant
-sur "false" si vous souhaitez que les points soient de la même taille quelle que soit leur
-distance par rapport à la caméra.</p>
+<p>Vous pouvez désactiver <a href="/docs/#api/en/materials/PointsMaterial#sizeAttenuation"><code class="notranslate" translate="no">sizeAttenuation</code></a> en le définissant à false si vous souhaitez que les points
+aient la même taille quelle que soit leur distance par rapport à la caméra.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.PointsMaterial({
     color: 'red',
 +    sizeAttenuation: false,
-+    size: 3, // en pixels
--    size: 0.2, // en unités du monde
++    size: 3,       // in pixels
+-    size: 0.2,     // in world units
 });
 ...
 </pre>
@@ -304,57 +290,63 @@ distance par rapport à la caméra.</p>
 <div data-diagram="PointsUniformSize"></div>
 </div>
 
-<p>Une autre chose qu'il est important de souligner : c'est que presque toutes les formes ont des
-réglages différents concernant leur subdivisions. Un bon exemple pourrait être les
-géométries des sphères prenant en paramètres le nombre de divisions à faire autour et de
-haut en bas. Par exemple :</p>
+<p>Une autre chose importante à aborder est que presque toutes les formes
+ont divers paramètres pour déterminer combien les subdiviser. Un bon exemple
+pourrait être les géométries de sphères. Les sphères prennent des paramètres pour
+le nombre de divisions à faire autour et de haut en bas. Par exemple</p>
 <div class="spread">
 <div data-diagram="SphereGeometryLow"></div>
 <div data-diagram="SphereGeometryMedium"></div>
 <div data-diagram="SphereGeometryHigh"></div>
 </div>
 
-<p>La première sphère a un tour de 5 segments et 3 de haut, soit 15 segments ou 30 triangles.
-La deuxième sphère a 24 segments sur 10. cela fait 240 segments ou 480 triangles. Le dernier a
-50 par 50, soir 2500 segments ou 5000 triangles.</p>
-<p>C'est à vous de décider du nombre de subdivisions dont vous avez besoin. Il peut sembler que vous
-ayez besoin d'un grand nombre de segments, mais si vous enlevez les lignes et les ombres plates,
-nous obtenons ceci :</p>
+<p>La première sphère a 5 segments autour et 3 en hauteur, soit 15 segments
+ou 30 triangles. La deuxième sphère a 24 segments sur 10, soit 240 segments
+ou 480 triangles. La dernière a 50 sur 50, soit 2500 segments ou 5000 triangles.</p>
+<p>C'est à vous de décider du nombre de subdivisions dont vous avez besoin. Il peut
+sembler que vous ayez besoin d'un grand nombre de segments, mais supprimez les lignes
+et l'ombrage plat, et nous obtenons ceci</p>
 <div class="spread">
 <div data-diagram="SphereGeometryLowSmooth"></div>
 <div data-diagram="SphereGeometryMediumSmooth"></div>
 <div data-diagram="SphereGeometryHighSmooth"></div>
 </div>
 
-<p>Il est moins perceptible que celle de droite avec 5000 triangles est meilleure que celle avec
-seulement 480 triangles. Si vous ne dessinez que quelques sphères, comme par exemple, un seul
-globe pour une carte de la terre, alors une sphère de 10 000 triangles n'est pas un mauvais choix.
-Si, par contre, vous essayez de dessiner 1000 sphères alors 1000 sphères multipliées par 10000
-triangles représentent chacune 10 millions de triangles. Pour que l'animation soit fluide,
-il faut que le navigateur dessine à 60 images par seconde pour que vous demandiez au navigateur
-de dessiner 600 millions de triangles par seconde. Cela fait beaucoup trop de calcul.</p>
-<p>Parfois, il est facile de choisir. Par exemple, vous pouvez aussi choisir
+<p>Il n'est maintenant plus si clair que celle de droite avec 5000 triangles
+soit entièrement meilleure que celle du milieu avec seulement 480.</p>
+<p>Si vous ne dessinez que quelques sphères, comme par exemple un seul globe pour
+une carte de la terre, alors une seule sphère de 10000 triangles n'est pas un mauvais
+choix. Si par contre vous essayez de dessiner 1000 sphères, alors
+1000 sphères multipliées par 10000 triangles chacune donnent 10 millions de triangles.
+Pour animer fluidement, vous avez besoin que le navigateur dessine à 60 images par
+seconde, donc vous demanderiez au navigateur de dessiner 600 millions de triangles
+par seconde. C'est beaucoup de calcul.</p>
+<p>Parfois, il est facile de choisir. Par exemple, vous pouvez également choisir
 de subdiviser un plan.</p>
 <div class="spread">
 <div data-diagram="PlaneGeometryLow"></div>
 <div data-diagram="PlaneGeometryHigh"></div>
 </div>
 
-<p>Le plan à gauche est composé de 2 triangles. Le plan de droite est composé de 200 triangles.
-Contrairement à la sphère, il n'y a pas vraiment de compromis sur la qualité pour la plupart des
-cas d'utilisation d'un plan. Vous ne subdiviserez probablement un plan que si vous vous attendez
-à vouloir le modifier ou le déformer d'une manière ou d'une autre. Idem pour une boîte.</p>
-<p>Choisissez donc ce qui convient le mieux à votre situation. Moins vous choisirez de subdivisions,
-plus les choses auront des chances de se dérouler sans heurts et moins il vous faudra de mémoire.
-Vous devrez décider vous-même du compromis qui convient le mieux à cas d'utilisation.</p>
-<p>Si aucune des formes ci-dessus ne correspond à votre cas d'utilisation, vous pouvez
-charger la géométrie par exemple à partir d'un <a href="load-obj.html">fichier .obj</a>
+<p>Le plan de gauche est composé de 2 triangles. Le plan de droite
+est composé de 200 triangles. Contrairement à la sphère, il n'y a vraiment aucun compromis sur la qualité pour la plupart
+des cas d'utilisation d'un plan. Vous ne subdiviseriez très probablement un plan
+que si vous vous attendiez à vouloir le modifier ou le déformer d'une manière ou d'une autre. Une boîte
+est similaire.</p>
+<p>Alors, choisissez ce qui convient le mieux à votre situation. Moins
+vous choisissez de subdivisions, plus il est probable que les choses fonctionneront fluidement et moins
+elles consommeront de mémoire. Vous devrez décider vous-même quel est le bon
+compromis pour votre situation particulière.</p>
+<p>Si aucune des formes ci-dessus ne correspond à votre cas d'utilisation, vous pouvez charger
+une géométrie, par exemple à partir d'un <a href="load-obj.html">fichier .obj</a>
 ou d'un <a href="load-gltf.html">fichier .gltf</a>.
-Vous pouvez également créer votre <a href="custom-buffergeometry.html">BufferGeometry</a>.</p>
-<p>Voyons maintenant l'article traitant sur <a href="scenegraph.html">comment fonctionne un graphe de scène three.js et comment l'utiliser</a>.</p>
+Vous pouvez également créer votre propre <a href="custom-buffergeometry.html">BufferGeometry personnalisée</a>.</p>
+<p>Ensuite, passons en revue <a href="scenegraph.html">le fonctionnement du graphe de scène de three et comment
+l'utiliser</a>.</p>
 <p><link rel="stylesheet" href="../resources/threejs-primitives.css"></p>
 <script type="module" src="../resources/threejs-primitives.js"></script>
 
+
         </div>
       </div>
     </div>
@@ -365,4 +357,4 @@ Vous pouvez également créer votre <a href="custom-buffergeometry.html">BufferG
 
 
 
-</body></html>
+</body></html>

+ 158 - 4
manual/fr/rendering-on-demand.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Rendering on Demand</title>
+    <title>Rendu à la demande</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,12 +22,166 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Rendering on Demand</h1>
+        <h1>Rendu à la demande</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/rendering-on-demand.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Le sujet peut sembler évident pour beaucoup, mais au cas où... la plupart des exemples Three.js rendent en continu. En d'autres termes, ils mettent en place une boucle <code class="notranslate" translate="no">requestAnimationFrame</code> ou "<em>boucle rAF</em>" comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
+  ...
+  requestAnimationFrame(render);
+}
+requestAnimationFrame(render);
+</pre>
+<p>Pour quelque chose qui s'anime, cela a du sens, mais qu'en est-il de quelque chose qui ne s'anime pas ? Dans ce cas, rendre en continu est un gaspillage de la puissance de l'appareil et si l'utilisateur est sur un appareil portable, cela gaspille la batterie de l'utilisateur. </p>
+<p>La façon la plus évidente de résoudre ce problème est de rendre une fois au début, puis de ne rendre que lorsque quelque chose change. Les changements incluent le chargement final des textures ou des modèles, l'arrivée de données depuis une source externe, l'ajustement d'un paramètre par l'utilisateur, le changement de caméra ou d'autres entrées pertinentes.</p>
+<p>Prenons un exemple de <a href="responsive.html">l'article sur la réactivité</a> et modifions-le pour qu'il rende à la demande.</p>
+<p>D'abord, nous allons ajouter les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> afin qu'il y ait quelque chose qui puisse changer et auquel nous puissions réagir en rendant.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
++import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
+</pre>
+<p>et les configurer</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
+const aspect = 2;  // the canvas default
+const near = 0.1;
+const far = 5;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+camera.position.z = 2;
+
++const controls = new OrbitControls(camera, canvas);
++controls.target.set(0, 0, 0);
++controls.update();
+</pre>
+<p>Puisque nous n'animerons plus les cubes, nous n'avons plus besoin de les suivre</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const cubes = [
+-  makeInstance(geometry, 0x44aa88,  0),
+-  makeInstance(geometry, 0x8844aa, -2),
+-  makeInstance(geometry, 0xaa8844,  2),
+-];
++makeInstance(geometry, 0x44aa88,  0);
++makeInstance(geometry, 0x8844aa, -2);
++makeInstance(geometry, 0xaa8844,  2);
+</pre>
+<p>Nous pouvons supprimer le code d'animation des cubes et les appels à <code class="notranslate" translate="no">requestAnimationFrame</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function render(time) {
+-  time *= 0.001;
++function render() {
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+-  cubes.forEach((cube, ndx) =&gt; {
+-    const speed = 1 + ndx * .1;
+-    const rot = time * speed;
+-    cube.rotation.x = rot;
+-    cube.rotation.y = rot;
+  });
+
+  renderer.render(scene, camera);
+
+-  requestAnimationFrame(render);
+}
+
+-requestAnimationFrame(render);
+</pre>
+<p>puis nous devons rendre une fois</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">render();
+</pre>
+<p>Nous devons rendre chaque fois que les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> modifient les paramètres de la caméra. Heureusement, les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> déclenchent un événement <code class="notranslate" translate="no">change</code> chaque fois que quelque chose change.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">controls.addEventListener('change', render);
+</pre>
+<p>Nous devons également gérer le cas où l'utilisateur redimensionne la fenêtre. C'était géré automatiquement auparavant puisque nous rendions en continu, mais maintenant que nous ne le faisons plus, nous devons rendre lorsque la taille de la fenêtre change.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">window.addEventListener('resize', render);
+</pre>
+<p>Et avec cela, nous obtenons quelque chose qui rend à la demande.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/render-on-demand.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/render-on-demand.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> ont des options pour ajouter une sorte d'inertie afin de les rendre moins rigides. Nous pouvons l'activer en définissant la propriété <code class="notranslate" translate="no">enableDamping</code> sur true.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">controls.enableDamping = true;
+</pre>
+<p>Avec <code class="notranslate" translate="no">enableDamping</code> activé, nous devons appeler <code class="notranslate" translate="no">controls.update</code> dans notre fonction de rendu afin que les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> puissent continuer à nous fournir de nouveaux paramètres de caméra pendant qu'ils lissent le mouvement. Mais cela signifie que nous ne pouvons pas appeler <code class="notranslate" translate="no">render</code> directement depuis l'événement <code class="notranslate" translate="no">change</code> car nous nous retrouverions dans une boucle infinie. Les contrôles nous enverraient un événement <code class="notranslate" translate="no">change</code> et appelleraient <code class="notranslate" translate="no">render</code>, <code class="notranslate" translate="no">render</code> appellerait <code class="notranslate" translate="no">controls.update</code>. <code class="notranslate" translate="no">controls.update</code> enverrait un autre événement <code class="notranslate" translate="no">change</code>.</p>
+<p>Nous pouvons résoudre cela en utilisant <code class="notranslate" translate="no">requestAnimationFrame</code> pour appeler <code class="notranslate" translate="no">render</code>, mais nous devons nous assurer de ne demander une nouvelle image que si une n'a pas déjà été demandée, ce que nous pouvons faire en conservant une variable qui suit si nous avons déjà demandé une image.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+let renderRequested = false;
+
+function render() {
++  renderRequested = false;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+  renderer.render(scene, camera);
+}
+render();
+
++function requestRenderIfNotRequested() {
++  if (!renderRequested) {
++    renderRequested = true;
++    requestAnimationFrame(render);
++  }
++}
+
+-controls.addEventListener('change', render);
++controls.addEventListener('change', requestRenderIfNotRequested);
+</pre>
+<p>Nous devrions probablement aussi utiliser <code class="notranslate" translate="no">requestRenderIfNotRequested</code> pour le redimensionnement également</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-window.addEventListener('resize', render);
++window.addEventListener('resize', requestRenderIfNotRequested);
+</pre>
+<p>Il peut être difficile de voir la différence. Essayez de cliquer sur l'exemple ci-dessous et utilisez les touches fléchées pour vous déplacer ou faites glisser pour faire tourner. Ensuite, essayez de cliquer sur l'exemple ci-dessus et faites la même chose, et vous devriez pouvoir faire la différence. Celui d'en haut s'accroche lorsque vous appuyez sur une touche fléchée ou faites glisser, celui d'en bas glisse.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/render-on-demand-w-damping.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/render-on-demand-w-damping.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Ajoutons également une simple GUI lil-gui et faisons en sorte que ses modifications rendent à la demande.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
++import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
+</pre>
+<p>Permettons de définir la couleur et l'échelle x de chaque cube. Pour pouvoir définir la couleur, nous utiliserons le <code class="notranslate" translate="no">ColorGUIHelper</code> que nous avons créé dans <a href="lights.html">l'article sur les lumières</a>.</p>
+<p>Tout d'abord, nous devons créer une GUI</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
+</pre>
+<p>puis pour chaque cube, nous créerons un dossier et ajouterons 2 contrôles, un pour <code class="notranslate" translate="no">material.color</code> et un autre pour <code class="notranslate" translate="no">cube.scale.x</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
+  const material = new THREE.MeshPhongMaterial({color});
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  cube.position.x = x;
+
++  const folder = gui.addFolder(`Cube${x}`);
++  folder.addColor(new ColorGUIHelper(material, 'color'), 'value')
++      .name('color')
++      .onChange(requestRenderIfNotRequested);
++  folder.add(cube.scale, 'x', .1, 1.5)
++      .name('scale x')
++      .onChange(requestRenderIfNotRequested);
++  folder.open();
+
+  return cube;
+}
+</pre>
+<p>Vous pouvez voir ci-dessus que les contrôles lil-gui ont une méthode <code class="notranslate" translate="no">onChange</code> à laquelle vous pouvez passer une fonction de rappel à appeler lorsque la GUI modifie une valeur. Dans notre cas, nous avons juste besoin qu'elle appelle <code class="notranslate" translate="no">requestRenderIfNotRequested</code>. L'appel à <code class="notranslate" translate="no">folder.open</code> fait que le dossier s'ouvre dès le départ.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/render-on-demand-w-gui.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/render-on-demand-w-gui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que cela vous donne une idée de la façon de faire en sorte que three.js rende à la demande plutôt qu'en continu. Les applications/pages qui rendent three.js à la demande ne sont pas aussi courantes que la plupart des pages utilisant three.js qui sont soit des jeux, soit de l'art animé en 3D, mais des exemples de pages qui pourraient mieux rendre à la demande seraient, par exemple, une visionneuse de carte, un éditeur 3D, un générateur de graphiques 3D, un catalogue de produits, etc...</p>
 
         </div>
       </div>

+ 33 - 34
manual/fr/rendertargets.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Render Targets</title>
+    <title>Cibles de rendu</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Render Targets">
+    <meta name="twitter:title" content="Three.js – Cibles de rendu">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,19 +22,19 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Render Targets</h1>
+        <h1>Cibles de rendu</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>A render target in three.js is basically a texture you can render to.
-After you render to it you can use that texture like any other texture.</p>
-<p>Let's make a simple example. We'll start with an example from <a href="responsive.html">the article on responsiveness</a>.</p>
-<p>Rendering to a render target is almost exactly the same as normal rendering. First we create a <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a>.</p>
+          <p>Une cible de rendu dans three.js est essentiellement une texture sur laquelle vous pouvez effectuer un rendu.
+Une fois le rendu effectué, vous pouvez utiliser cette texture comme n'importe quelle autre texture.</p>
+<p>Faisons un exemple simple. Nous allons commencer par un exemple tiré de <a href="responsive.html">l'article sur la responsivité</a>.</p>
+<p>Rendre sur une cible de rendu est presque exactement la même chose qu'un rendu normal. D'abord, nous créons un <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const rtWidth = 512;
 const rtHeight = 512;
 const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);
 </pre>
-<p>Then we need a <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> and a <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a></p>
+<p>Ensuite, nous avons besoin d'une <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> et d'une <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const rtFov = 75;
 const rtAspect = rtWidth / rtHeight;
 const rtNear = 0.1;
@@ -45,11 +45,11 @@ rtCamera.position.z = 2;
 const rtScene = new THREE.Scene();
 rtScene.background = new THREE.Color('red');
 </pre>
-<p>Notice we set the aspect to the aspect for the render target, not the canvas.
-The correct aspect to use depends on what we are rendering for. In this case
-we'll use the render target's texture on the side of a cube. Since faces of
-the cube are square we want an aspect of 1.0.</p>
-<p>We fill the scene with stuff. In this case we're using the light and the 3 cubes <a href="responsive.html">from the previous article</a>.</p>
+<p>Notez que nous avons défini l'aspect sur celui de la cible de rendu, et non sur celui du canvas.
+Le bon aspect à utiliser dépend de ce pour quoi nous rendons. Dans ce cas,
+nous utiliserons la texture de la cible de rendu sur la face d'un cube. Puisque les faces de
+du cube sont carrées, nous voulons un aspect de 1.0.</p>
+<p>Nous remplissons la scène. Dans ce cas, nous utilisons la lumière et les 3 cubes <a href="responsive.html">de l'article précédent</a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const color = 0xFFFFFF;
   const intensity = 1;
@@ -80,22 +80,22 @@ function makeInstance(geometry, color, x) {
   makeInstance(geometry, 0xaa8844,  2),
 ];
 </pre>
-<p>The <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> and <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> from the previous article are still there. We'll use them to render to the canvas.
-We just need to add stuff to render.</p>
-<p>Let's add a cube that uses the render target's texture.</p>
+<p>La <a href="/docs/#api/en/scenes/Scene"><code class="notranslate" translate="no">Scene</code></a> et la <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a> de l'article précédent sont toujours là. Nous les utiliserons pour rendre sur le canvas.
+Il nous suffit d'ajouter des éléments à rendre.</p>
+<p>Ajoutons un cube qui utilise la texture de la cible de rendu.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial({
   map: renderTarget.texture,
 });
 const cube = new THREE.Mesh(geometry, material);
 scene.add(cube);
 </pre>
-<p>Now at render time first we render the render target scene to the render target.</p>
+<p>Maintenant, au moment du rendu, nous rendons d'abord la scène de la cible de rendu sur la cible de rendu.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
   time *= 0.001;
 
   ...
 
-  // rotate all the cubes in the render target scene
+  // faire tourner tous les cubes dans la scène de la cible de rendu
   rtCubes.forEach((cube, ndx) =&gt; {
     const speed = 1 + ndx * .1;
     const rot = time * speed;
@@ -103,44 +103,43 @@ scene.add(cube);
     cube.rotation.y = rot;
   });
 
-  // draw render target scene to render target
+  // dessiner la scène de la cible de rendu sur la cible de rendu
   renderer.setRenderTarget(renderTarget);
   renderer.render(rtScene, rtCamera);
   renderer.setRenderTarget(null);
 </pre>
-<p>Then we render the scene with the single cube that is using the render target's texture to the canvas.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">  // rotate the cube in the scene
+<p>Ensuite, nous rendons la scène avec le cube unique qui utilise la texture de la cible de rendu sur le canvas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">  // faire tourner le cube dans la scène
   cube.rotation.x = time;
   cube.rotation.y = time * 1.1;
 
-  // render the scene to the canvas
+  // rendre la scène sur le canvas
   renderer.render(scene, camera);
 </pre>
 <p>And voilà</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/render-target.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/render-target.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/render-target.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>The cube is red because we set the <code class="notranslate" translate="no">background</code> of the <code class="notranslate" translate="no">rtScene</code> to red so the
-render target's texture is being cleared to red.</p>
-<p>Render targets are used for all kinds of things. <a href="shadows.html">Shadows</a> use render targets.
-<a href="picking.html">Picking can use a render target</a>. Various kinds of
-<a href="post-processing.html">post processing effects</a> require render targets.
-Rendering a rear view mirror in a car or a live view on a monitor inside a 3D
-scene might use a render target.</p>
-<p>A few notes about using <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a>.</p>
+<p>Le cube est rouge car nous avons défini le <code class="notranslate" translate="no">background</code> de la <code class="notranslate" translate="no">rtScene</code> sur rouge, de sorte que la
+texture de la cible de rendu est effacée en rouge.</p>
+<p>Les cibles de rendu sont utilisées pour toutes sortes de choses. <a href="shadows.html">Les ombres</a> utilisent des cibles de rendu.
+<a href="picking.html">La sélection (picking) peut utiliser une cible de rendu</a>. Divers types d'<a href="post-processing.html">effets de post-traitement</a>
+nécessitent des cibles de rendu. Rendre un rétroviseur dans une voiture ou une vue en direct sur un moniteur à l'intérieur d'une scène 3D
+pourrait utiliser une cible de rendu.</p>
+<p>Quelques notes sur l'utilisation de <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a>.</p>
 <ul>
-<li><p>By default <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a> creates 2 textures. A color texture and a depth/stencil texture. If you don't need the depth or stencil textures you can request to not create them by passing in options. Example:</p>
+<li><p>Par défaut, <a href="/docs/#api/en/renderers/WebGLRenderTarget"><code class="notranslate" translate="no">WebGLRenderTarget</code></a> crée 2 textures. Une texture de couleur et une texture de profondeur/stencil. Si vous n'avez pas besoin des textures de profondeur ou de stencil, vous pouvez demander à ne pas les créer en passant des options. Exemple :</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">  const rt = new THREE.WebGLRenderTarget(width, height, {
     depthBuffer: false,
     stencilBuffer: false,
   });
 </pre>
 </li>
-<li><p>You might need to change the size of a render target</p>
-<p>In the example above we make a render target of a fixed size, 512x512. For things like post processing you generally need to make a render target the same size as your canvas. In our code that would mean when we change the canvas size we would also update both the render target size and the camera we're using when rendering to the render target. Example:</p>
+<li><p>Vous pourriez avoir besoin de changer la taille d'une cible de rendu</p>
+<p>Dans l'exemple ci-dessus, nous créons une cible de rendu de taille fixe, 512x512. Pour des choses comme le post-traitement, vous devez généralement créer une cible de rendu de la même taille que votre canvas. Dans notre code, cela signifierait que lorsque nous changeons la taille du canvas, nous mettons également à jour la taille de la cible de rendu et la caméra que nous utilisons lors du rendu sur la cible de rendu. Exemple :</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">function render(time) {
   time *= 0.001;
 

+ 128 - 153
manual/fr/responsive.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Design réactif et </title>
+    <title>Conception Réactive</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Design réactif et ">
+    <meta name="twitter:title" content="Three.js – Conception Réactive">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,31 +22,21 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Design réactif</h1>
+        <h1>Conception Réactive</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Ceci est le second article dans une série traitant de Three.js.
-Le premier traitait <a href="fundamentals.html">des principes de base</a>.
-Si vous ne l'avez pas encore lu, vous devriez peut-être commencer par là.</p>
-<p>Cet article explique comment rendre votre application Three.js adaptable
-à n'importe quelle situation. Rendre une page web adaptable (<em>responsive</em>)
-se réfère généralement à faire en sorte que la page s'affiche de manière
-appropriée sur des écrans de taille différente, des ordinateurs de bureau
-aux <em>smart-phones</em>, en passant par les tablettes.</p>
-<p>Concernant Three.js, il y a d'ailleurs davantage de situations à traiter.
-Par exemple, un éditeur 3D avec des contrôles à gauche, droite, en haut ou
-en bas est quelque chose que nous voudrions gérer. Un schéma interactif
-au milieu d'un document en est un autre exemple.</p>
-<p>Le dernier exemple que nous avions utilisé est un canvas sans CSS et
-sans taille :</p>
+          <p>Ceci est le deuxième article d'une série d'articles sur three.js.
+Le premier article traitait <a href="fundamentals.html">des fondamentaux</a>.
+Si vous ne l'avez pas encore lu, vous voudrez peut-être commencer par là.</p>
+<p>Cet article explique comment rendre votre application three.js réactive à toute situation. Rendre une page web réactive fait généralement référence à la capacité de la page à s'afficher correctement sur des écrans de différentes tailles, des ordinateurs de bureau aux tablettes et téléphones.</p>
+<p>Pour three.js, il y a encore plus de situations à considérer. Par exemple, un éditeur 3D avec des contrôles à gauche, à droite, en haut ou en bas est quelque chose que nous pourrions vouloir gérer. Un diagramme interactif au milieu d'un document est un autre exemple.</p>
+<p>Le dernier exemple que nous avions utilisait un simple canevas sans CSS et sans taille</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
 </pre>
-<p>Ce canvas a, par défaut, une taille de 300x150 pixels.
-Dans le navigateur, la manière recommandée de fixer la taille
-de quelque chose est d'utiliser CSS.</p>
-<p>Paramétrons le canvas pour occuper complètement la page en ajoutant
-du CSS :</p>
+<p>Ce canevas a par défaut une taille de 300x150 pixels CSS.</p>
+<p>Dans la plateforme web, la méthode recommandée pour définir la taille d'un élément est d'utiliser CSS.</p>
+<p>Faisons en sorte que le canevas remplisse la page en ajoutant du CSS</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;style&gt;
 html, body {
    margin: 0;
@@ -59,38 +49,35 @@ html, body {
 }
 &lt;/style&gt;
 </pre>
-<p>En HTML, la balise <em>body</em> a une marge fixée à 5 pixels par défaut donc
-la changer à 0 la retire. Modifier la hauteur de <em>html</em> et <em>body</em> à 100%
-leur fait occuper toute la fenêtre. Sinon, ils sont seulement aussi large
-que le contenu qu'ils contiennent.</p>
-<p>Ensuite, nous faisons en sorte que l'élément <code class="notranslate" translate="no">id=c</code> fasse
-100% de la taille de son conteneur qui est, dans ce cas, la balise body.</p>
-<p>Finalement, nous passons le mode <code class="notranslate" translate="no">display</code> à <code class="notranslate" translate="no">block</code>.
-Le mode d'affichage par défaut d'un canvas est <code class="notranslate" translate="no">inline</code>, ce qui implique
-que des espaces peuvent être ajoutés à l'affichage.
-En passant le canvas à <code class="notranslate" translate="no">block</code>, ce problème est supprimé.</p>
-<p>Voici le résultat :</p>
+<p>En HTML, le corps (body) a une marge de 5 pixels par défaut, donc définir la marge à 0 supprime cette marge. Définir la hauteur de html et body à 100 % leur permet de remplir la fenêtre. Sinon, ils ne sont que de la taille du contenu qui les remplit.</p>
+<p>Ensuite, nous disons à l'élément <code class="notranslate" translate="no">id=c</code> de prendre
+100 % de la taille de son conteneur, qui est ici le corps du
+document.</p>
+<p>Enfin, nous définissons son mode <code class="notranslate" translate="no">display</code> à <code class="notranslate" translate="no">block</code>. Le mode d'affichage par défaut d'un canevas est <code class="notranslate" translate="no">inline</code>. Les éléments inline
+peuvent finir par ajouter des espaces blancs à l'affichage. En
+définissant le canevas à <code class="notranslate" translate="no">block</code>, ce problème disparaît.</p>
+<p>Voici le résultat</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-no-resize.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive-no-resize.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive-no-resize.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Le canvas, comme nous le voyons, remplit maintenant la page mais il y a deux
-problèmes. Tout d'abord, nos cubes sont étirés et ressemblent à des boîtes trop
-hautes et trop larges. Ouvrez l'exemple dans sa propre fenêtre et
-redimensionnez la, vous verrez comment les cubes s'en trouvent déformés
-en hauteur et en largeur.</p>
+<p>Vous pouvez voir que le canevas remplit maintenant la page, mais il y a 2
+problèmes. Premièrement, nos cubes sont étirés. Ce ne sont pas des cubes,
+mais plutôt des boîtes. Trop hauts ou trop larges. Ouvrez l'exemple
+dans sa propre fenêtre et redimensionnez-le. Vous verrez comment
+les cubes s'étirent en largeur et en hauteur.</p>
 <p><img src="../resources/images/resize-incorrect-aspect.png" width="407" class="threejs_center nobg"></p>
-<p>Le second problème est qu'ils semblent affichés en basse résolution ou
-à la fois flous et pixelisés. Si vous étirez beaucoup la fenêtre, vous verrez
-pleinement le problème.</p>
+<p>Le deuxième problème est qu'ils semblent avoir une faible résolution ou être pixellisés et
+flous. Élargissez beaucoup la fenêtre et vous verrez vraiment
+le problème.</p>
 <p><img src="../resources/images/resize-low-res.png" class="threejs_center nobg"></p>
-<p>Tout d'abord, nous allons résoudre le problème d'étirement.
-Pour cela, nous devons calquer le ratio de la caméra sur celui
-de la taille d'affichage du canvas. Nous pouvons le faire
-en utilisant les propriétés <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code> du canvas.</p>
-<p>Nous mettons alors notre boucle de rendu comme cela :</p>
+<p>Résolvons d'abord le problème de l'étirement. Pour ce faire, nous devons
+définir l'aspect de la caméra sur l'aspect de la taille d'affichage du canevas.
+Nous pouvons le faire en examinant les propriétés <code class="notranslate" translate="no">clientWidth</code>
+et <code class="notranslate" translate="no">clientHeight</code> du canevas.</p>
+<p>Nous allons mettre à jour notre boucle de rendu comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
   time *= 0.001;
 
@@ -100,34 +87,32 @@ en utilisant les propriétés <code class="notranslate" translate="no">clientWid
 
   ...
 </pre>
-<p>A présent les cubes ne devraient plus être déformés.</p>
+<p>Maintenant, les cubes ne devraient plus être déformés.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-update-camera.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive-update-camera.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive-update-camera.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Ouvrez l'exemple dans une fenêtre séparée et redimensionnez la.
-Vous devriez voir que les cubes ne sont plus étirés, que ce soit
-en hauteur ou en largeur.
-Ils restent corrects quelque soit l'aspect de la taille de la fenêtre.</p>
+<p>Ouvrez l'exemple dans une fenêtre séparée et redimensionnez la fenêtre.
+Vous devriez voir que les cubes ne sont plus étirés en hauteur ou en largeur.
+Ils conservent le bon aspect quelle que soit la taille de la fenêtre.</p>
 <p><img src="../resources/images/resize-correct-aspect.png" width="407" class="threejs_center nobg"></p>
-<p>Maintenant résolvons le problème de la pixellisation.</p>
-<p>Les éléments de type <em>canvas</em> ont deux tailles. La première
-est celle du canvas affiché dans la page. C'est ce que nous paramétrons avec le CSS.
-L'autre taille est le nombre de pixels dont est constitué le canvas lui-même.
-Ceci n'est pas différent d'une image.
-Par exemple, nous pouvons avoir une image de taille 128x64 et, en utilisant le CSS,
-nous pouvons l'afficher avec une taille de 400x200.</p>
+<p>Maintenant, résolvons le problème de la pixellisation.</p>
+<p>Les éléments Canvas ont 2 tailles. Une taille est la taille à laquelle le canevas est affiché
+sur la page. C'est ce que nous définissons avec CSS. L'autre taille est le
+nombre de pixels dans le canevas lui-même. Ce n'est pas différent d'une image.
+Par exemple, nous pourrions avoir une image de 128x64 pixels et
+l'afficher en 400x200 pixels en utilisant CSS.</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;img src="some128x64image.jpg" style="width:400px; height:200px"&gt;
 </pre>
-<p>La taille interne d'un canvas, sa résolution, est souvent appelée sa taille de tampon
-de dessin (<em>drawingbuffer</em>). Dans Three.js, nous pouvons ajuster la taille
-du canvas en appelant <code class="notranslate" translate="no">renderer.setSize</code>.
-Quelle taille devons nous choisir ? La réponse la plus évidente est "la même taille que
-celle du canvas". A nouveau, pour le faire, nous pouvons recourir
-aux propriétés <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code>.</p>
-<p>Ecrivons une fonction qui vérifie si le rendu du canvas a la bonne taille et l'ajuste en conséquence.</p>
+<p>La taille interne d'un canevas, sa résolution, est souvent appelée sa taille de drawingbuffer.
+Dans three.js, nous pouvons définir la taille du drawingbuffer du canevas en appelant <code class="notranslate" translate="no">renderer.setSize</code>.
+Quelle taille devrions-nous choisir ? La réponse la plus évidente est « la même taille que celle affichée par le canevas ».
+Encore une fois, pour ce faire, nous pouvons examiner les propriétés <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code>
+du canevas.</p>
+<p>Écrivons une fonction qui vérifie si le canevas du renderer n'a pas
+déjà la taille à laquelle il est affiché et, si ce n'est pas le cas, définit sa taille.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function resizeRendererToDisplaySize(renderer) {
   const canvas = renderer.domElement;
   const width = canvas.clientWidth;
@@ -139,21 +124,18 @@ aux propriétés <code class="notranslate" translate="no">clientWidth</code> et
   return needResize;
 }
 </pre>
-<p>Remarquez que nous vérifions si le canvas a réellement besoin d'être redimensionné.
-Le redimensionnement est une partie intéressante de la spécification du canvas
-et il est mieux de ne pas lui donner à nouveau la même taille s'il est déjà
-à la dimension que nous voulons.</p>
-<p>Une fois que nous savons si le redimensionnement est nécessaire ou non, nous
-appelons <code class="notranslate" translate="no">renderer.setSize</code> et lui passons les nouvelles largeur et hauteur.
-Il est important de passer <code class="notranslate" translate="no">false</code> en troisième.
-<code class="notranslate" translate="no">render.setSize</code> modifie par défaut la taille du canvas dans le CSS, mais ce n'est
-pas ce que nous voulons. Nous souhaitons que le navigateur continue à fonctionner
-comme pour les autres éléments, en utilisant le CSS pour déterminer la
-taille d'affichage d'un élément. Nous ne voulons pas que les canvas utilisés
-par Three.js aient un comportement différent des autres éléments.</p>
-<p>Remarquez que notre fonction renvoie <em>true</em> si le canvas a été redimensionné.
-Nous pouvons l'utiliser pour vérifier si d'autre choses doivent être mises à jour.
-Modifions à présent notre boucle de rendu pour utiliser la nouvelle fonction :</p>
+<p>Notez que nous vérifions si le canevas a réellement besoin d'être redimensionné. Le redimensionnement du canevas
+est une partie intéressante de la spécification du canevas, et il est préférable de ne pas définir la même
+taille si elle est déjà celle que nous souhaitons.</p>
+<p>Une fois que nous savons si nous devons redimensionner ou non, nous appelons alors <code class="notranslate" translate="no">renderer.setSize</code> et
+passons la nouvelle largeur et hauteur. Il est important de passer <code class="notranslate" translate="no">false</code> à la fin.
+<code class="notranslate" translate="no">render.setSize</code> définit par défaut la taille CSS du canevas, mais ce n'est pas
+ce que nous voulons. Nous voulons que le navigateur continue à fonctionner comme il le fait pour tous les autres
+éléments, c'est-à-dire en utilisant CSS pour déterminer la taille d'affichage de l'élément. Nous ne
+voulons pas que les canevas utilisés par three soient différents des autres éléments.</p>
+<p>Notez que notre fonction retourne true si le canevas a été redimensionné.
+Nous pouvons l'utiliser pour vérifier s'il y a d'autres éléments que nous devrions mettre à jour. Modifions
+notre boucle de rendu pour utiliser la nouvelle fonction</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
   time *= 0.001;
 
@@ -165,73 +147,71 @@ Modifions à présent notre boucle de rendu pour utiliser la nouvelle fonction :
 
   ...
 </pre>
-<p>Puisque l'aspect ne change que si la taille d'affichage du canvas change,
-nous ne modifions l'aspect de la caméra que si <code class="notranslate" translate="no">resizeRendererToDisplaySize</code>
+<p>Comme l'aspect ne changera que si la taille d'affichage du canevas a changé,
+nous ne définissons l'aspect de la caméra que si <code class="notranslate" translate="no">resizeRendererToDisplaySize</code>
 retourne <code class="notranslate" translate="no">true</code>.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Le rendu devrait à présent avoir une résolution correspondant à
-la taille d'affichage du canvas.</p>
-<p>Afin de comprendre pourquoi il faut laisser le CSS gérer le redimensionnement,
-prenons notre code et mettons le dans un <a href="../examples/threejs-responsive.js">fichier <code class="notranslate" translate="no">.js</code> séparé</a>. Voici donc quelques autres exemples où nous avons laissé le CSS choisir la taille et remarquez que nous n'avons
-eu aucun code à modifier pour qu'ils fonctionnent.</p>
+<p>Il devrait maintenant rendre avec une résolution qui correspond à la taille d'affichage
+du canevas.</p>
+<p>Pour illustrer le fait de laisser CSS gérer le redimensionnement, prenons
+notre code et mettons-le dans un <a href="../examples/threejs-responsive.js">fichier <code class="notranslate" translate="no">.js</code> séparé</a>.
+Voici ensuite quelques exemples supplémentaires où nous laissons CSS choisir la taille, et notez
+que nous n'avons eu à modifier aucun code pour qu'ils fonctionnent.</p>
 <p>Mettons nos cubes au milieu d'un paragraphe de texte.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-paragraph.html&amp;startPane=html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive-paragraph.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive-paragraph.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>et voici notre même code utilisé dans un éditeur où la zone de contrôle à droite peut être redimensionnée.</p>
+<p>et voici notre même code utilisé dans une disposition de style éditeur
+où la zone de contrôle à droite peut être redimensionnée.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-editor.html&amp;startPane=html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive-editor.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive-editor.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Le point important à remarquer est que le code n'est pas modifié, seulement
-le HTML et le CSS.</p>
-<h2 id="g-rer-les-affichages-hd-dpi">Gérer les affichages HD-DPI</h2>
-<p>HD-DPI est l'acronyme pour <em>high-density dot per inch</em>,
-autrement dit, les écrans à haute densité d'affichage.
-C'est le cas de la plupart des Macs, des machines sous Windows
-ainsi que des smartphones.</p>
-<p>La façon dont cela fonctionne dans le navigateur est
-qu'il utilise les pixels CSS pour mettre à jour la taille
-qui est supposée être la même quelque soit la résolution de
-l'affichage. Le navigateur effectue le rendu du texte avec davantage
-de détails mais la même taille physique.</p>
-<p>Il y a plusieurs façons de gérer les HD-DPI avec Three.js.</p>
-<p>La première façon est de ne rien faire de spécial. Cela
-est, de manière discutable, le plus commun. Effectuer le
-rendu de graphismes 3D réclame beaucoup de puissance de calcul au GPU
-(<em>Graphics Processing Units</em>, les processeurs dédiés de carte graphique).
-Les GPUs des smartphones ont moins de puissance que ceux des ordinateurs de bureau,
-du moins en 2018, et pourtant les téléphones mobiles ont des affichages
-haute résolution. Le haut de gamme actuel pour les smartphones a un ratio
-HD-DPI de 3x, ce qui signifie que pour chaque pixel d'un affichage non HD-DPI,
-ces téléphones ont 9 pixels. Il y a donc 9 fois plus de travail
-pour le rendu.</p>
-<p>Calculer pour 9 pixels nécessite des ressources. Donc, si
-nous laissons le code comme cela, nous calculerons pour 1 pixel
-et le navigateur le dessinera avec 3 fois sa taille (3 x 3 = 9 pixels).</p>
-<p>Pour toute application Three.js lourde, c'est probablement ce que vous
-voulez sinon vous risquez d'avoir un taux de rafraîchissement faible (<em>framerate</em>).</p>
-<p>Ceci étant dit, si vous préférez effectuer le rendu à la résolution de l'appareil,
-voici quelques façons de le faire en Three.js.</p>
-<p>La première est d'indiquer à Three.js le facteur de multiplication de la résolution
-en utilisant <code class="notranslate" translate="no">renderer.setPixelRatio</code>. Nous pouvons demander au navigateur ce
-facteur entre les pixels CSS et les pixels du périphérique et les passer à Three.js</p>
+<p>Le point important à noter est qu'aucun code n'a été modifié. Seuls notre HTML et notre CSS
+ont changé.</p>
+<h2 id="handling-hd-dpi-displays">Gestion des écrans HD-DPI</h2>
+<p>HD-DPI signifie écrans à haute densité de points par pouce (high-density dot per inch displays).
+C'est le cas de la plupart des Macs actuels et de nombreuses machines Windows,
+ainsi que de la quasi-totalité des smartphones.</p>
+<p>La façon dont cela fonctionne dans le navigateur est qu'ils utilisent
+des pixels CSS pour définir les tailles, qui sont censées être les mêmes
+quelle que soit la résolution de l'écran. Le navigateur
+se contentera de rendre le texte avec plus de détails, mais avec la même
+taille physique.</p>
+<p>Il existe différentes façons de gérer les écrans HD-DPI avec three.js.</p>
+<p>La première consiste simplement à ne rien faire de spécial. C'est sans doute
+la méthode la plus courante. Le rendu graphique 3D nécessite beaucoup de
+puissance de traitement GPU. Les GPU mobiles ont moins de puissance que les ordinateurs de bureau,
+du moins en 2018, et pourtant les téléphones portables ont souvent des écrans à très
+haute résolution. Les téléphones haut de gamme actuels ont un rapport HD-DPI
+de 3x, ce qui signifie que pour chaque pixel d'un écran non HD-DPI,
+ces téléphones ont 9 pixels. Cela signifie qu'ils doivent effectuer 9 fois
+le rendu.</p>
+<p>Calculer 9 fois les pixels représente beaucoup de travail, donc si nous
+laissons le code tel quel, nous calculerons 1x les pixels et le
+navigateur se contentera de l'afficher à 3x la taille (3x par 3x = 9x pixels).</p>
+<p>Pour toute application three.js lourde, c'est probablement ce que vous voudrez,
+sinon vous risquez d'avoir une fréquence d'images lente.</p>
+<p>Cela dit, si vous souhaitez réellement rendre à la résolution
+de l'appareil, il existe plusieurs façons de le faire dans three.js.</p>
+<p>L'une consiste à indiquer à three.js un multiplicateur de résolution en utilisant <code class="notranslate" translate="no">renderer.setPixelRatio</code>.
+Vous demandez au navigateur quel est le multiplicateur entre les pixels CSS et les pixels de l'appareil,
+et vous le passez à three.js</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> renderer.setPixelRatio(window.devicePixelRatio);
-</pre><p>Après cela, tout appel à <code class="notranslate" translate="no">renderer.setSize</code> va automatiquement
-utiliser la taille que vous avez demandé, multiplié par le
-ratio que vous avez demandé.
-<strong>Ceci est fortement DÉCONSEILLÉ</strong>. Voir ci-dessous.</p>
-<p>L'autre façon est de le faire par soi-même quand on redimensionne le canvas.</p>
+</pre><p>Après cela, tout appel à <code class="notranslate" translate="no">renderer.setSize</code> utilisera magiquement
+la taille demandée multipliée par le rapport de pixels
+que vous avez passé. <strong>Ceci est fortement DÉCONSEILLÉ</strong>. Voir ci-dessous</p>
+<p>L'autre méthode consiste à le faire vous-même lorsque vous redimensionnez le canevas.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">    function resizeRendererToDisplaySize(renderer) {
       const canvas = renderer.domElement;
       const pixelRatio = window.devicePixelRatio;
@@ -244,33 +224,28 @@ ratio que vous avez demandé.
       return needResize;
     }
 </pre>
-<p>Cette seconde façon est objectivement meilleure. Pourquoi ? Parce que cela signifie
-que nous avons ce que nous avons demandé. Il y a plusieurs cas où,
-quand on utilise Three.js, nous avons besoin de connaître la taille effective
-du tampon d'affichage du canvas. Par exemple, quand on réalise un filtre de
-post-processing, ou si nous faisons un <em>shader</em> qui accède à <code class="notranslate" translate="no">gl_FragCoord</code>,
-si nous sommes en train de faire une capture d'écran, ou en train de lire les pixels
-pour une sélection par GPU, pour dessiner dans un canvas 2D, etc...
-Il y a plusieurs cas où, si nous utilisons <code class="notranslate" translate="no">setPixelRatio</code> alors notre
-taille effective est différente de la taille que nous avons demandé et nous
-aurons alors à deviner quand utiliser la taille demandée ou la taille utilisée
-par Three.js.
-En le faisant par soi-même, nous savons toujours que la taille utilisée
-est celle que nous avons demandé. Il n'y a aucun cas où cela se fait tout
-seul autrement.</p>
-<p>Voici un exemple utilisant le code vu plus haut.</p>
+<p>Cette seconde méthode est objectivement meilleure. Pourquoi ? Parce que cela signifie que j'obtiens ce que je demande.
+Il existe de nombreux cas lors de l'utilisation de three.js où nous devons connaître la taille réelle
+du drawingBuffer du canevas. Par exemple, lors de la création d'un filtre de post-traitement,
+ou si nous créons un shader qui accède à <code class="notranslate" translate="no">gl_FragCoord</code>, si nous faisons
+une capture d'écran, ou lisons des pixels pour la sélection GPU, pour dessiner dans un canevas 2D,
+etc... Il existe de nombreux cas où si nous utilisons <code class="notranslate" translate="no">setPixelRatio</code>, notre taille réelle sera différente
+de la taille demandée, et nous devrons deviner quand utiliser la taille
+que nous avons demandée et quand utiliser la taille que three.js utilise réellement.
+En le faisant nous-mêmes, nous savons toujours que la taille utilisée est la taille que nous avons demandée.
+Il n'y a pas de cas particulier où de la magie opère en coulisses.</p>
+<p>Voici un exemple utilisant le code ci-dessus.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-hd-dpi.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/responsive-hd-dpi.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/responsive-hd-dpi.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Il vous est peut-être difficile de voir la différence, mais si vous avez
-un affichage HD-DPI et que vous comparez cet exemple aux autres plus
-haut, vous devriez remarquer que les arêtes sont plus vives.</p>
+<p>Il peut être difficile de voir la différence, mais si vous avez un écran HD-DPI
+et que vous comparez cet exemple à ceux ci-dessus, vous devriez
+remarquer que les bords sont plus nets.</p>
 <p>Cet article a couvert un sujet très basique mais fondamental.
-Dans l'article suivant, nous allons rapidement
-<a href="primitives.html">passer en revue les primitives de base proposées par Three.js</a>.</p>
+Ensuite, passons rapidement en revue <a href="primitives.html">les primitives de base que three.js fournit</a>.</p>
 
         </div>
       </div>
@@ -282,4 +257,4 @@ Dans l'article suivant, nous allons rapidement
 
 
 
-</body></html>
+</body></html>

+ 206 - 99
manual/fr/scenegraph.html

@@ -26,23 +26,43 @@
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là.</p>
-<p>Le cœur de Three.js est sans aucun doute son graphe de scène. Un graphe de scène est une représentation arborescente des objets que l’on souhaite afficher, où chaque nœud représente un espace local.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le
+premier article est <a href="fundamentals.html">les bases de three.js</a>. Si
+vous ne l'avez pas encore lu, vous pourriez envisager de commencer par là.</p>
+<p>Le cœur de Three.js est sans doute son graphe de scène. Un graphe de scène dans un moteur 3D
+est une hiérarchie de nœuds dans un graphe où chaque nœud représente
+un espace local.</p>
 <p><img src="../resources/images/scenegraph-generic.svg" align="center"></p>
 <p>C'est un peu abstrait, alors essayons de donner quelques exemples.</p>
-<p>On pourrait prendre comme exemple le système solaire, le Soleil, la Terre et la Lune.</p>
+<p>Un exemple pourrait être le système solaire : soleil, terre, lune.</p>
 <p><img src="../resources/images/scenegraph-solarsystem.svg" align="center"></p>
-<p>La Terre tourne autour du Soleil. La Lune tourne autour de la Terre. La Lune se déplace en cercle autour de la Terre. Du point de vue de la Lune, elle tourne dans "l'espace local" de la Terre. Même si son mouvement par rapport au Soleil est une courbe folle comme un spirographe du point de vue de la Lune, il n'a qu'à se préoccuper de tourner autour de l'espace local de la Terre.</p>
+<p>La Terre tourne autour du Soleil. La Lune tourne autour de la Terre. La Lune
+se déplace en cercle autour de la Terre. Du point de
+vue de la Lune, elle tourne dans l'« espace local » de la Terre. Même si
+son mouvement par rapport au Soleil est une courbe folle ressemblant à un spirographe, du point de vue de la Lune, elle n'a qu'à se soucier de tourner
+autour de l'espace local de la Terre.</p>
 <p></p><div class="threejs_diagram_container">
   <iframe class="threejs_diagram " style="width: 400px; height: 300px;" src="/manual/foo/../resources/moon-orbit.html"></iframe>
 </div>
 
 <p></p>
-<p>Pour le voir autrement, vous qui vivez sur Terre, n'avez pas à penser à la rotation de la Terre sur son axe ni à sa rotation autour du Soleil. Vous marchez ou conduisez ou nagez ou courez comme si la Terre ne bougeait pas ou ne tournait pas du tout. Vous marchez, conduisez, nagez, courez et vivez dans "l'espace local" de la Terre, même si par rapport au Soleil, vous tournez autour de la Terre à environ 1 600 km/h et autour du Soleil à environ 107000 km/h. Votre position dans le système solaire est similaire à celle de la Lune au-dessus, mais vous n'avez pas à vous en préoccuper. Vous vous souciez de votre position par rapport à la Terre dans son "espace local".</p>
-<p>Mais allons-y une étape à la fois! Imaginez que nous voulions faire un diagramme du Soleil, de la Terre et de la Lune. Nous allons commencer par le Soleil en créant une simple sphère et en la mettant à l'origine. Remarque : Nous utilisons le Soleil, la Terre et la Lune comme démonstration de l'utilisation d'une scène. Bien sûr, le vrai Soleil, la Terre et la Lune utilisent la physique, mais pour nos besoins, nous allons faire semblant.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// un tableau d'objets dont la rotation à mettre à jour
+<p>Pour le voir autrement, vous qui vivez sur Terre n'avez pas à penser
+à la rotation de la Terre sur son axe ni à sa rotation autour du
+Soleil. Vous marchez, conduisez, nagez ou courez comme si la Terre
+ne bougeait ni ne tournait pas du tout. Vous marchez, conduisez, nagez, courez et vivez
+dans l'« espace local » de la Terre, même si, par rapport au Soleil, vous tournez
+autour de la Terre à environ 1600 kilomètres par heure et autour
+du Soleil à environ 108 000 kilomètres par heure. Votre position dans le système solaire
+est similaire à celle de la Lune ci-dessus, mais vous n'avez pas à vous en soucier.
+Vous vous préoccupez simplement de votre position par rapport à la Terre dans son
+« espace local ».</p>
+<p>Prenons les choses étape par étape. Imaginez que nous voulions faire
+un diagramme du soleil, de la terre et de la lune. Nous commencerons par le soleil en
+créant simplement une sphère et en la plaçant à l'origine. Remarque : Nous utilisons
+le soleil, la terre, la lune pour illustrer comment utiliser un graphe de scène. Bien sûr,
+le vrai soleil, la terre et la lune utilisent la physique, mais pour nos besoins, nous allons
+simuler cela avec un graphe de scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// un tableau d'objets dont la rotation doit être mise à jour
 const objects = [];
 
 // utiliser une seule sphère pour tout
@@ -54,14 +74,19 @@ const sphereGeometry = new THREE.SphereGeometry(
 
 const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
 const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
-sunMesh.scale.set(5, 5, 5);  // agrandir le Soleil
+sunMesh.scale.set(5, 5, 5);  // agrandir le soleil
 scene.add(sunMesh);
 objects.push(sunMesh);
 </pre>
-<p>Nous utilisons une sphère ayant un faible nombre de polygones (avec seulement 6 segments autour de son équateur) afin de faciliter la visualisation de sa rotation.</p>
-<p>Nous allons réutiliser la même sphère pour les autres astres : nous allons grossir la <code class="notranslate" translate="no">sunMesh</code> 5 fois.</p>
-<p>Nous avons également défini la propriété <code class="notranslate" translate="no">emissive</code> du matériau Phong sur jaune. La propriété émissive d'un matériau Phong est essentiellement la couleur qui sera dessinée lorsque la lumière ne frappe pas la surface. La lumière est ajoutée à cette couleur.</p>
-<p>Mettons aussi une 'point light' au centre de la scène. Nous entrerons dans les détails plus tard, mais pour l'instant, la version simple est une lumière qui émane d'un seul point.</p>
+<p>Nous utilisons une sphère avec très peu de polygones. Seulement 6 subdivisions autour de son équateur.
+C'est pour qu'il soit facile de voir la rotation.</p>
+<p>Nous allons réutiliser la même sphère pour tout, nous allons donc définir une échelle
+de 5x pour le maillage du soleil.</p>
+<p>Nous définissons également la propriété <code class="notranslate" translate="no">emissive</code> du matériau phong en jaune. La propriété emissive d'un matériau phong est essentiellement la couleur qui sera dessinée sans lumière frappant
+la surface. La lumière est ajoutée à cette couleur.</p>
+<p>Plaçons également une seule lumière ponctuelle au centre de la scène. Nous reviendrons plus tard sur
+les détails des lumières ponctuelles, mais pour l'instant, la version simple est qu'une lumière ponctuelle
+représente la lumière qui émane d'un point unique.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const color = 0xFFFFFF;
   const intensity = 3;
@@ -69,58 +94,73 @@ objects.push(sunMesh);
   scene.add(light);
 }
 </pre>
-<p>Pour faciliter la visualisation, nous allons placer la caméra directement au-dessus de l'origine en regardant vers le bas. Le moyen le plus simple de le faire est d'utiliser la fonction <code class="notranslate" translate="no">lookAt</code>. Cette fonction oriente la caméra pour "regarder" vers la position que nous passons à <code class="notranslate" translate="no">lookAt</code>. Avant de faire cela, nous devons cependant indiquer à la caméra dans quelle direction est orienté son "haut". Pour la plupart des situations, un Y positif est suffisant, mais puisque nous regardons vers le bas, nous devons dire à la caméra que le "haut" est le Z positif.</p>
+<p>Pour faciliter la visualisation, nous allons placer la caméra juste au-dessus de l'origine,
+regardant vers le bas. La façon la plus simple de faire cela est d'utiliser la fonction <code class="notranslate" translate="no">lookAt</code>. La fonction <code class="notranslate" translate="no">lookAt</code>
+orientera la caméra depuis sa position pour « regarder » la position
+que nous lui passons. Cependant, avant de faire cela, nous devons indiquer à la caméra
+dans quelle direction se trouve le haut de la caméra, ou plutôt quelle direction est le « haut » pour la
+caméra. Dans la plupart des situations, Y positif vers le haut est suffisant, mais puisque
+nous regardons directement vers le bas, nous devons indiquer à la caméra que Z positif est vers le haut.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 camera.position.set(0, 50, 0);
 camera.up.set(0, 0, 1);
 camera.lookAt(0, 0, 0);
 </pre>
-<p>Dans la boucle de rendu, issue des exemples précédents, nous faisons pivoter tous les objets de notre tableau <code class="notranslate" translate="no">objects</code> avec ce code.</p>
+<p>Dans la boucle de rendu, adaptée des exemples précédents, nous faisons pivoter tous
+les objets de notre tableau <code class="notranslate" translate="no">objects</code> avec ce code.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">objects.forEach((obj) =&gt; {
   obj.rotation.y = time;
 });
 </pre>
-<p>Ajouter la <code class="notranslate" translate="no">sunMesh</code> au tableau <code class="notranslate" translate="no">objects</code>, la fait pivoter.</p>
+<p>Comme nous avons ajouté le <code class="notranslate" translate="no">sunMesh</code> au tableau <code class="notranslate" translate="no">objects</code>, il va tourner.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Ajoutons maintenant la Terre.</p>
+<p>Maintenant, ajoutons la Terre.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
 const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
 earthMesh.position.x = 10;
 scene.add(earthMesh);
 objects.push(earthMesh);
 </pre>
-<p>Nous fabriquons un matériau bleu, mais nous lui donnons une petite quantité de bleu émissif pour qu'il apparaisse sur notre fond noir.</p>
-<p>Nous utilisons la même <code class="notranslate" translate="no">sphereGeometry</code> avec notre nouveau <code class="notranslate" translate="no">EarthMaterial</code> bleu pour faire une <code class="notranslate" translate="no">earthMesh</code>.
-Nous le positionnons 10 unités à gauche du Soleil et l'ajoutons à la scène. Nous l'ajoutons à notre tableau <code class="notranslate" translate="no">objects</code> ce qui le met également en mouvement.</p>
+<p>Nous créons un matériau bleu, mais nous lui donnons une petite quantité de bleu <em>émissif</em>
+afin qu'il ressorte sur notre fond noir.</p>
+<p>Nous utilisons la même <code class="notranslate" translate="no">sphereGeometry</code> avec notre nouveau <code class="notranslate" translate="no">earthMaterial</code> bleu pour créer
+un <code class="notranslate" translate="no">earthMesh</code>. Nous le positionnons à 10 unités à gauche du soleil
+et l'ajoutons à la scène.  Comme nous l'avons ajouté à notre tableau <code class="notranslate" translate="no">objects</code>, il tournera aussi.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Vous pouvez voir que le Soleil et la Terre tournent, mais que la Terre ne tourne pas autour du Soleil. Faisons de la Terre un enfant du Soleil</p>
+<p>Vous pouvez voir que le soleil et la terre tournent, mais la terre ne
+tourne pas autour du soleil. Faisons de la terre un enfant du soleil</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-scene.add(earthMesh);
 +sunMesh.add(earthMesh);
 </pre>
 <p>et...</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth-orbit.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Que s'est-il passé ? Pourquoi la Terre a-t-elle la même taille que le Soleil et pourquoi est-elle si loin ? En fait, j'ai dû déplacer la caméra de 50 à 150 unités au-dessus pour voir la Terre.</p>
-<p>Nous avons fait de <code class="notranslate" translate="no">earthMesh</code> un enfant du <code class="notranslate" translate="no">sunMesh</code>.
-La <code class="notranslate" translate="no">sunMesh</code> a son échelle définie sur 5x grâce à <code class="notranslate" translate="no">sunMesh.scale.set(5, 5, 5)</code>. Cela signifie que l'espace local sunMeshs est 5 fois plus grand.
-Tout ce qui est mis dans cet espace sera multiplié par 5. Cela signifie que la Terre est maintenant 5 fois plus grande et sa distance par rapport au Soleil (<code class="notranslate" translate="no">earthMesh.position.x = 10</code>) est également 5 fois plus grande.</p>
-<p> Notre scène ressemble maintenant à cela</p>
+<p>Que s'est-il passé ? Pourquoi la Terre a-t-elle la même taille que le Soleil et pourquoi est-elle si loin ?
+J'ai en fait dû déplacer la caméra de 50 unités au-dessus à 150 unités au-dessus pour voir la Terre.</p>
+<p>Nous avons fait du <code class="notranslate" translate="no">earthMesh</code> un enfant du <code class="notranslate" translate="no">sunMesh</code>. L'échelle du <code class="notranslate" translate="no">sunMesh</code> est
+réglée à 5x avec <code class="notranslate" translate="no">sunMesh.scale.set(5, 5, 5)</code>. Cela signifie que
+l'espace local du <code class="notranslate" translate="no">sunMesh</code> est 5 fois plus grand. Tout ce qui est placé dans cet espace
+sera multiplié par 5. Cela signifie que la terre est maintenant 5 fois plus grande et
+que sa distance par rapport au soleil (<code class="notranslate" translate="no">earthMesh.position.x = 10</code>) est également
+multipliée par 5.</p>
+<p> Notre graphe de scène ressemble actuellement à ceci</p>
 <p><img src="../resources/images/scenegraph-sun-earth.svg" align="center"></p>
-<p>Pour résoudre ce problème, ajoutons un nœud vide. Nous lions le Soleil et la Terre à ce nœud.</p>
+<p>Pour corriger cela, ajoutons un nœud de graphe de scène vide. Nous allons rendre le soleil et la terre
+enfants de ce nœud.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const solarSystem = new THREE.Object3D();
 +scene.add(solarSystem);
 +objects.push(solarSystem);
@@ -139,18 +179,22 @@ earthMesh.position.x = 10;
 +solarSystem.add(earthMesh);
 objects.push(earthMesh);
 </pre>
-<p>Ici, nous avons créé un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Comme une <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, c'est aussi un nœud, mais contrairement à une <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, il n'a ni matériau ni géométrie. Il ne représente qu'un espace local.</p>
-<p>Notre nouvelle scène ressemble à ceci :</p>
+<p>Ici, nous avons créé un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Comme un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, c'est aussi un nœud dans le graphe de scène,
+mais contrairement à un <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, il n'a ni matériau ni géométrie. Il représente simplement un espace local.</p>
+<p>Notre nouveau graphe de scène ressemble à ceci</p>
 <p><img src="../resources/images/scenegraph-sun-earth-fixed.svg" align="center"></p>
-<p>La  <code class="notranslate" translate="no">sunMesh</code> et la <code class="notranslate" translate="no">earthMesh</code> sont tous les deux des enfants de <code class="notranslate" translate="no">solarSystem</code>. Les trois sont en train de tourner, et comme <code class="notranslate" translate="no">earthMesh</code> n'est pas un enfant de <code class="notranslate" translate="no">sunMesh</code>, elle n'est plus mise à l'échelle.</p>
+<p>Le <code class="notranslate" translate="no">sunMesh</code> et le <code class="notranslate" translate="no">earthMesh</code> sont tous deux enfants du <code class="notranslate" translate="no">solarSystem</code>. Les 3
+tournent et maintenant, parce que le <code class="notranslate" translate="no">earthMesh</code> n'est pas un enfant du <code class="notranslate" translate="no">sunMesh</code>,
+il n'est plus mis à l'échelle par 5x.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth-orbit-fixed.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit-fixed.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit-fixed.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Encore mieux. La Terre est plus petite que le Soleil, elle tourne autour de lui et sur elle-même.</p>
-<p>Sur le même schéma, ajoutons une Lune.</p>
+<p>Bien mieux. La Terre est plus petite que le soleil et elle tourne autour du soleil
+tout en tournant sur elle-même.</p>
+<p>En continuant sur ce même schéma, ajoutons une lune.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const earthOrbit = new THREE.Object3D();
 +earthOrbit.position.x = 10;
 +solarSystem.add(earthOrbit);
@@ -158,7 +202,7 @@ objects.push(earthMesh);
 
 const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
 const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
--earthMesh.position.x = 10; // note that this offset is already set in its parent's THREE.Object3D object "earthOrbit"
+-earthMesh.position.x = 10; // notez que ce décalage est déjà défini dans l'objet THREE.Object3D parent "earthOrbit"
 -solarSystem.add(earthMesh);
 +earthOrbit.add(earthMesh);
 objects.push(earthMesh);
@@ -173,25 +217,28 @@ objects.push(earthMesh);
 +moonOrbit.add(moonMesh);
 +objects.push(moonMesh);
 </pre>
-<p>Ajoutons à nouveau d'autres nœuds à notre scène. D'abord, un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">earthOrbit</code>
-ensuite ajoutons-lui un <code class="notranslate" translate="no">earthMesh</code> et un <code class="notranslate" translate="no">moonOrbit</code>. Finalement, ajoutons un <code class="notranslate" translate="no">moonMesh</code>
-au <code class="notranslate" translate="no">moonOrbit</code>. Notre scène devrait ressembler à ceci :</p>
+<p>Encore une fois, nous avons ajouté d'autres nœuds de graphe de scène invisibles. Le premier, un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">earthOrbit</code>
+et auquel nous avons ajouté le <code class="notranslate" translate="no">earthMesh</code> et le <code class="notranslate" translate="no">moonOrbit</code>, également nouveau. Nous avons ensuite ajouté le <code class="notranslate" translate="no">moonMesh</code>
+au <code class="notranslate" translate="no">moonOrbit</code>. Le nouveau graphe de scène ressemble à ceci.</p>
 <p><img src="../resources/images/scenegraph-sun-earth-moon.svg" align="center"></p>
-<p>et à ça :</p>
+<p>et voici le résultat</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth-moon.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Vous pouvez voir que la Lune suit le modèle de spirographe indiqué en haut de cet article, mais nous n'avons pas eu à le calculer manuellement. Nous venons de configurer notre graphe de scène pour le faire pour nous.</p>
+<p>Vous pouvez voir que la lune suit le motif spirographe montré en haut
+de cet article, mais nous n'avons pas eu à le calculer manuellement. Nous avons simplement
+configuré notre graphe de scène pour qu'il le fasse pour nous.</p>
 <p>Il est souvent utile de dessiner quelque chose pour visualiser les nœuds dans le graphe de scène.
-Three.js dispose pour cela de Helpers.</p>
-<p>L'un d'entre eux s'appelle <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>. Il dessine trois lignes représentant les axes
-<span style="color:red">X</span>,
-<span style="color:green">Y</span>, et
-<span style="color:blue">Z</span>. Ajoutons-en un à chacun de nos nœuds. </p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// add an AxesHelper to each node
+Three.js dispose de quelques ummmm, helpers utiles pour ummm, ... aider à cela.</p>
+<p>L'un s'appelle <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>. Il dessine 3 lignes représentant les axes
+locaux <span style="color:red">X</span>,
+<span style="color:green">Y</span> et
+<span style="color:blue">Z</span>. Ajoutons-en un à chaque nœud que nous
+avons créé.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// ajouter un AxesHelper à chaque nœud
 objects.forEach((node) =&gt; {
   const axes = new THREE.AxesHelper();
   axes.material.depthTest = false;
@@ -200,20 +247,35 @@ objects.forEach((node) =&gt; {
 });
 </pre>
 <p>Dans notre cas, nous voulons que les axes apparaissent même s'ils sont à l'intérieur des sphères.
-Pour cela, nous définissons le <code class="notranslate" translate="no">depthTest</code> de material à false, pour ne pas vérifier s'ils sont dessinés derrière quelque chose. Nous définissons également leur <code class="notranslate" translate="no">renderOrder</code> sur 1 (la valeur par défaut est 0) afin qu'ils soient dessinés après toutes les sphères. Sinon, une sphère pourrait être dessinée dessus et les recouvrir.</p>
+Pour ce faire, nous réglons la propriété <code class="notranslate" translate="no">depthTest</code> de leur matériau sur false, ce qui signifie qu'ils ne
+vérifieront pas s'ils dessinent derrière quelque chose d'autre. Nous réglons également
+leur <code class="notranslate" translate="no">renderOrder</code> à 1 (la valeur par défaut est 0) afin qu'ils soient dessinés après
+toutes les sphères. Sinon, une sphère pourrait dessiner par-dessus et les masquer.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth-moon-axes.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Vous pouvez voir les axes
+<p>Nous pouvons voir les axes
 <span style="color:red">x (rouge)</span> et
-<span style="color:blue">z (bleu)</span>. Comme nous regardons vers le bas et que chacun de nos objets tourne autour de son axe y, nous ne voyons pas bien l'axe <span style="color:green">y (vert)</span>.</p>
-<p>Il peut être difficile de voir certains d'entre eux, car il y a 2 paires d'axes qui se chevauchent. Le <code class="notranslate" translate="no">sunMesh</code> et le <code class="notranslate" translate="no">solarSystem</code> sont tous les deux à la même position. De même, <code class="notranslate" translate="no">earthMesh</code> et <code class="notranslate" translate="no">earthOrbit</code> sont à la même position. Ajoutons quelques contrôles simples pour nous permettre de les activer/désactiver pour chaque nœud. Pendant que nous y sommes, ajoutons également un autre assistant appelé <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>. Il crée une grille 2D sur le plan X,Z. Par défaut, la grille est de 10x10 unités.</p>
-<p>Nous allons également utiliser <a href="https://github.com/georgealways/lil-gui">lil-gui</a>, une librairie d'interface utilisateur très populaire pour les projets Three.js. lil-gui prend un objet et un nom de propriété sur cet objet et, en fonction du type de la propriété, crée automatiquement une interface utilisateur pour manipuler cette propriété.</p>
-<p>Nous voulons créer à la fois un <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> et un <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> pour chaque nœud. Nous avons besoin d'un label pour chaque nœud, nous allons donc nous débarrasser de l'ancienne boucle et faire appel à une fonction pour ajouter les helpers pour chaque nœud.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-// add an AxesHelper to each node
+<span style="color:blue">z (bleu)</span>. Comme nous regardons
+directement vers le bas et que chacun de nos objets ne tourne que autour de son
+axe y, nous ne voyons pas beaucoup les axes <span style="color:green">y (vert)</span>.</p>
+<p>Il peut être difficile d'en voir certains car il y a 2 paires d'axes qui se chevauchent. Le <code class="notranslate" translate="no">sunMesh</code>
+et le <code class="notranslate" translate="no">solarSystem</code> sont tous deux à la même position. De même, le <code class="notranslate" translate="no">earthMesh</code> et
+le <code class="notranslate" translate="no">earthOrbit</code> sont à la même position. Ajoutons quelques contrôles simples pour nous permettre
+de les activer/désactiver pour chaque nœud.
+Tant qu'on y est, ajoutons également un autre helper appelé <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>. Il
+crée une grille 2D sur le plan XZ. Par défaut, la grille est de 10x10 unités.</p>
+<p>Nous allons également utiliser <a href="https://github.com/georgealways/lil-gui">lil-gui</a>, une bibliothèque d'interface utilisateur
+très populaire dans les projets three.js. lil-gui prend un
+objet et le nom d'une propriété sur cet objet et, en fonction du type de la propriété,
+crée automatiquement une interface utilisateur pour manipuler cette propriété.</p>
+<p>Nous voulons créer à la fois un <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> et un <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> pour chaque nœud. Nous avons besoin
+d'une étiquette pour chaque nœud, nous allons donc nous débarrasser de l'ancienne boucle et passer à l'appel
+d'une fonction pour ajouter les helpers pour chaque nœud</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-// ajouter un AxesHelper à chaque nœud
 -objects.forEach((node) =&gt; {
 -  const axes = new THREE.AxesHelper();
 -  axes.material.depthTest = false;
@@ -233,17 +295,26 @@ Pour cela, nous définissons le <code class="notranslate" translate="no">depthTe
 +makeAxisGrid(moonOrbit, 'moonOrbit');
 +makeAxisGrid(moonMesh, 'moonMesh');
 </pre>
-<p><code class="notranslate" translate="no">makeAxisGrid</code> crée un <code class="notranslate" translate="no">AxisGridHelper</code> qui est une classe que nous allons créer pour rendre lil-gui heureux. Comme il est dit ci-dessus, lil-gui créera automatiquement une interface utilisateur qui manipule la propriété nommée d'un objet. Cela créera une interface utilisateur différente selon le type de propriété. Nous voulons qu'il crée une case à cocher, nous devons donc spécifier une propriété bool. Mais, nous voulons que les axes et la grille apparaissent/disparaissent en fonction d'une seule propriété, nous allons en conséquence créer une classe qui a un getter et un setter pour une propriété. De cette façon, nous pouvons laisser lil-gui penser qu'il manipule une seule propriété, mais en interne, nous pouvons définir la propriété visible de <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> et <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> pour un nœud.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Activer/désactiver les axes et la grille lil-gui
-// nécessite une propriété qui renvoie un bool
-// pour décider de faire une case à cocher
-// afin que nous créions un setter et un getter pour `visible`
-// que nous pouvons dire à lil-gui de regarder.
+<p><code class="notranslate" translate="no">makeAxisGrid</code> crée un <code class="notranslate" translate="no">AxisGridHelper</code>, une classe que nous allons créer
+pour rendre lil-gui heureux. Comme indiqué ci-dessus, lil-gui
+créera automatiquement une interface utilisateur qui manipule la propriété nommée
+de certains objets. Il créera une interface utilisateur différente en fonction du type
+de propriété. Nous voulons qu'il crée une case à cocher, nous devons donc spécifier
+une propriété <code class="notranslate" translate="no">bool</code>. Mais, nous voulons que les axes et la grille
+apparaissent/disparaissent en fonction d'une seule propriété, nous allons donc créer une classe
+qui a un getter et un setter pour une propriété. De cette façon, nous pouvons laisser lil-gui
+penser qu'il manipule une seule propriété, mais en interne, nous pouvons définir
+la propriété visible à la fois de l'<a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> et du <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> pour un nœud.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Active/désactive la visibilité des axes et de la grille
+// lil-gui nécessite une propriété qui renvoie un booléen
+// pour décider de créer une case à cocher, nous créons donc un setter
+// et un getter pour `visible` que nous pouvons dire à lil-gui
+// de regarder.
 class AxisGridHelper {
   constructor(node, units = 10) {
     const axes = new THREE.AxesHelper();
     axes.material.depthTest = false;
-    axes.renderOrder = 2;  // after the grid
+    axes.renderOrder = 2;  // après la grille
     node.add(axes);
 
     const grid = new THREE.GridHelper(units, units);
@@ -265,27 +336,48 @@ class AxisGridHelper {
   }
 }
 </pre>
-<p>Une chose à noter est que nous définissons le <code class="notranslate" translate="no">renderOrder</code> de l'<a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> sur 2 et pour le <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> sur 1 afin que les axes soient dessinés après la grille. Sinon, la grille pourrait écraser les axes.</p>
+<p>Une chose à noter est que nous avons défini le <code class="notranslate" translate="no">renderOrder</code> de l'<a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>
+à 2 et celui du <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> à 1 afin que les axes soient dessinés après la grille.
+Sinon, la grille pourrait écraser les axes.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth-moon-axes-grids.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes-grids.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes-grids.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Cliquez sur <code class="notranslate" translate="no">solarSystem</code> et vous verrez que la Terre est exactement à 10 unités du centre, comme nous l'avons défini ci-dessus. Vous pouvez voir que la Terre est dans l'espace local du <code class="notranslate" translate="no">solarSystem</code>. De même, si vous cliquez sur <code class="notranslate" translate="no">earthOrbit</code>, vous verrez que la Lune est exactement à 2 unités du centre de <em>l'espace local</em> de <code class="notranslate" translate="no">earthOrbit</code>.</p>
-<p>Un autre exemple de scène. Une automobile dans un jeu simple pourrait avoir un graphe de scène comme celui-ci</p>
+<p>Activez le <code class="notranslate" translate="no">solarSystem</code> et vous verrez que la terre se trouve exactement à 10
+unités du centre, comme nous l'avons réglé ci-dessus. Vous pouvez voir comment la
+terre se trouve dans l'<em>espace local</em> du <code class="notranslate" translate="no">solarSystem</code>. De même, si vous
+activez l'<code class="notranslate" translate="no">earthOrbit</code>, vous verrez que la lune se trouve exactement à 2 unités
+du centre de l'<em>espace local</em> de l'<code class="notranslate" translate="no">earthOrbit</code>.</p>
+<p>Quelques autres exemples de graphes de scène. Une automobile dans un monde de jeu simple pourrait avoir un graphe de scène comme celui-ci</p>
 <p><img src="../resources/images/scenegraph-car.svg" align="center"></p>
-<p>Si vous déplacez la carrosserie de la voiture, toutes les roues bougeront avec elle. Si vous vouliez que le corps rebondisse séparément des roues, vous pouvez lier le corps et les roues à un nœud "cadre" qui représente le cadre de la voiture.</p>
-<p>Un autre exemple avec un humain dans un jeu vidéo.</p>
+<p>Si vous déplacez la carrosserie de la voiture, toutes les roues bougeront avec elle. Si vous vouliez que la carrosserie
+rebondisse séparément des roues, vous pourriez rendre la carrosserie et les roues enfants d'un nœud « châssis »
+qui représente le châssis de la voiture.</p>
+<p>Un autre exemple est un humain dans un monde de jeu.</p>
 <p><img src="../resources/images/scenegraph-human.svg" align="center"></p>
-<p>Vous pouvez voir que le graphique de la scène devient assez complexe pour un humain. En fait, le graphe ci-dessus est simplifié. Par exemple, vous pouvez l'étendre pour couvrir chaque doigt (au moins 28 autres nœuds) et chaque orteil (encore 28 nœuds) plus ceux pour le visage et la mâchoire, les yeux et peut-être plus.</p>
-<p>Créons une scène un peu plus complexe : nous allons créer un char d'assaut (tank) avec ses six roues et sa tourelle. Le tank suivra un chemin prédéfini. Il y aura aussi une sphère qui tourne autour du tank et ce dernier orientera sa tourelle vers cette cible.</p>
-<p>Voici le graphique de la scène. Les maillages sont colorés en vert, les <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> en bleu, les lumières en or et les caméras en violet. Une caméra n'a pas été ajoutée au graphique de la scène.</p>
+<p>Vous pouvez voir que le graphe de scène devient assez complexe pour un humain. En fait,
+le graphe de scène ci-dessus est simplifié. Par exemple, vous pourriez l'étendre
+pour couvrir chaque doigt (au moins 28 nœuds supplémentaires) et chaque orteil
+(encore 28 nœuds) plus ceux pour le visage et la mâchoire, les yeux et peut-être plus.</p>
+<p>Créons un graphe de scène semi-complexe. Nous allons faire un char. Le char aura
+6 roues et une tourelle. Le char suivra un chemin. Il y aura une sphère qui
+se déplacera et le char ciblera la sphère.</p>
+<p>Voici le graphe de scène. Les maillages sont colorés en vert, les <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> en bleu,
+les lumières en or et les caméras en violet. Une caméra n'a pas été ajoutée
+au graphe de scène.</p>
 <div class="threejs_center"><img src="../resources/images/scenegraph-tank.svg" style="width: 800px;"></div>
 
-<p>Regardez dans le code pour voir la configuration de tous ces nœuds.</p>
-<p>Pour la cible, la sphère que le char vise, il y a une <code class="notranslate" translate="no">targetOrbit</code> (Object3D) qui tourne de la même manière que la <code class="notranslate" translate="no">earthOrbit</code> ci-dessus. Une <code class="notranslate" translate="no">targetElevation</code> (Object3D) qui est un enfant de <code class="notranslate" translate="no">targetOrbit</code> fournit un décalage par rapport à <code class="notranslate" translate="no">targetOrbit</code> et une élévation de base. Un autre <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">targetBob</code> qui monte et descend par rapport à la <code class="notranslate" translate="no">targetElevation</code>. Enfin, il y a le <code class="notranslate" translate="no">targetMesh</code> qui est seulement un cube que nous faisons pivoter et changeons ses couleurs.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// mettre en mouvement la cible
+<p>Regardez le code pour voir la configuration de tous ces nœuds.</p>
+<p>Pour la cible, la chose que le char vise, il y a un <code class="notranslate" translate="no">targetOrbit</code>
+(<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>) qui tourne simplement comme l'<code class="notranslate" translate="no">earthOrbit</code> ci-dessus. Un
+<code class="notranslate" translate="no">targetElevation</code> (<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>), qui est un enfant du <code class="notranslate" translate="no">targetOrbit</code>, fournit un
+décalage par rapport au <code class="notranslate" translate="no">targetOrbit</code> et une élévation de base. Un autre
+<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">targetBob</code> est enfant de celui-ci et il monte et descend simplement
+par rapport au <code class="notranslate" translate="no">targetElevation</code>. Enfin, il y a le <code class="notranslate" translate="no">targetMesh</code> qui est juste un cube que nous
+faisons pivoter et dont nous changeons les couleurs</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// déplacer la cible
 targetOrbit.rotation.y = time * .27;
 targetBob.position.y = Math.sin(time * 2) * 4;
 targetMesh.rotation.x = time * 7;
@@ -293,67 +385,82 @@ targetMesh.rotation.y = time * 13;
 targetMaterial.emissive.setHSL(time * 10 % 1, 1, .25);
 targetMaterial.color.setHSL(time * 10 % 1, 1, .25);
 </pre>
-<p>Pour le char, il y a un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">tank</code> qui est utilisé pour déplacer tout ce qui se trouve en dessous. Le code utilise une <a href="/docs/#api/en/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a> à laquelle il peut demander des positions le long de cette courbe. 0.0 est le début de la courbe. 1,0 est la fin de la courbe. Il demande la position actuelle où il met le tank. Il demande ensuite une position légèrement plus bas dans la courbe et l'utilise pour pointer le tank dans cette direction à l'aide de <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>.</p>
+<p>Pour le char, il y a un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> appelé <code class="notranslate" translate="no">tank</code> qui est utilisé pour déplacer tout ce qui se
+trouve en dessous. Le code utilise une <a href="/docs/#api/en/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a> à laquelle il peut demander les positions
+le long de cette courbe. 0.0 est le début de la courbe. 1.0 est la fin de la courbe. Il
+demande la position actuelle où il place le char. Il demande ensuite une
+position légèrement plus loin le long de la courbe et l'utilise pour orienter le char dans cette
+direction en utilisant <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tankPosition = new THREE.Vector2();
 const tankTarget = new THREE.Vector2();
 
 ...
 
-// Mettre en mouvement le char
+// déplacer le char
 const tankTime = time * .05;
 curve.getPointAt(tankTime % 1, tankPosition);
 curve.getPointAt((tankTime + 0.01) % 1, tankTarget);
 tank.position.set(tankPosition.x, 0, tankPosition.y);
 tank.lookAt(tankTarget.x, 0, tankTarget.y);
 </pre>
-<p>La tourelle sur le dessus du char est déplacée automatiquement en tant qu'enfant du char. Pour le pointer sur la cible, nous demandons simplement la position de la cible, puis utilisons à nouveau <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>.</p>
+<p>La tourelle sur le dessus du char est déplacée automatiquement en étant un enfant
+du char. Pour la pointer vers la cible, nous demandons simplement la position mondiale de la cible
+et ensuite utilisons à nouveau <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const targetPosition = new THREE.Vector3();
 
 ...
 
-// Tourelle face à la cible
+// orienter la tourelle vers la cible
 targetMesh.getWorldPosition(targetPosition);
 turretPivot.lookAt(targetPosition);
 </pre>
-<p>Il y a une <code class="notranslate" translate="no">tourretCamera</code> qui est un enfant de <code class="notranslate" translate="no">turretMesh</code> donc il se déplacera de haut en bas et tournera avec la tourelle. On la fait viser la cible.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// la turretCamera regarde la cible
+<p>Il y a une <code class="notranslate" translate="no">turretCamera</code> qui est un enfant du <code class="notranslate" translate="no">turretMesh</code>, donc
+elle montera et descendra et tournera avec la tourelle. Nous la faisons
+viser la cible.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// faire pointer la turretCamera vers la cible
 turretCamera.lookAt(targetPosition);
 </pre>
-<p>Il y a aussi un <code class="notranslate" translate="no">targetCameraPivot</code> qui est un enfant de <code class="notranslate" translate="no">targetBob</code> donc il flotte
-autour de la cible. Nous le pointons vers le char. Son but est de permettre à la
-<code class="notranslate" translate="no">targetCamera</code> d'être décalée par rapport à la cible elle-même. Si nous faisions de la caméra
-un enfant de <code class="notranslate" translate="no">targetBob</code>, elle serait à l'intérieur de la cible.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// faire en sorte que la targetCameraPivot regarde le char
+<p>Il y a aussi un <code class="notranslate" translate="no">targetCameraPivot</code> qui est un enfant du <code class="notranslate" translate="no">targetBob</code>, donc il flotte
+autour de la cible. Nous le faisons viser le char. Son but est de permettre à la
+<code class="notranslate" translate="no">targetCamera</code> d'être décalée par rapport à la cible elle-même. Si nous avions fait de la caméra
+un enfant du <code class="notranslate" translate="no">targetBob</code> et l'avions simplement fait pointer, elle serait à l'intérieur de la
+cible.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// faire pointer le targetCameraPivot vers le char
 tank.getWorldPosition(targetPosition);
 targetCameraPivot.lookAt(targetPosition);
 </pre>
-<p>Enfin on fait tourner toutes les roues</p>
+<p>Enfin, nous faisons tourner toutes les roues</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">wheelMeshes.forEach((obj) =&gt; {
   obj.rotation.x = time * 3;
 });
 </pre>
-<p>Pour les caméras, nous avons configuré un ensemble de 4 caméras au moment de l'initialisation avec des descriptions.</p>
+<p>Pour les caméras, nous avons configuré un tableau de toutes les 4 caméras au moment de l'initialisation avec des descriptions.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameras = [
-  { cam: camera, desc: 'detached camera', },
-  { cam: turretCamera, desc: 'on turret looking at target', },
-  { cam: targetCamera, desc: 'near target looking at tank', },
-  { cam: tankCamera, desc: 'above back of tank', },
+  { cam: camera, desc: 'caméra détachée', },
+  { cam: turretCamera, desc: 'sur tourelle regardant la cible', },
+  { cam: targetCamera, desc: 'près de la cible regardant le char', },
+  { cam: tankCamera, desc: 'au-dessus de l'arrière du char', },
 ];
 
 const infoElem = document.querySelector('#info');
 </pre>
-<p>et nous parcourons chaque caméra au moment du rendu</p>
+<p>et passons en revue nos caméras au moment du rendu.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = cameras[time * .25 % cameras.length | 0];
 infoElem.textContent = camera.desc;
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-tank.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/scenegraph-tank.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/scenegraph-tank.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>J'espère que cet article vous aura donné une bonne idée du fonctionnement des graphes de scène et de la façon dont vous devez les utiliser. Créer des nœuds « Object3D » et savoir leur attacher d'autres nœuds est une étape importante dans l'utilisation d'un moteur 3D tel que Three.js., car bien souvent, on pourrait penser que des mathématiques complexes sont nécessaires pour faire bouger et pivoter quelque chose comme vous le souhaitez (comme calculer le mouvement de la Lune, savoir où calculer la position des roues de la voiture par rapport à son corps). En utilisant un graphe de scène, et comme nous l'avons vu dans cet article, le travail en est grandement simplifié.</p>
-<p>Article suivant :<a href="materials.html">Passons maintenant en revue les matériaux</a>.</p>
+<p>J'espère que cela vous donne une idée du fonctionnement des graphes de scène et de la manière dont vous pourriez les utiliser.
+Créer des nœuds <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et les rendre parents d'autres objets est une étape importante pour bien utiliser
+un moteur 3D comme three.js. Souvent, il peut sembler qu'il soit nécessaire de faire des calculs mathématiques complexes
+pour faire bouger et pivoter quelque chose comme vous le souhaitez. Par exemple, sans graphe de scène,
+calculer le mouvement de la lune ou où placer les roues de la voiture par rapport à sa
+carrosserie serait très compliqué, mais en utilisant un graphe de scène, cela devient beaucoup plus facile.</p>
+<p><a href="materials.html">Ensuite, nous aborderons les matériaux</a>.</p>
 
         </div>
       </div>
@@ -365,4 +472,4 @@ infoElem.textContent = camera.desc;
 
 
 
-</body></html>
+</body></html>

+ 32 - 26
manual/fr/setup.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Configuration de </title>
+    <title>Configuration</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Configuration de ">
+    <meta name="twitter:title" content="Three.js – Configuration">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,48 +22,54 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Configuration de </h1>
+        <h1>Configuration</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cette article fait parti d'une série consacrée à Three.js. Le premier article traité des <a href="fundamentals.html">fondements de Three.js</a>.
-Si vous ne l'avez pas encore lu, vous devriez peut-être commencer par là.</p>
-<p>Avant d'aller plus loin, parlons du paramétrage de votre environnement de travail. Pour des raisons de sécurité
-WebGL ne peut pas utiliser des images provenant de votre disque dur. Cela signifie qu'il faille utiliser
-un serveur web. Heureusement, ils sont très facile à utiliser.</p>
-<p>Tout d'abord, si vous le souhaitez, vous pouvez télécharger l'intégralité de ce site depuis <a href="https://github.com/gfxfundamentals/threejsfundamentals/archive/gh-pages.zip">ce lien</a>.
-Une fois téléchargé, dézippez le dossier. </p>
-<p>Ensuite, téléchargez l'un des web serveurs suivants.</p>
-<p>Si vous en préférez un avec une interface graphique, voici <a href="https://greggman.github.io/servez">Servez</a></p>
+          <p>Cet article fait partie d'une série d'articles sur three.js.
+Le premier article portait sur <a href="fundamentals.html">les fondamentaux de three.js</a>.
+Si vous ne l'avez pas encore lu, vous pourriez vouloir commencer par là.</p>
+<p>Avant d'aller plus loin, nous devons parler de la configuration de votre
+ordinateur en tant qu'environnement de développement. En particulier, pour des raisons de sécurité,
+WebGL ne peut pas utiliser directement les images de votre disque dur. Cela signifie
+que pour développer, vous devez utiliser un serveur web. Heureusement,
+les serveurs web de développement sont très faciles à configurer et à utiliser.</p>
+<p>Tout d'abord, si vous le souhaitez, vous pouvez télécharger l'intégralité de ce site à partir de <a href="https://github.com/gfxfundamentals/threejsfundamentals/archive/gh-pages.zip">ce lien</a>.
+Une fois téléchargé, double-cliquez sur le fichier zip pour décompresser les fichiers.</p>
+<p>Ensuite, téléchargez l'un de ces serveurs web simples.</p>
+<p>Si vous préférez un serveur web avec une interface utilisateur, il existe
+<a href="https://greggman.github.io/servez">Servez</a></p>
 <p></p><div class="threejs_image border">
   <img class="" src="../resources/servez.gif">
 </div>
 
 <p></p>
-<p>Pointez-le simplement sur le dossier où vous avez décompressé les fichiers, cliquez sur "Démarrer", puis accédez-y dans votre navigateur à l'adresse suivante <a href="http://localhost:8080/"><code class="notranslate" translate="no">http://localhost:8080/</code></a> ou si vous voulez souhaitez parcourir les exemples, accédez à <a href="http://localhost:8080/threejs"><code class="notranslate" translate="no">http://localhost:8080/threejs</code></a>.</p>
-<p>Pour arrêter le serveur, cliquez sur stop ou quittez Servez.</p>
-<p>Si vous préférez la ligne de commande, une autre façon consiste à utiliser <a href="https://nodejs.org">node.js</a>.
-Téléchargez-le, installez-le, puis ouvrez une fenêtre d'invite de commande / console / terminal. Si vous êtes sous Windows, le programme d'installation ajoutera une "Invite de commande de nœud" spéciale, alors utilisez-la.</p>
-<p>Ensuite installez <a href="https://github.com/greggman/servez-cli"><code class="notranslate" translate="no">servez</code></a> avec ces commandes</p>
+<p>Il suffit de le pointer vers le dossier où vous avez décompressé les fichiers, cliquez sur "Démarrer", puis rendez-vous
+dans votre navigateur à l'adresse <a href="http://localhost:8080/"><code class="notranslate" translate="no">http://localhost:8080/</code></a> ou si vous souhaitez
+parcourir les exemples, allez à l'adresse <a href="http://localhost:8080/threejs"><code class="notranslate" translate="no">http://localhost:8080/threejs</code></a>.</p>
+<p>Pour arrêter le service, cliquez sur stop ou quittez Servez.</p>
+<p>Si vous préférez la ligne de commande (c'est mon cas), une autre solution est d'utiliser <a href="https://nodejs.org">node.js</a>.
+Téléchargez-le, installez-le, puis ouvrez une fenêtre d'invite de commandes / console / terminal. Si vous êtes sous Windows, le programme d'installation ajoutera une "Invite de commandes Node" spéciale, utilisez donc celle-ci.</p>
+<p>Ensuite, installez <a href="https://github.com/greggman/servez-cli"><code class="notranslate" translate="no">servez</code></a> en tapant</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">npm -g install servez
-</pre><p>Ou si vous êtes sous OSX</p>
+</pre><p>Si vous êtes sur OSX, utilisez</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">sudo npm -g install servez
-</pre><p>Une fois que c'est fait, tapez cette commande</p>
+</pre><p>Une fois que vous avez fait cela, tapez</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">servez path/to/folder/where/you/unzipped/files
 </pre><p>Ou si vous êtes comme moi</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">cd path/to/folder/where/you/unzipped/files
 servez
-</pre><p>Il devrait imprimer quelque chose comme</p>
+</pre><p>Il devrait afficher quelque chose comme</p>
 <p></p><div class="threejs_image ">
   <img class="" src="../resources/servez-response.png">
 </div>
 
 <p></p>
-<p>Ensuite, ouvrez <a href="http://localhost:8080/"><code class="notranslate" translate="no">http://localhost:8080/</code></a> dans votre navigateur.</p>
-<p>Si vous ne spécifiez pas de chemin, Servez choisira le dossier courant.</p>
-<p>Si ces options ne vous conviennent pas, vous pouvez choisir
-<a href="https://stackoverflow.com/questions/12905426/what-is-a-faster-alternative-to-pythons-servez-or-simplehttpserver">d'autres alternatives</a>.</p>
-<p>Maintenant que vous avez un serveur configuré, nous pouvons passer aux <a href="textures.html">textures</a>.</p>
+<p>Puis, dans votre navigateur, allez à l'adresse <a href="http://localhost:8080/"><code class="notranslate" translate="no">http://localhost:8080/</code></a>.</p>
+<p>Si vous ne spécifiez pas de chemin, servez servira le dossier actuel.</p>
+<p>Si aucune de ces options ne vous convient,
+<a href="https://stackoverflow.com/questions/12905426/what-is-a-faster-alternative-to-pythons-servez-or-simplehttpserver">il existe de nombreux autres serveurs simples parmi lesquels choisir</a>.</p>
+<p>Maintenant que vous avez configuré un serveur, nous pouvons passer aux <a href="textures.html">textures</a>.</p>
 
         </div>
       </div>
@@ -75,4 +81,4 @@ servez
 
 
 
-</body></html>
+</body></html>

+ 285 - 5
manual/fr/shadertoy.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Three.js and Shadertoy</title>
+    <title>Three.js et Shadertoy</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – and Shadertoy">
+    <meta name="twitter:title" content="Three.js – et Shadertoy">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,292 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Three.js and Shadertoy</h1>
+        <h1>Three.js et Shadertoy</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/shadertoy.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p><a href="https://shadertoy.com">Shadertoy</a> est un site web célèbre qui héberge des expériences de shaders incroyables. Les gens demandent souvent comment ils peuvent utiliser ces shaders avec Three.js.</p>
+<p>Il est important de reconnaître que cela s'appelle Shader<strong>TOY</strong> pour une raison. En général, les shaders shadertoy ne sont pas axés sur les meilleures pratiques. Il s'agit plutôt d'un défi amusant, similaire à dwitter (écrire du code en 140 caractères) ou js13kGames (faire un jeu en 13k ou moins).</p>
+<p>Dans le cas de Shadertoy, le défi est : <em>écrire une fonction qui, pour une position de pixel donnée, produit une couleur qui dessine quelque chose d'intéressant</em>. C'est un défi amusant et de nombreux résultats sont incroyables. Mais ce n'est pas la meilleure pratique.</p>
+<p>Comparez <a href="https://www.shadertoy.com/view/XtsSWs">ce shader shadertoy incroyable qui dessine une ville entière</a></p>
+<div class="threejs_center"><img src="../resources/images/shadertoy-skyline.png"></div>
+
+<p>En plein écran sur mon GPU, il tourne à environ 5 images par seconde. Comparez cela à <a href="https://store.steampowered.com/app/255710/Cities_Skylines/">un jeu comme Cities: Skylines</a></p>
+<div class="threejs_center"><img src="../resources/images/cities-skylines.jpg" style="width: 600px;"></div>
+
+<p>Ce jeu tourne à 30-60 images par seconde sur la même machine car il utilise des techniques plus traditionnelles, dessinant des bâtiments faits de triangles avec des textures, etc...</p>
+<p>Néanmoins, passons en revue l'utilisation d'un shader Shadertoy avec three.js.</p>
+<p>C'est le shader shadertoy par défaut si vous <a href="https://www.shadertoy.com/new">choisissez "New" sur shadertoy.com</a>, du moins en janvier 2019.</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">// Par iq: https://www.shadertoy.com/user/iq
+// licence: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
+void mainImage( out vec4 fragColor, in vec2 fragCoord )
+{
+    // Coordonnées normalisées des pixels (de 0 à 1)
+    vec2 uv = fragCoord/iResolution.xy;
+
+    // Couleur variable des pixels avec le temps
+    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
+
+    // Sortie à l'écran
+    fragColor = vec4(col,1.0);
+}
+</pre>
+<p>Une chose importante à comprendre à propos des shaders est qu'ils sont écrits dans un langage appelé GLSL (Graphics Library Shading Language) conçu pour les mathématiques 3D qui inclut des types spéciaux. Ci-dessus, nous voyons <code class="notranslate" translate="no">vec4</code>, <code class="notranslate" translate="no">vec2</code>, <code class="notranslate" translate="no">vec3</code> comme 3 de ces types spéciaux. Un <code class="notranslate" translate="no">vec2</code> a 2 valeurs, un <code class="notranslate" translate="no">vec3</code> 3, un <code class="notranslate" translate="no">vec4</code> 4 valeurs. Ils peuvent être adressés de plusieurs manières. Les manières les plus courantes sont avec <code class="notranslate" translate="no">x</code>, <code class="notranslate" translate="no">y</code>, <code class="notranslate" translate="no">z</code> et <code class="notranslate" translate="no">w</code> comme dans</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">vec4 v1 = vec4(1.0, 2.0, 3.0, 4.0);
+float v2 = v1.x + v1.y;  // adds 1.0 + 2.0
+</pre>
+<p>Contrairement à JavaScript, GLSL ressemble plus à C/C++ où les variables doivent avoir leur type déclaré, donc au lieu de <code class="notranslate" translate="no">var v = 1.2;</code> c'est <code class="notranslate" translate="no">float v = 1.2;</code> déclarant <code class="notranslate" translate="no">v</code> comme un nombre à virgule flottante.</p>
+<p>Expliquer GLSL en détail dépasse le cadre de cet article. Pour un aperçu rapide, consultez <a href="https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html">cet article</a> et peut-être poursuivez avec <a href="https://thebookofshaders.com/">cette série</a>.</p>
+<p>Il convient de noter que, du moins en janvier 2019, <a href="https://shadertoy.com">shadertoy.com</a> ne s'occupe que des <em>fragment shaders</em>. La responsabilité d'un fragment shader est, étant donné une position de pixel, de produire une couleur pour ce pixel.</p>
+<p>En regardant la fonction ci-dessus, nous pouvons voir que le shader a un paramètre <code class="notranslate" translate="no">out</code> appelé <code class="notranslate" translate="no">fragColor</code>. <code class="notranslate" translate="no">out</code> signifie <code class="notranslate" translate="no">output</code> (sortie). C'est un paramètre pour lequel la fonction est censée fournir une valeur. Nous devons le définir à une certaine couleur.</p>
+<p>Il a également un paramètre <code class="notranslate" translate="no">in</code> (pour input, entrée) appelé <code class="notranslate" translate="no">fragCoord</code>. C'est la coordonnée du pixel qui est sur le point d'être dessinée. Nous pouvons utiliser cette coordonnée pour décider d'une couleur. Si le canevas sur lequel nous dessinons est de 400x300 pixels, alors la fonction sera appelée 400x300 fois, soit 120 000 fois. À chaque fois, <code class="notranslate" translate="no">fragCoord</code> sera une coordonnée de pixel différente.</p>
+<p>Il y a 2 autres variables utilisées qui ne sont pas définies dans le code. L'une est <code class="notranslate" translate="no">iResolution</code>. Elle est définie à la résolution du canevas. Si le canevas est de 400x300, alors <code class="notranslate" translate="no">iResolution</code> serait 400,300, donc au fur et à mesure que les coordonnées des pixels changent, cela fait passer <code class="notranslate" translate="no">uv</code> de 0.0 à 1.0 en travers et vers le haut de la texture. Travailler avec des valeurs <em>normalisées</em> rend souvent les choses plus faciles, c'est pourquoi la majorité des shaders shadertoy commencent par quelque chose comme ça.</p>
+<p>L'autre variable non définie dans le shader est <code class="notranslate" translate="no">iTime</code>. C'est le temps écoulé depuis le chargement de la page en secondes.</p>
+<p>Dans le jargon des shaders, ces variables globales sont appelées variables <em>uniformes</em>. Elles sont appelées <em>uniformes</em> car elles ne changent pas, elles restent uniformes d'une itération du shader à la suivante. Il est important de noter qu'elles sont toutes spécifiques à shadertoy. Ce ne sont pas des variables GLSL <em>officielles</em>. Ce sont des variables que les créateurs de shadertoy ont inventées.</p>
+<p>La <a href="https://www.shadertoy.com/howto">documentation de Shadertoy en définit plusieurs autres</a>. Pour l'instant, écrivons quelque chose qui gère les deux utilisées dans le shader ci-dessus.</p>
+<p>La première chose à faire est de créer un simple plan qui remplit le canevas. Si vous ne l'avez pas encore lu, nous l'avons fait dans <a href="backgrounds.html">l'article sur les arrière-plans</a>, alors prenons cet exemple mais retirons les cubes. C'est assez court, voici donc l'intégralité :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+  renderer.autoClearColor = false;
+
+  const camera = new THREE.OrthographicCamera(
+    -1, // left
+     1, // right
+     1, // top
+    -1, // bottom
+    -1, // near,
+     1, // far
+  );
+  const scene = new THREE.Scene();
+  const plane = new THREE.PlaneGeometry(2, 2);
+  const material = new THREE.MeshBasicMaterial({
+      color: 'red',
+  });
+  scene.add(new THREE.Mesh(plane, material));
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  function render() {
+    resizeRendererToDisplaySize(renderer);
+
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</pre>
+<p>Comme <a href="backgrounds.html">expliqué dans l'article sur les arrière-plans</a>, une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> avec ces paramètres et un plan de 2 unités remplira le canevas. Pour l'instant, tout ce que nous obtiendrons est un canevas rouge car notre plan utilise un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> rouge.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadertoy-prep.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadertoy-prep.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Maintenant que nous avons quelque chose qui fonctionne, ajoutons le shader shadertoy. </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fragmentShader = `
+#include &lt;common&gt;
+
+uniform vec3 iResolution;
+uniform float iTime;
+
+// Par iq: https://www.shadertoy.com/user/iq
+// licence: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
+void mainImage( out vec4 fragColor, in vec2 fragCoord )
+{
+    // Coordonnées normalisées des pixels (de 0 à 1)
+    vec2 uv = fragCoord/iResolution.xy;
+
+    // Couleur variable des pixels avec le temps
+    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
+
+    // Sortie à l'écran
+    fragColor = vec4(col,1.0);
+}
+
+void main() {
+  mainImage(gl_FragColor, gl_FragCoord.xy);
+}
+`;
+</pre>
+<p>Ci-dessus, nous avons déclaré les 2 variables uniformes dont nous avons parlé. Ensuite, nous avons inséré le code GLSL du shader de shadertoy. Enfin, nous avons appelé <code class="notranslate" translate="no">mainImage</code> en lui passant <code class="notranslate" translate="no">gl_FragColor</code> et <code class="notranslate" translate="no">gl_FragCoord.xy</code>. <code class="notranslate" translate="no">gl_FragColor</code> est une variable globale WebGL officielle que le shader est responsable de définir à la couleur qu'il souhaite pour le pixel actuel. <code class="notranslate" translate="no">gl_FragCoord</code> est une autre variable globale WebGL officielle qui nous indique la coordonnée du pixel pour lequel nous choisissons actuellement une couleur.</p>
+<p>Nous devons ensuite configurer les uniformes de three.js afin de pouvoir fournir des valeurs au shader.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const uniforms = {
+  iTime: { value: 0 },
+  iResolution:  { value: new THREE.Vector3() },
+};
+</pre>
+<p>Chaque uniforme dans THREE.js a un paramètre <code class="notranslate" translate="no">value</code>. Cette valeur doit correspondre au type de l'uniforme.</p>
+<p>Ensuite, nous passons le fragment shader et les uniformes à un <a href="/docs/#api/en/materials/ShaderMaterial"><code class="notranslate" translate="no">ShaderMaterial</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({
+-    color: 'red',
+-});
++const material = new THREE.ShaderMaterial({
++  fragmentShader,
++  uniforms,
++});
+</pre>
+<p>et avant de rendre, nous devons définir les valeurs des uniformes</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function render() {
++function render(time) {
++  time *= 0.001;  // convertir en secondes
+
+  resizeRendererToDisplaySize(renderer);
+
++  const canvas = renderer.domElement;
++  uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
++  uniforms.iTime.value = time;
+
+  renderer.render(scene, camera);
+
+  requestAnimationFrame(render);
+}
+</pre>
+<blockquote>
+<p>Note : Je n'ai aucune idée pourquoi <code class="notranslate" translate="no">iResolution</code> est un <code class="notranslate" translate="no">vec3</code> et ce que contient la 3ème valeur <a href="https://www.shadertoy.com/howto">n'est pas documenté sur shadertoy.com</a>. Elle n'est pas utilisée ci-dessus, donc je la définis juste à 1 pour l'instant. ¯\_(ツ)_/¯</p>
+</blockquote>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadertoy-basic.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadertoy-basic.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela <a href="https://www.shadertoy.com/new">correspond à ce que nous voyons sur Shadertoy pour un nouveau shader</a>, du moins en janvier 2019 😉. Que fait le shader ci-dessus ? </p>
+<ul>
+<li><code class="notranslate" translate="no">uv</code> va de 0 à 1. </li>
+<li><code class="notranslate" translate="no">cos(uv.xyx)</code> nous donne 3 valeurs de cosinus sous forme de <code class="notranslate" translate="no">vec3</code>. Une pour <code class="notranslate" translate="no">uv.x</code>, une autre pour <code class="notranslate" translate="no">uv.y</code> et une autre pour <code class="notranslate" translate="no">uv.x</code> à nouveau.</li>
+<li>L'ajout du temps, <code class="notranslate" translate="no">cos(iTime+uv.xyx)</code>, les rend animés.</li>
+<li>L'ajout de <code class="notranslate" translate="no">vec3(0,2,4)</code> comme dans <code class="notranslate" translate="no">cos(iTime+uv.xyx+vec3(0,2,4))</code> décale les ondes cosinusoïdales</li>
+<li><code class="notranslate" translate="no">cos</code> va de -1 à 1, donc <code class="notranslate" translate="no">0.5 * 0.5 + cos(...)</code> convertit de -1 &lt;-&gt; 1 à 0.0 &lt;-&gt; 1.0</li>
+<li>les résultats sont ensuite utilisés comme couleur RVB pour le pixel actuel</li>
+</ul>
+<p>Un petit changement facilitera la visualisation des ondes cosinusoïdales. Actuellement, <code class="notranslate" translate="no">uv</code> ne va que de 0 à 1. Un cosinus se répète à 2π, alors faisons-le aller de 0 à 40 en multipliant par 40.0. Cela devrait le faire se répéter environ 6,3 fois.</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">-vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
++vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx*40.0+vec3(0,2,4));
+</pre>
+<p>En comptant ci-dessous, je vois environ 6,3 répétitions. Nous pouvons voir le bleu entre le rouge car il est décalé de 4 via le <code class="notranslate" translate="no">+vec3(0,2,4)</code>. Sans cela, le bleu et le rouge se chevaucheraient parfaitement, créant du violet.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadertoy-basic-x40.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadertoy-basic-x40.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Savoir à quel point les entrées sont simples et ensuite voir des résultats comme <a href="https://www.shadertoy.com/view/MdXGW2">un canal urbain</a>, <a href="https://www.shadertoy.com/view/4ttSWf">une forêt</a>, <a href="https://www.shadertoy.com/view/ld3Gz2">un escargot</a>, <a href="https://www.shadertoy.com/view/4tBXR1">un champignon</a> rend le défi d'autant plus impressionnant. Espérons qu'ils expliquent également clairement pourquoi ce n'est généralement pas la bonne approche par rapport aux méthodes plus traditionnelles de création de scènes à partir de triangles. Le fait qu'il faille faire autant de calculs pour déterminer la couleur de chaque pixel signifie que ces exemples tournent très lentement.</p>
+<p>Certains shaders shadertoy prennent des textures en entrée, comme <a href="https://www.shadertoy.com/view/MsXSzM">celui-ci</a>. </p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">// Par Daedelus: https://www.shadertoy.com/user/Daedelus
+// licence: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
+#define TIMESCALE 0.25
+#define TILES 8
+#define COLOR 0.7, 1.6, 2.8
+
+void mainImage( out vec4 fragColor, in vec2 fragCoord )
+{
+    vec2 uv = fragCoord.xy / iResolution.xy;
+    uv.x *= iResolution.x / iResolution.y;
+
+    vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES));
+    float p = 1.0 - mod(noise.r + noise.g + noise.b + iTime * float(TIMESCALE), 1.0);
+    p = min(max(p * 3.0 - 1.8, 0.1), 2.0);
+
+    vec2 r = mod(uv * float(TILES), 1.0);
+    r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0));
+    p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0);
+
+    fragColor = vec4(COLOR, 1.0) * p;
+}
+</pre>
+<p>Passer une texture à un shader est similaire à <a href="textures.html">en passer une à un matériau normal</a>, mais nous devons configurer la texture sur les uniformes.</p>
+<p>Tout d'abord, ajoutons l'uniforme pour la texture au shader. Ils sont appelés <code class="notranslate" translate="no">sampler2D</code> en GLSL.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fragmentShader = `
+#include &lt;common&gt;
+
+uniform vec3 iResolution;
+uniform float iTime;
++uniform sampler2D iChannel0;
+
+...
+</pre>
+<p>Ensuite, nous pouvons charger une texture comme nous l'avons vu <a href="textures.html">ici</a> et affecter la valeur de l'uniforme.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
++const texture = loader.load('resources/images/bayer.png');
++texture.minFilter = THREE.NearestFilter;
++texture.magFilter = THREE.NearestFilter;
++texture.wrapS = THREE.RepeatWrapping;
++texture.wrapT = THREE.RepeatWrapping;
+const uniforms = {
+  iTime: { value: 0 },
+  iResolution:  { value: new THREE.Vector3() },
++  iChannel0: { value: texture },
+};
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadertoy-bleepy-blocks.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadertoy-bleepy-blocks.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Jusqu'à présent, nous avons utilisé les shaders Shadertoy tels qu'ils sont utilisés sur <a href="https://shadertoy.com">Shadertoy.com</a>, à savoir pour couvrir le canevas. Il n'y a cependant aucune raison de nous limiter à ce cas d'utilisation. L'important à retenir est que les fonctions que les gens écrivent sur shadertoy prennent généralement juste une entrée <code class="notranslate" translate="no">fragCoord</code> et une <code class="notranslate" translate="no">iResolution</code>. <code class="notranslate" translate="no">fragCoord</code> n'a pas à provenir des coordonnées de pixels ; nous pourrions utiliser autre chose, comme des coordonnées de texture, et les utiliser ensuite un peu comme d'autres textures. Cette technique d'utilisation d'une fonction pour générer des textures est souvent appelée une <a href="https://www.google.com/search?q=procedural+texture"><em>texture procédurale</em></a>.</p>
+<p>Modifions le shader ci-dessus pour faire cela. La chose la plus simple à faire pourrait être de prendre les coordonnées de texture que three.js fournit normalement, de les multiplier par <code class="notranslate" translate="no">iResolution</code> et de les passer pour <code class="notranslate" translate="no">fragCoords</code>.</p>
+<p>Pour ce faire, nous ajoutons un <em>varying</em>. Un varying est une valeur passée du vertex shader au fragment shader qui est interpolée (ou varie) entre les sommets. Pour l'utiliser dans notre fragment shader, nous la déclarons. Three.js fait référence à ses coordonnées de texture comme <code class="notranslate" translate="no">uv</code> avec le <code class="notranslate" translate="no">v</code> devant signifiant <em>varying</em> (variable).</p>
+<pre class="prettyprint showlinemods notranslate lang-glsl" translate="no">...
+
++varying vec2 vUv;
+
+void main() {
+-  mainImage(gl_FragColor, gl_FragCoord.xy);
++  mainImage(gl_FragColor, vUv * iResolution.xy);
+}
+</pre>
+<p>Ensuite, nous devons également fournir notre propre vertex shader. Voici un vertex shader three.js minimal assez courant. Three.js déclare et fournira des valeurs pour <code class="notranslate" translate="no">uv</code>, <code class="notranslate" translate="no">projectionMatrix</code>, <code class="notranslate" translate="no">modelViewMatrix</code> et <code class="notranslate" translate="no">position</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertexShader = `
+  varying vec2 vUv;
+  void main() {
+    vUv = uv;
+    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+  }
+`;
+</pre>
+<p>Nous devons passer le vertex shader au <a href="/docs/#api/en/materials/ShaderMaterial"><code class="notranslate" translate="no">ShaderMaterial</code></a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.ShaderMaterial({
+  vertexShader,
+  fragmentShader,
+  uniforms,
+});
+</pre>
+<p>Nous pouvons définir la valeur de l'uniforme <code class="notranslate" translate="no">iResolution</code> au moment de l'initialisation car elle ne changera plus.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const uniforms = {
+  iTime: { value: 0 },
+-  iResolution:  { value: new THREE.Vector3() },
++  iResolution:  { value: new THREE.Vector3(1, 1, 1) },
+  iChannel0: { value: texture },
+};
+</pre>
+<p>et nous n'avons plus besoin de la définir au moment du rendu</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const canvas = renderer.domElement;
+-uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
+uniforms.iTime.value = time;
+</pre>
+<p>Sinon, j'ai copié à nouveau la caméra originale et le code qui configure 3 cubes en rotation de <a href="responsive.html">l'article sur la réactivité</a>. Le résultat :</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadertoy-as-texture.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadertoy-as-texture.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que cela vous donnera au moins une base sur la façon d'utiliser un shader shadertoy avec three.js. Encore une fois, il est important de se rappeler que la plupart des shaders shadertoy sont un défi intéressant (tout dessiner avec une seule fonction) plutôt que la méthode recommandée pour réellement afficher des choses de manière performante. Néanmoins, ils sont incroyables, impressionnants, beaux, et vous pouvez apprendre énormément en voyant comment ils fonctionnent.</p>
 
         </div>
       </div>

+ 197 - 109
manual/fr/shadows.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Les ombres</title>
+    <title>Ombres</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Les ombres">
+    <meta name="twitter:title" content="Three.js – Ombres">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,30 +22,52 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Les ombres</h1>
+        <h1>Ombres</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là.
-L'<a href="cameras.html">article précédent qui s'intéressait aux caméras</a> ainsi que <a href="lights.html">celui à propos des lumières</a> sont à lire avant d'entamer celui-ci.</p>
-<p>Les ombres peuvent être un sujet compliqué. Il existe différentes solutions et toutes ont des compromis, y compris les solutions disponibles dans Three.js.</p>
-<p>Three.js, par défaut, utilise des <em>shadow maps</em>. Comment ça marche ? <em>pour chaque lumière qui projette des ombres, tous les objets marqués pour projeter des ombres sont rendus du point de vue de la lumière</em>. <strong>RELISEZ ENCORE UNE FOIS</strong> pour que ça soit bien clair pour vous.</p>
-<p>En d'autres termes, si vous avez 20 objets et 5 lumières, et que les 20 objets projettent des ombres et que les 5 lumières projettent des ombres, toute votre scène sera dessinée 6 fois. Les 20 objets seront dessinés pour la lumière #1, puis les 20 objets seront dessinés pour la lumière #2, puis #3, et ainsi de suite. Enfin la scène sera dessinée en utilisant les données des 5 premiers rendus.</p>
-<p>C'est pire, si vous avez une 'pointLight' projetant des ombres, la scène devra être dessinée 6 fois juste pour cette lumière !</p>
-<p>Pour ces raisons, il est courant de trouver d'autres solutions que d'avoir un tas de lumières générant toutes des ombres. Une solution courante consiste à avoir plusieurs lumières mais une seule lumière directionnelle générant des ombres.</p>
-<p>Une autre solution consiste à utiliser des lightmaps et/ou des maps d'occlusion ambiante pour pré-calculer les effets de l'éclairage hors ligne. Cela se traduit par un éclairage statique ou des soupçons d'éclairage statique, mais au moins c'est rapide. Nous verrons cela dans un autre article.</p>
-<p>Une autre solution consiste à utiliser de fausses ombres. Créez un plan, placez une texture en niveaux de gris dans le plan qui se rapproche d'une ombre, dessinez-la au-dessus du sol sous votre objet.</p>
-<p>Par exemple, utilisons cette texture comme une fausse ombre.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js. Le
+premier article est <a href="fundamentals.html">les fondamentaux de three.js</a>. Si
+vous ne l'avez pas encore lu et que vous débutez avec three.js, vous pourriez envisager de
+commencer par là. L'<a href="cameras.html">article précédent portait sur les caméras</a>, ce qui est
+important à lire avant de lire cet article, tout comme
+l'<a href="lights.html">article précédent sur les lumières</a>.</p>
+<p>Les ombres sur ordinateur peuvent être un sujet complexe. Il existe diverses
+solutions et toutes impliquent des compromis, y compris les solutions
+disponibles dans three.js.</p>
+<p>Three.js utilise par défaut des <em>cartes d'ombres</em>. Le fonctionnement d'une carte d'ombres
+est le suivant : <em>pour chaque lumière qui projette des ombres, tous les objets marqués pour projeter
+des ombres sont rendus du point de vue de la lumière</em>. **LISEZ CELA
+À NOUVEAU !** et laissez-le s'imprégner.</p>
+<p>En d'autres termes, si vous avez 20 objets et 5 lumières, et que
+les 20 objets projettent des ombres et les 5 lumières projettent
+des ombres, alors toute votre scène sera dessinée 6 fois. Les 20 objets
+seront dessinés pour la lumière n°1, puis les 20 objets seront dessinés pour
+la lumière n°2, puis n°3, etc., et enfin la scène réelle sera dessinée
+en utilisant les données des 5 premiers rendus.</p>
+<p>Pire encore, si vous avez une lumière ponctuelle (point light) qui projette des ombres, la scène
+doit être dessinée 6 fois juste pour cette lumière !</p>
+<p>Pour ces raisons, il est courant de trouver d'autres solutions plutôt que d'avoir
+un tas de lumières générant toutes des ombres. Une solution courante
+consiste à avoir plusieurs lumières mais seulement une lumière directionnelle (directional light) générant
+des ombres.</p>
+<p>Une autre solution consiste à utiliser des lightmaps (cartes d'éclairage) et/ou des ambient occlusion maps
+(cartes d'occlusion ambiante) pour pré-calculer les effets d'éclairage hors ligne. Cela se traduit par un éclairage statique
+ou des indices d'éclairage statique, mais au moins c'est rapide. Nous
+aborderons ces deux points dans un autre article.</p>
+<p>Une autre solution consiste à utiliser de fausses ombres. Créez un plan, placez une texture en niveaux de gris
+sur le plan qui approxime une ombre,
+dessinez-le au-dessus du sol, sous votre objet.</p>
+<p>Par exemple, utilisons cette texture comme fausse ombre</p>
 <div class="threejs_center"><img src="../examples/resources/images/roundshadow.png"></div>
 
-<p>Utilisons une partie du code de <a href="cameras.html">l'article précédent</a>.</p>
-<p>Réglons la couleur de fond sur blanc.</p>
+<p>Nous utiliserons une partie du code de l'<a href="cameras.html">article précédent</a>.</p>
+<p>Définissons la couleur de fond sur blanc.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
 +scene.background = new THREE.Color('white');
 </pre>
-<p>Ensuite, nous allons configurer le même sol en damier, mais cette fois, nous utilisons un <a href="https://threejs.org/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> car nous n'avons pas besoin d'éclairage pour le sol.</p>
+<p>Ensuite, nous allons configurer le même sol en damier, mais cette fois-ci en utilisant
+un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> car nous n'avons pas besoin d'éclairage pour le sol.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
 
 {
@@ -70,70 +92,80 @@ L'<a href="cameras.html">article précédent qui s'intéressait aux caméras</a>
   scene.add(mesh);
 }
 </pre>
-<p>Notez que nous définissons la couleur sur <code class="notranslate" translate="no">1.5, 1.5, 1.5</code>. Cela multipliera les couleurs de la texture du damier par 1,5, 1,5, 1,5. Puisque les couleurs de la texture sont 0x808080 et 0xC0C0C0, c'est-à-dire gris moyen et gris clair, les multiplier par 1,5 nous donnera un damier blanc et gris clair.</p>
-<p>Chargeons la texture de l'ombre</p>
+<p>Notez que nous définissons la couleur sur <code class="notranslate" translate="no">1.5, 1.5, 1.5</code>. Cela multipliera les couleurs
+de la texture en damier par 1.5, 1.5, 1.5. Étant donné que les couleurs de la texture sont 0x808080 et 0xC0C0C0,
+ce qui correspond à un gris moyen et un gris clair, les multiplier par 1.5 nous donnera un damier
+blanc et gris clair.</p>
+<p>Chargeons la texture d'ombre</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const shadowTexture = loader.load('resources/images/roundshadow.png');
 </pre>
 <p>et créons un tableau pour mémoriser chaque sphère et les objets associés.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereShadowBases = [];
 </pre>
-<p>Ensuite, créons une sphère.</p>
+<p>Ensuite, nous allons créer une géométrie de sphère</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereRadius = 1;
 const sphereWidthDivisions = 32;
 const sphereHeightDivisions = 16;
 const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
 </pre>
-<p>Et un plan pour simuler l'ombre.</p>
+<p>Et une géométrie de plan pour la fausse ombre</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeSize = 1;
 const shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);
 </pre>
-<p>Maintenant, nous allons faire un tas de sphères. Pour chaque sphère, nous allons créer une <code class="notranslate" translate="no">base</code> <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> et nous allons créer à la fois le maillage du plan d'ombre et le maillage de la sphère en tant qu'enfants de la base. De cette façon, si nous déplaçons la base, la sphère et l'ombre bougeront. Nous devons placer l'ombre légèrement au-dessus du sol pour éviter le Z-fighting . Nous définissons également <code class="notranslate" translate="no">depthWrite</code> sur false pour que les ombres n'apportent de la confusion dans l'ordre des éléments. Nous reviendrons sur ces deux problèmes dans un <a href="transparency.html">autre article</a>. L'ombre est un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> car elle n'a pas besoin d'éclairage.</p>
-<p>Nous donnons à chaque sphère une teinte différente, puis nous enregistrons la base, le maillage de la sphère, le maillage de l'ombre et la position y initiale de chaque sphère.</p>
+<p>Maintenant, nous allons créer un tas de sphères. Pour chaque sphère, nous créerons une <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">base</code></a>
+<code class="notranslate" translate="no">THREE.Object3D</code> et nous ferons du maillage du plan d'ombre et du maillage de la sphère des enfants de la base.
+De cette façon, si nous déplaçons la base, la sphère et l'ombre se déplaceront. Nous devons placer l'ombre légèrement au-dessus du sol pour éviter le z-fighting.
+Nous définissons également <code class="notranslate" translate="no">depthWrite</code> à false afin que les ombres ne s'entremêlent pas.
+Nous aborderons ces deux problèmes dans <a href="transparency.html">un autre article</a>.
+L'ombre est un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> car elle n'a pas besoin d'éclairage.</p>
+<p>Nous donnons à chaque sphère une teinte différente, puis nous enregistrons la base, le maillage de la sphère,
+le maillage de l'ombre et la position y initiale de chaque sphère.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const numSpheres = 15;
 for (let i = 0; i &lt; numSpheres; ++i) {
-   // créer une base pour l'ombre et la sphère
-   // donc ils bougent ensemble.
+  // créer une base pour l'ombre et la sphère
+  // afin qu'elles se déplacent ensemble.
   const base = new THREE.Object3D();
   scene.add(base);
 
-   // ajoute l'ombre à la base
-   // remarque : nous fabriquons un nouveau matériau pour chaque sphère
-   // afin que nous puissions définir la transparence matérielle de cette sphère
-   // séparément.
+  // ajouter l'ombre à la base
+  // note : nous créons un nouveau matériau pour chaque sphère
+  // afin de pouvoir définir la transparence du matériau de cette sphère
+  // séparément.
   const shadowMat = new THREE.MeshBasicMaterial({
     map: shadowTexture,
     transparent: true,    // pour que nous puissions voir le sol
-    depthWrite: false,    // donc nous n'avons pas à trier
+    depthWrite: false,    // pour ne pas avoir à trier
   });
   const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
-  shadowMesh.position.y = 0.001;  // donc nous sommes légèrement au-dessus du sol
+  shadowMesh.position.y = 0.001;  // pour être légèrement au-dessus du sol
   shadowMesh.rotation.x = Math.PI * -.5;
   const shadowSize = sphereRadius * 4;
   shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
   base.add(shadowMesh);
 
   // ajouter la sphère à la base
-  const u = i / numSpheres;   // passe de 0 à 1 au fur et à mesure que nous itérons les sphères.
+  const u = i / numSpheres;   // va de 0 à 1 au fur et à mesure que nous parcourons les sphères.
   const sphereMat = new THREE.MeshPhongMaterial();
   sphereMat.color.setHSL(u, 1, .75);
   const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
   sphereMesh.position.set(0, sphereRadius + 2, 0);
   base.add(sphereMesh);
 
-  // rappelez-vous tous les 3 plus la position y
+  // mémoriser les 3 plus la position y
   sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
 }
 </pre>
-<p>Nous ajoutons 2 lumières. L'une est une <a href="https://threejs.org/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> avec une intensité réglée sur 2 pour vraiment illuminer les choses.</p>
+<p>Nous configurons 2 lumières. L'une est une <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> avec l'intensité définie à 2 pour vraiment
+éclaircir les choses.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
-  const skyColor = 0xB1E1FF;  // bleu
-  const groundColor = 0xB97A20;  // orange brun
+  const skyColor = 0xB1E1FF;  // bleu clair
+  const groundColor = 0xB97A20;  // orange brunâtre
   const intensity = 2;
   const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
   scene.add(light);
 }
 </pre>
-<p>L'autre est une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> afin que les sphères aient une définition.</p>
+<p>L'autre est une <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> afin que les sphères obtiennent une certaine définition</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const color = 0xFFFFFF;
   const intensity = 1;
@@ -144,7 +176,11 @@ for (let i = 0; i &lt; numSpheres; ++i) {
   scene.add(light.target);
 }
 </pre>
-<p>Nous pourrions faire un rendu tel quel mais animons les sphères. Pour chaque sphère, ombre et base, nous déplaçons la base dans le plan xz et nous déplaçons la sphère de haut en bas en utilisant <a href="https://threejs.org/docs/#api/en/math/Math.abs(Math.sin(time"><code class="notranslate" translate="no">Math.abs(Math.sin(time))</code></a>. Ceci nous donnera une animation de rebond. Enfin nous définirons l'opacité du matériau d'ombre de telle sorte qu'à mesure que chaque sphère monte, son ombre s'estompe.</p>
+<p>Cela rendrait tel quel, mais animons ces sphères.
+Pour chaque ensemble sphère, ombre, base, nous déplaçons la base dans le plan xz, nous
+déplaçons la sphère de haut en bas en utilisant <a href="/docs/#api/en/math/Math.abs(Math.sin(time))"><code class="notranslate" translate="no">Math.abs(Math.sin(time))</code></a>
+ce qui nous donne une animation rebondissante. Et nous définissons également l'opacité du matériau de l'ombre
+afin que, à mesure que chaque sphère monte, son ombre s'estompe.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
   time *= 0.001;  // convertir en secondes
 
@@ -153,50 +189,59 @@ for (let i = 0; i &lt; numSpheres; ++i) {
   sphereShadowBases.forEach((sphereShadowBase, ndx) =&gt; {
     const {base, sphereMesh, shadowMesh, y} = sphereShadowBase;
 
-    // u est une valeur qui va de 0 à 1 au fur et à mesure que l'on itère les sphères
+    // u est une valeur qui va de 0 à 1 au fur et à mesure que nous parcourons les sphères
     const u = ndx / sphereShadowBases.length;
 
-    // calculer une position pour la base. Cela va bouger
+    // calculer une position pour la base. Cela déplacera
     // à la fois la sphère et son ombre
     const speed = time * .2;
     const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
     const radius = Math.sin(speed - ndx) * 10;
     base.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
 
-    // yOff est une valeur allant de 0 à 1
+    // yOff est une valeur qui va de 0 à 1
     const yOff = Math.abs(Math.sin(time * 2 + ndx));
-    // déplace la sphère de haut en bas
+    // déplacer la sphère de haut en bas
     sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);
-    // estompe l'ombre au fur et à mesure que la sphère monte
+    // estomper l'ombre à mesure que la sphère monte
     shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
   });
 
   ...
 </pre>
-<p>Et voici 15 balles rebondissantes.</p>
+<p>Et voici 15 sortes de balles rebondissantes.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-fake.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-fake.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/shadows-fake.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Dans certaines applications, il est courant d'utiliser une ombre ronde ou ovale pour tout, mais bien sûr, vous pouvez également utiliser différentes textures d'ombre de forme. Vous pouvez également donner à l'ombre un bord plus dur. Un bon exemple d'utilisation de ce type d'ombre est <a href="https://www.google.com/search?tbm=isch&amp;q=animal+crossing+pocket+camp+screenshots">Animal Crossing Pocket Camp</a> où vous pouvez voir que chaque personnage a une simple ombre ronde. C'est efficace et pas cher. <a href="https://www.google.com/search?q=monument+valley+screenshots&amp;tbm=isch">Monument Valley</a> semble également utiliser ce type d'ombre pour le personnage principal.</p>
-<p>Passons maintenant aux cartes d'ombre, il y a 3 types de lumières qui peuvent projeter des ombres. La <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>, la <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> et la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>.</p>
-<p>Commençons avec la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> en nous appuyant sur l'exemple de <a href="lights.html">l'article sur les lumières</a>.</p>
-<p>La première chose à faire est d'activer les ombres dans le renderer (moteur de rendu).</p>
+<p>Dans certaines applications, il est courant d'utiliser une ombre ronde ou ovale pour tout, mais
+bien sûr, vous pourriez aussi utiliser des textures d'ombre de formes différentes. Vous pourriez également
+donner à l'ombre un bord plus net. Un bon exemple de l'utilisation de ce type
+d'ombre est <a href="https://www.google.com/search?tbm=isch&amp;q=animal+crossing+pocket+camp+screenshots">Animal Crossing Pocket Camp</a>
+où vous pouvez voir que chaque personnage a une simple ombre ronde. C'est efficace et peu coûteux.
+<a href="https://www.google.com/search?q=monument+valley+screenshots&amp;tbm=isch">Monument Valley</a>
+semble également utiliser ce type d'ombre pour le personnage principal.</p>
+<p>Passons maintenant aux cartes d'ombres. Il existe 3 types de lumières qui peuvent projeter des ombres : la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>,
+la <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> et la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>.</p>
+<p>Commençons par la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> en utilisant l'exemple avec helper de l'<a href="lights.html">article sur les lumières</a>.</p>
+<p>La première chose à faire est d'activer les ombres dans le rendu (renderer).</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
 +renderer.shadowMap.enabled = true;
 </pre>
-<p>Ensuite, nous devons également dire à la lumière de projeter une ombre.</p>
+<p>Ensuite, nous devons également dire à la lumière de projeter une ombre</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const light = new THREE.DirectionalLight(color, intensity);
 +light.castShadow = true;
 </pre>
-<p>Nous devons également aller sur chaque maillage de la scène et décider s'il doit à la fois projeter des ombres et/ou recevoir des ombres.</p>
-<p>Faisons en sorte que le 'plane' (le sol) reçoive uniquement les ombres car nous ne nous intéressons pas vraiment qu'il projette quelque chose en-dessous lui.</p>
+<p>Nous devons également parcourir chaque maillage dans la scène et décider s'il doit
+à la fois projeter des ombres et/ou recevoir des ombres.</p>
+<p>Faisons en sorte que le plan (le sol) reçoive uniquement les ombres, car nous ne
+nous soucions pas vraiment de ce qui se passe en dessous.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(planeGeo, planeMat);
 mesh.receiveShadow = true;
 </pre>
-<p>Pour le cube et la sphère faisons en sorte qu'ils reçoivent et projettent des ombres.</p>
+<p>Pour le cube et la sphère, faisons en sorte qu'ils reçoivent et projettent tous deux des ombres</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(cubeGeo, cubeMat);
 mesh.castShadow = true;
 mesh.receiveShadow = true;
@@ -207,33 +252,45 @@ const mesh = new THREE.Mesh(sphereGeo, sphereMat);
 mesh.castShadow = true;
 mesh.receiveShadow = true;
 </pre>
-<p>Et puis nous exécutons cela.</p>
+<p>Et ensuite, nous l'exécutons.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
-  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-directional-light.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-directional-light.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-directional-light.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadows-directional-light.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Que s'est-il passé? Pourquoi des parties des ombres manquent-elles ?</p>
-<p>C'est parce que les shadow maps sont créées en rendant la scène du point de vue de la lumière. C'est comme si il y avait une caméra dans la <a href="https://threejs.org/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> qui regardait sa cible. Tout comme <a href="cameras.html">la caméra de l'article précédent</a>, la 'caméra de la lumière' définit une zone à l'intérieur de laquelle les ombres sont projetées. Dans l'exemple ci-dessus, cette zone est trop petite.</p>
-<p>Afin de bien visualiser cette zone, ajoutons un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> à la scène.</p>
+<p>Que s'est-il passé ? Pourquoi des parties des ombres sont-elles manquantes ?</p>
+<p>La raison est que les cartes d'ombres sont créées en rendant la scène du point
+de vue de la lumière. Dans ce cas, il y a une caméra au niveau de la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>
+qui regarde sa cible. Tout comme les <a href="cameras.html">caméras que nous avons précédemment couvertes</a>,
+la caméra d'ombre de la lumière définit une zone à l'intérieur de laquelle
+les ombres sont rendues. Dans l'exemple ci-dessus, cette zone est trop petite.</p>
+<p>Afin de visualiser cette zone, nous pouvons obtenir la caméra d'ombre de la lumière et ajouter
+un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> à la scène.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
 scene.add(cameraHelper);
 </pre>
-<p>Maintenant, on peut voir cette zone où les ombres sont projetés.</p>
+<p>Et maintenant, vous pouvez voir la zone pour laquelle les ombres sont projetées et reçues.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
-  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-directional-light-with-camera-helper.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-helper.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-directional-light-with-camera-helper.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-helper.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Ajustez la valeur x cible dans les deux sens et il devrait être assez clair que seul ce qui se trouve à l'intérieur de la boîte de la caméra d'ombre de la lumière est l'endroit où les ombres sont dessinées.</p>
+<p>Ajustez la valeur x de la cible d'avant en arrière et il devrait être assez clair que seules
+les ombres sont dessinées dans la zone de la caméra d'ombre de la lumière.</p>
 <p>Nous pouvons ajuster la taille de cette boîte en ajustant la caméra d'ombre de la lumière.</p>
-<p>Ajoutons quelques paramètres à lil-gui pour ajuster les ombres. Étant donné qu'une <a href="https://threejs.org/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> représente la lumière allant dans une direction parallèle, la <a href="https://threejs.org/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> utilise une <a href="https://threejs.org/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> pour sa caméra d'ombre. Nous avons expliqué le fonctionnement d'une caméra orthographique dans <a href="cameras.html">l'article précédent sur les caméras</a>.</p>
-<p>Pour rappel, une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> définit son <em>frustum</em> par ses propriétés <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>, <code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code> et <code class="notranslate" translate="no">zoom</code>.</p>
-<p>Créons à nouveau un helper pour lil-gui. Appelons-le <code class="notranslate" translate="no">DimensionGUIHelper</code>
-et passons-lui un objet et 2 propriétés. Il dispose d'une propriété que lil-gui peut ajuster et en réponse définit les deux propriétés, une positive et une négative.
-Nous pouvons l'utiliser pour définir <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">right</code> en tant que <code class="notranslate" translate="no">width</code> et <code class="notranslate" translate="no">up</code>, <code class="notranslate" translate="no">down</code> en tant que <code class="notranslate" translate="no">height</code>.</p>
+<p>Ajoutons des paramètres d'interface graphique pour ajuster la boîte de la caméra d'ombre de la lumière. Comme une
+<a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> représente une lumière allant dans une direction parallèle, la
+<a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> utilise une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> pour sa caméra d'ombre.
+Nous avons vu comment fonctionne une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> dans l'<a href="cameras.html">article précédent sur les caméras</a>.</p>
+<p>Rappelons qu'une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> définit
+sa boîte ou son <em>frustum de visualisation</em> par ses propriétés <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>, <code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code>,
+et <code class="notranslate" translate="no">zoom</code>.</p>
+<p>À nouveau, créons une classe d'aide pour lil-gui. Nous créerons une <code class="notranslate" translate="no">DimensionGUIHelper</code>
+à laquelle nous passerons un objet et 2 propriétés. Elle présentera une propriété que lil-gui
+peut ajuster et, en réponse, définira les deux propriétés, l'une positive et l'autre négative.
+Nous pouvons l'utiliser pour définir <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">right</code> comme <code class="notranslate" translate="no">width</code>, et <code class="notranslate" translate="no">up</code> et <code class="notranslate" translate="no">down</code> comme <code class="notranslate" translate="no">height</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DimensionGUIHelper {
   constructor(obj, minProp, maxProp) {
     this.obj = obj;
@@ -249,86 +306,115 @@ Nous pouvons l'utiliser pour définir <code class="notranslate" translate="no">l
   }
 }
 </pre>
-<p>Utilisons aussi le <code class="notranslate" translate="no">MinMaxGUIHelper</code> que nous avons créé dans <a href="cameras.html">l'article sur les caméra</a> pour paramétrer <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>.</p>
+<p>Nous utiliserons également la <code class="notranslate" translate="no">MinMaxGUIHelper</code> que nous avons créée dans l'<a href="cameras.html">article sur les caméras</a>
+pour ajuster <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
 gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
 gui.add(light, 'intensity', 0, 2, 0.01);
 +{
-+  const folder = gui.addFolder('Shadow Camera');
++  const folder = gui.addFolder('Caméra d\'ombre');
 +  folder.open();
 +  folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)
-+    .name('width')
++    .name('largeur')
 +    .onChange(updateCamera);
 +  folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)
-+    .name('height')
++    .name('hauteur')
 +    .onChange(updateCamera);
 +  const minMaxGUIHelper = new MinMaxGUIHelper(light.shadow.camera, 'near', 'far', 0.1);
-+  folder.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
-+  folder.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
++  folder.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('proche').onChange(updateCamera);
++  folder.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('loin').onChange(updateCamera);
 +  folder.add(light.shadow.camera, 'zoom', 0.01, 1.5, 0.01).onChange(updateCamera);
 +}
 </pre>
-<p>Disons à lil-gui d'appeler la fonction <code class="notranslate" translate="no">updateCamera</code> à chaque changement.
-Écrivons cette fonction pour mettre à jour la lumière et son helper, la caméra d'ombre de la lumière et son helper.</p>
+<p>Nous demandons à l'interface graphique d'appeler notre fonction <code class="notranslate" translate="no">updateCamera</code> chaque fois que quelque chose change.
+Écrivons cette fonction pour mettre à jour la lumière, l'helper pour la lumière, la
+caméra d'ombre de la lumière et l'helper affichant la caméra d'ombre de la lumière.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateCamera() {
-  // mettre à jour le MatrixWorld de la cible de lumière car il est requis par le helper
+  // mettre à jour le matrixWorld de la cible de la lumière car il est nécessaire pour l'helper
   light.target.updateMatrixWorld();
   helper.update();
   // mettre à jour la matrice de projection de la caméra d'ombre de la lumière
   light.shadow.camera.updateProjectionMatrix();
-  // et maintenant mettre à jour l'assistant de caméra que nous utilisons pour afficher la caméra d'ombre de la lumière
+  // et maintenant mettre à jour l'helper caméra que nous utilisons pour afficher la caméra d'ombre de la lumière
   cameraHelper.update();
 }
 updateCamera();
 </pre>
-<p>Et maintenant que nous avons accès aux propriétés de la caméra d'ombre, jouons avec.</p>
+<p>Et maintenant que nous avons donné à la caméra d'ombre de la lumière une interface graphique, nous pouvons jouer avec les valeurs.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-directional-light-with-camera-gui.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-gui.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-gui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Réglez <code class="notranslate" translate="no">width</code> et <code class="notranslate" translate="no">height</code> sur 30 et vous verrez que les ombres sont correctement projetées.</p>
-<p>Mais cela soulève la question, pourquoi ne pas simplement définir <code class="notranslate" translate="no">width</code> et <code class="notranslate" translate="no">height</code> avec des chiffres plus grands ? Réglez la largeur et la hauteur sur 100 et vous pourriez voir quelque chose comme ceci.</p>
+<p>Définissez la <code class="notranslate" translate="no">largeur</code> et la <code class="notranslate" translate="no">hauteur</code> à environ 30 et vous pourrez voir que les ombres sont correctes
+et que les zones qui doivent être dans l'ombre pour cette scène sont entièrement couvertes.</p>
+<p>Mais cela soulève la question : pourquoi ne pas simplement définir la <code class="notranslate" translate="no">largeur</code> et la <code class="notranslate" translate="no">hauteur</code> à des
+nombres géants pour tout couvrir ? Définissez la <code class="notranslate" translate="no">largeur</code> et la <code class="notranslate" translate="no">hauteur</code> à 100
+et vous pourriez voir quelque chose comme ceci</p>
 <div class="threejs_center"><img src="../resources/images/low-res-shadow-map.png" style="width: 369px"></div>
 
-<p>Que se passe-t-il avec ces ombres basse résolution ?!</p>
-<p>Ce problème est lié à un autre paramètres des ombres. Les textures d'ombre sont des textures dans lesquelles les ombres sont dessinées. Ces textures ont une taille. La zone de la caméra d'ombre que nous avons définie ci-dessus est étirée sur cette taille. Cela signifie que plus la zone que vous définissez est grande, plus vos ombres seront pixelisées.</p>
-<p>Vous pouvez définir la résolution de la texture de l'ombre en définissant <code class="notranslate" translate="no">light.shadow.mapSize.width</code> et <code class="notranslate" translate="no">light.shadow.mapSize.height</code>. Ils sont par défaut à 512x512. Plus vous les agrandissez, plus ils prennent de mémoire et plus ils sont lents à s'afficher, donc vous devrez les définir aussi petits que possible tout en faisant fonctionner votre scène. La même chose est vraie avec la zone d'ombre. Plus petite signifie des ombres plus belles, alors réduisez la zone autant que possible tout en couvrant votre scène. Sachez que la machine de chaque utilisateur a une taille de texture maximale autorisée qui est disponible sur le renderer en tant que <a href="/docs/#api/en/renderers/WebGLRenderer#capabilities"><code class="notranslate" translate="no">renderer.capabilities.maxTextureSize</code></a>.</p>
+<p>Qu'est-ce qui se passe avec ces ombres basse résolution ?!</p>
+<p>Ce problème est un autre paramètre lié aux ombres dont il faut être conscient.
+Les cartes d'ombres sont des textures dans lesquelles les ombres sont dessinées.
+Ces textures ont une taille. La zone de la caméra d'ombre que nous avons définie ci-dessus est étirée
+sur cette taille. Cela signifie que plus la zone que vous définissez est grande, plus vos ombres seront
+pixelisées.</p>
+<p>Vous pouvez définir la résolution de la texture de la carte d'ombre en définissant <code class="notranslate" translate="no">light.shadow.mapSize.width</code>
+et <code class="notranslate" translate="no">light.shadow.mapSize.height</code>. Par défaut, elles sont de 512x512.
+Plus vous les augmentez, plus elles consomment de mémoire et plus elles sont lentes à calculer, vous voulez donc
+les définir aussi petites que possible tout en faisant fonctionner votre scène. Il en va de même pour la
+zone de la caméra d'ombre de la lumière. Plus elle est petite, meilleures sont les ombres, alors rendez la zone aussi petite que possible et
+continuez à couvrir votre scène. Sachez que la machine de chaque utilisateur a une taille de texture maximale
+autorisée qui est disponible sur le renderer sous la forme de <a href="/docs/#api/en/renderers/WebGLRenderer#capabilities"><code class="notranslate" translate="no">renderer.capabilities.maxTextureSize</code></a>.</p>
 <!--
-Ok but what about `near` and `far` I hear you thinking. Can we set `near` to 0.00001 and far to `100000000`
+Ok, mais qu'en est-il de `near` et `far` me direz-vous. Pouvons-nous définir `near` sur 0.00001 et `far` sur 100000000
 -->
-<p>En passant à une <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> la caméra d'ombre devient une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Contrairement à la caméra d'ombre de la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> où nous pouvons régler manuellement la plupart de ses paramètres, celle de la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>est auto-contrôlée. Le <code class="notranslate" translate="no">fov</code> de la caméra d'ombre est directement connecté au réglage de l'<code class="notranslate" translate="no">angle</code> de la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>.
-L'<code class="notranslate" translate="no">aspect</code> est directement définit en fonction de la taille de la zone d'ombre.</p>
+<p>En passant à la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>, la caméra d'ombre de la lumière devient une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Contrairement à la caméra d'ombre
+de la <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> où nous pouvions définir manuellement la plupart de ses paramètres, la caméra d'ombre
+de la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> est contrôlée par la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> elle-même. Le <code class="notranslate" translate="no">fov</code> (champ de vision) pour l'ombre
+de la caméra est directement lié au paramètre <code class="notranslate" translate="no">angle</code> de la <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>.
+L'<code class="notranslate" translate="no">aspect</code> est défini automatiquement en fonction de la taille de la carte d'ombre.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.DirectionalLight(color, intensity);
 +const light = new THREE.SpotLight(color, intensity);
 </pre>
-<p>Ajoutons les paramètres <code class="notranslate" translate="no">penumbra</code> et <code class="notranslate" translate="no">angle</code> vu dans <a href="lights.html">l'article sur les lumières</a>.</p>
+<p>et nous avons rajouté les paramètres <code class="notranslate" translate="no">penumbra</code> et <code class="notranslate" translate="no">angle</code>
+de notre <a href="lights.html">article sur les lumières</a>.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-spot-light-with-camera-gui.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-camera-gui.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-camera-gui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
 <!--
-You can notice, just like the last example if we set the angle high
-then the shadow map, the texture is spread over a very large area and
-the resolution of our shadows gets really low.
+Vous pouvez remarquer, tout comme dans le dernier exemple, si nous définissons l'angle élevé
+alors la carte d'ombre, la texture est étendue sur une très grande zone et
+la résolution de nos ombres devient très faible.
 
 div class="threejs_center"><img src="../resources/images/low-res-shadow-map-spotlight.png" style="width: 344px"></div>
 
-You can increase the size of the shadow map as mentioned above. You can
-also blur the result
+Vous pouvez augmenter la taille de la carte d'ombre comme mentionné ci-dessus. Vous pouvez
+également flouter le résultat
 
 <div translate="no" class="threejs_example_container notranslate">
-  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-spot-light-with-shadow-radius"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-shadow-radius" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-spot-light-with-shadow-radius"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-shadow-radius" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 
 -->
-<p>Et enfin il y a les ombres avec une <a href="https://threejs.org/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>. Étant donné qu'une <a href="https://threejs.org/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> brille dans toutes les directions, les seuls paramètres pertinents sont <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Sinon, l'ombre PointLight est effectivement constituée de 6 ombres <a href="https://threejs.org/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>, chacune pointant vers la face d'un cube autour de la lumière. Cela signifie que les ombres <a href="https://threejs.org/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> sont beaucoup plus lentes car la scène entière doit être dessinée 6 fois, une pour chaque direction.</p>
-<p>Plaçons notre scène à l'intérieur d'une boîte afin que nous puissions voir des ombres sur les murs et le plafond. Nous allons définir la propriété <code class="notranslate" translate="no">side</code> du matériau sur <code class="notranslate" translate="no">THREE.BackSide</code> afin de rendre l'intérieur de la boîte au lieu de l'extérieur. Comme avec le sol, nous ne la paramétrons que pour recevoir des ombres. Nous allons également définir la position de la boîte de sorte que son fond soit légèrement en dessous du sol afin d'éviter un problème de z-fighting.</p>
+<p>Et enfin, il y a les ombres avec une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>. Comme une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>
+brille dans toutes les directions, les seuls paramètres pertinents sont <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>.
+Sinon, l'ombre d'une <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> est effectivement composée de 6 ombres de <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>
+, chacune pointant vers une face d'un cube autour de la lumière. Cela signifie
+que les ombres des <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> sont beaucoup plus lentes car toute la scène doit être
+dessinée 6 fois, une pour chaque direction.</p>
+<p>Mettons une boîte autour de notre scène pour que nous puissions voir les ombres sur les murs
+et le plafond. Nous définirons la propriété <code class="notranslate" translate="no">side</code> du matériau sur <code class="notranslate" translate="no">THREE.BackSide</code>
+afin de rendre l'intérieur de la boîte au lieu de l'extérieur. Comme le sol,
+nous la définirons pour qu'elle ne reçoive que les ombres. De plus, nous définirons la position de la
+boîte de manière à ce que son bas soit légèrement en dessous du sol afin que le sol et le bas
+de la boîte ne causent pas de z-fighting.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
   const cubeSize = 30;
   const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
@@ -342,26 +428,28 @@ also blur the result
   scene.add(mesh);
 }
 </pre>
-<p>Et bien sûr, il faut passer la lumière en <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>.</p>
+<p>Et bien sûr, nous devons changer la lumière en <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.SpotLight(color, intensity);
 +const light = new THREE.PointLight(color, intensity);
 
 ....
 
-// afin que nous puissions facilement voir où se trouve la spotLight
+// pour pouvoir facilement voir où se trouve la lumière ponctuelle
 +const helper = new THREE.PointLightHelper(light);
 +scene.add(helper);
 </pre>
 <p></p><div translate="no" class="threejs_example_container notranslate">
-  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-point-light.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/shadows-point-light.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-point-light.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/shadows-point-light.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Utilisez les paramètres position de lil-gui pour déplacer la lumière et vous verrez les ombres se projeter sur tous les murs. Vous pouvez également ajuster les paramètres near et far et voir comment les autres ombres se comportent.</p>
-<!--
-self shadow, shadow acne
--->
+<p>Utilisez les paramètres d'interface graphique <code class="notranslate" translate="no">position</code> pour déplacer la lumière
+et vous verrez les ombres tomber sur tous les murs. Vous pouvez
+également ajuster les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> et voir, tout comme
+pour les autres ombres, que lorsque les objets sont plus proches que <code class="notranslate" translate="no">near</code>, ils
+ne reçoivent plus d'ombre et lorsqu'ils sont plus loin que <code class="notranslate" translate="no">far</code>, ils sont toujours dans l'ombre.</p>
+<!-- auto-ombre, acné d'ombre -->
 
         </div>
       </div>
@@ -373,4 +461,4 @@ self shadow, shadow acne
 
 
 
-</body></html>
+</body></html>

+ 220 - 134
manual/fr/textures.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Les textures dans </title>
+    <title>Textures</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Les textures dans ">
+    <meta name="twitter:title" content="Three.js – Textures">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,39 +22,45 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Les textures</h1>
+        <h1>Textures</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Cet article fait partie d'une série consacrée à Three.js.
-Le premier article s'intitule <a href="fundamentals.html">Principes de base</a>.
-L'<a href="setup.html">article précédent</a> concerne la configuration nécessaire pour cet article.
-Si vous ne l'avez pas encore lu, vous voudriez peut-être commencer par là.</p>
-<p>Les textures sont un gros sujet dans Three.js et je ne suis pas sûr de pouvoir les expliquer à 100% mais je vais essayer. Il y a de nombreuses choses à voir et beaucoup d'entre elles sont interdépendantes, il est donc difficile de les expliquer toutes en même temps. Voici une table des matières rapide pour cet article.</p>
+          <p>Cet article fait partie d'une série d'articles sur three.js.
+Le premier article concernait <a href="fundamentals.html">les bases de three.js</a>.
+L'<a href="setup.html">article précédent</a> expliquait comment se préparer pour cet article.
+Si vous ne l'avez pas encore lu, vous pourriez vouloir commencer par là.</p>
+<p>Les textures sont un sujet assez vaste dans Three.js et
+je ne suis pas sûr à 100% du niveau auquel les expliquer, mais je vais essayer.
+Il y a de nombreux sujets et beaucoup d'entre eux sont interdépendants, il est donc difficile d'expliquer
+tout en une seule fois. Voici une table des matières rapide pour cet article.</p>
 <ul>
-<li><a href="#hello">Hello Texture</a></li>
-<li><a href="#six">6 textures, une pour chaque face d'un cube</a></li>
-<li><a href="#loading">Téléchargement de textures</a></li>
+<li><a href="#hello">Bonjour la texture</a></li>
+<li><a href="#six">6 textures, une différente sur chaque face d'un cube</a></li>
+<li><a href="#loading">Charger des textures</a></li>
 <ul>
-  <li><a href="#easy">La façon la plus simple</a></li>
-  <li><a href="#wait1">En attente du chargement d'une texture</a></li>
-  <li><a href="#waitmany">En attente du téléchargement de plusieurs textures</a></li>
-  <li><a href="#cors">Chargement de textures d'autres origines</a></li>
+  <li><a href="#easy">La manière simple</a></li>
+  <li><a href="#wait1">Attendre le chargement d'une texture</a></li>
+  <li><a href="#waitmany">Attendre le chargement de plusieurs textures</a></li>
+  <li><a href="#cors">Charger des textures depuis d'autres origines</a></li>
 </ul>
 <li><a href="#memory">Utilisation de la mémoire</a></li>
-<li><a href="#format">JPG ou PNG</a></li>
+<li><a href="#format">JPG vs PNG</a></li>
 <li><a href="#filtering-and-mips">Filtrage et mips</a></li>
-<li><a href="#uvmanipulation">Répétition, décalage, rotation, emballage</a></li>
+<li><a href="#uvmanipulation">Répétition, décalage, rotation, habillage</a></li>
 </ul>
 
-<h2 id="-a-name-hello-a-hello-texture"><a name="hello"></a> Hello Texture</h2>
-<p>Les textures sont <em>généralement</em> des images qui sont le plus souvent créées dans un programme tiers comme Photoshop ou GIMP. Par exemple, mettons cette image sur un cube.</p>
+<h2 id="-a-name-hello-a-hello-texture"><a name="hello"></a> Bonjour la texture</h2>
+<p>Les textures sont <em>généralement</em> des images qui sont le plus souvent créées
+dans un programme tiers comme Photoshop ou GIMP. Par exemple, mettons
+cette image sur un cube.</p>
 <div class="threejs_center">
   <img src="../examples/resources/images/wall.jpg" style="width: 600px;" class="border">
 </div>
 
-<p>Modifions l'un de nos premiers échantillons. Tout ce que nous avons à faire, c'est de créer un <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>. Appelons-le avec sa méthode
-<a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> et l'URL d'une image puis définissons le résultat sur la propriété <code class="notranslate" translate="no">map</code> du matériau au lieu de définir <code class="notranslate" translate="no">color</code>.</p>
+<p>Nous allons modifier un de nos premiers exemples. Tout ce que nous avons à faire est de créer un <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>. Appelez sa
+méthode <a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> avec l'URL d'une
+image et définissez la propriété <code class="notranslate" translate="no">map</code> du matériau sur le résultat au lieu de définir sa <code class="notranslate" translate="no">color</code>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
 +const texture = loader.load( 'resources/images/wall.jpg' );
 +texture.colorSpace = THREE.SRGBColorSpace;
@@ -64,14 +70,14 @@ const material = new THREE.MeshBasicMaterial({
 +  map: texture,
 });
 </pre>
-<p>Notez que nous utilisons un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>, donc pas besoin de lumières.</p>
+<p>Notez que nous utilisons <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>, donc pas besoin de lumières.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/textured-cube.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/textured-cube.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<h2 id="-a-name-six-a-6-textures-une-pour-chaque-face-d-un-cube"><a name="six"></a> 6 textures, une pour chaque face d'un cube</h2>
+<h2 id="-a-name-six-a-6-textures-a-different-one-on-each-face-of-a-cube"><a name="six"></a> 6 textures, une différente sur chaque face d'un cube</h2>
 <p>Que diriez-vous de 6 textures, une sur chaque face d'un cube ?</p>
 <div class="threejs_center">
   <div>
@@ -86,7 +92,7 @@ const material = new THREE.MeshBasicMaterial({
   </div>
 </div>
 
-<p>Fabriquons juste 6 matériaux et passons-les sous forme de tableau lors de la création du <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a></p>
+<p>Nous créons simplement 6 matériaux et les passons sous forme de tableau lorsque nous créons le <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a></p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
 -const texture = loader.load( 'resources/images/wall.jpg' );
 -texture.colorSpace = THREE.SRGBColorSpace;
@@ -114,30 +120,45 @@ const material = new THREE.MeshBasicMaterial({
 <p>Ça marche !</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-6-textures.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/textured-cube-6-textures.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/textured-cube-6-textures.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Il convient de noter, cependant, que tous les types de géométries ne peuvent supporter la prise en charge de plusieurs matériaux. <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> ne peut utiliser que 6 matériaux,  un pour chaque face.
-<a href="/docs/#api/en/geometries/ConeGeometry"><code class="notranslate" translate="no">ConeGeometry</code></a>, seulement 2, un pour la base et un pour le cône.
-<a href="/docs/#api/en/geometries/CylinderGeometry"><code class="notranslate" translate="no">CylinderGeometry</code></a> peut recevoir 3 matériaux pour le bas, le haut et le côté.
-Dans les autres cas, vous devrez créer ou charger une géométrie personnalisée et/ou modifier les coordonnées de texture.</p>
-<p>Il est bien plus performant d'utiliser, comme dans bien d'autres moteurs 3D, un
-<a href="https://fr.wikipedia.org/wiki/Atlas_de_texture">atlas de texture</a>
-si vous voulez utiliser plusieurs images sur une même géométrie. Un atlas de texture est l'endroit où vous placez plusieurs images dans une seule texture, puis utilisez les coordonnées de texture sur les sommets de votre géométrie pour sélectionner les parties d'une texture à utiliser sur chaque triangle de votre géométrie.</p>
-<p>Que sont les coordonnées de texture ? Ce sont des données ajoutées à chaque sommet d'un morceau de géométrie qui spécifient quelle partie de la texture correspond à ce sommet spécifique. Nous les examinerons lorsque nous commencerons à <a href="custom-buffergeometry.html">créer une géométrie personnalisée</a>.</p>
-<h2 id="-a-name-loading-a-t-l-chargement-de-textures"><a name="loading"></a> Téléchargement de textures</h2>
-<h3 id="-a-name-easy-a-la-fa-on-la-plus-simple"><a name="easy"></a> La façon la plus simple</h3>
-<p>La plupart du code de ce site utilise la méthode la plus simple pour charger les textures. Nous créons un <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a> puis appelons sa méthode de <a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">chargement</code></a>.
+<p>Il convient de noter cependant que tous les types de géométrie ne supportent pas plusieurs
+matériaux. <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> peut utiliser 6 matériaux, un pour chaque face.
+<a href="/docs/#api/en/geometries/ConeGeometry"><code class="notranslate" translate="no">ConeGeometry</code></a> peut utiliser 2 matériaux, un pour le fond et un pour le cône.
+<a href="/docs/#api/en/geometries/CylinderGeometry"><code class="notranslate" translate="no">CylinderGeometry</code></a> peut utiliser 3 matériaux : fond, haut et côté.
+Dans d'autres cas, vous devrez construire ou charger une géométrie personnalisée et/ou modifier les coordonnées de texture.</p>
+<p>Il est beaucoup plus courant dans d'autres moteurs 3D et beaucoup plus performant d'utiliser un
+<a href="https://en.wikipedia.org/wiki/Texture_atlas">atlas de textures</a>
+si vous souhaitez autoriser plusieurs images sur une seule géométrie. Un atlas de textures
+est un endroit où vous placez plusieurs images dans une seule texture et utilisez ensuite les coordonnées de texture
+sur les sommets de votre géométrie pour sélectionner quelles parties d'une texture sont utilisées
+sur chaque triangle de votre géométrie.</p>
+<p>Que sont les coordonnées de texture ? Ce sont des données ajoutées à chaque sommet d'une pièce de géométrie
+qui spécifient quelle partie de la texture correspond à ce sommet spécifique.
+Nous les aborderons lorsque nous commencerons à <a href="custom-buffergeometry.html">construire une géométrie personnalisée</a>.</p>
+<h2 id="-a-name-loading-a-loading-textures"><a name="loading"></a> Chargement des textures</h2>
+<h3 id="-a-name-easy-a-the-easy-way"><a name="easy"></a> La manière simple</h3>
+<p>La plupart du code sur ce site utilise la méthode la plus simple pour charger des textures.
+Nous créons un <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>, puis appelons sa méthode <a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a>.
 Cela renvoie un objet <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a>.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const texture = loader.load('resources/images/flower-1.jpg');
 </pre>
-<p>Il est important de noter qu'en utilisant cette méthode, notre texture sera transparente jusqu'à ce que l'image soit chargée de manière asynchrone par Three.js, auquel cas elle mettra à jour la texture avec l'image téléchargée.</p>
-<p>Le gros avantage, c'est que nous n'avons pas besoin d'attendre que la texture soit chargée pour que notre page s'affiche. C'est probablement correct pour un grand nombre de cas d'utilisation, mais si nous le voulons, nous pouvons demander à Three.js de nous dire quand le téléchargement de la texture est terminé.</p>
-<h3 id="-a-name-wait1-a-en-attente-du-chargement-d-une-texture"><a name="wait1"></a> En attente du chargement d'une texture</h3>
-<p>Pour attendre qu'une texture se charge, la méthode <code class="notranslate" translate="no">load</code> du chargeur de texture prend une fonction de rappel qui sera appelée lorsque la texture aura fini de se charger. Revenons à notre exemple du dessus, nous pouvons attendre que la texture se charge avant de créer notre <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> et de l'ajouter à une scène comme ceci :</p>
+<p>Il est important de noter qu'en utilisant cette méthode, notre texture sera transparente jusqu'à
+ce que l'image soit chargée de manière asynchrone par three.js, moment auquel elle mettra à jour la texture
+avec l'image téléchargée.</p>
+<p>Cela présente le grand avantage de ne pas avoir à attendre le chargement de la texture et notre
+page commencera à s'afficher immédiatement. C'est probablement acceptable pour un grand nombre de cas d'utilisation
+mais si nous le souhaitons, nous pouvons demander à three.js de nous informer lorsque la texture a fini de se télécharger.</p>
+<h3 id="-a-name-wait1-a-waiting-for-a-texture-to-load"><a name="wait1"></a> Attendre le chargement d'une texture</h3>
+<p>Pour attendre le chargement d'une texture, la méthode <code class="notranslate" translate="no">load</code> du chargeur de textures prend un rappel
+qui sera appelé lorsque la texture aura fini de se charger. En reprenant notre premier exemple,
+nous pouvons attendre le chargement de la texture avant de créer notre <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> et de l'ajouter à la scène
+comme ceci</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
 loader.load('resources/images/wall.jpg', (texture) =&gt; {
+  texture.colorSpace = THREE.SRGBColorSpace;
   const material = new THREE.MeshBasicMaterial({
     map: texture,
   });
@@ -146,15 +167,18 @@ loader.load('resources/images/wall.jpg', (texture) =&gt; {
   cubes.push(cube);  // add to our list of cubes to rotate
 });
 </pre>
-<p>À moins de vider le cache de votre navigateur et d'avoir une connexion lente, il est peu probable que vous voyiez la différence, mais soyez assuré qu'il attend le chargement de la texture.</p>
+<p>À moins que vous ne vidiez le cache de votre navigateur et que vous ayez une connexion lente, il est peu probable
+que vous voyiez une différence, mais soyez assuré qu'elle attend le chargement de la texture.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-texture.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-texture.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-texture.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<h3 id="-a-name-waitmany-a-en-attente-du-chargement-de-plusieurs-textures"><a name="waitmany"></a>Attendre le chargement de plusieurs textures</h3>
-<p>Pour attendre que toutes les textures soient chargées, vous pouvez utiliser un <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>. Créez-en un et transmettez-le à <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>, puis définissez sa propriété <a href="/docs/#api/en/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a> avec une fonction de rappel.</p>
+<h3 id="-a-name-waitmany-a-waiting-for-multiple-textures-to-load"><a name="waitmany"></a> Attendre le chargement de plusieurs textures</h3>
+<p>Pour attendre que toutes les textures soient chargées, vous pouvez utiliser un <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>. Créez-en un
+et passez-le au <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>, puis définissez sa propriété  <a href="/docs/#api/en/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a>
+sur un rappel.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadManager = new THREE.LoadingManager();
 *const loader = new THREE.TextureLoader(loadManager);
 
@@ -170,11 +194,12 @@ const materials = [
 +loadManager.onLoad = () =&gt; {
 +  const cube = new THREE.Mesh(geometry, materials);
 +  scene.add(cube);
-+  cubes.push(cube);  // ajouter à la liste des cubes
++  cubes.push(cube);  // add to our list of cubes to rotate
 +};
 </pre>
-<p>Le <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> a également une propriété <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a> que nous pouvons définir sur une autre 'callback' pour afficher un indicateur de progression.</p>
-<p>Ajoutons d'abord une barre de progression en HTML</p>
+<p>Le <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> a également une propriété <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a>
+que nous pouvons définir sur un autre rappel pour afficher un indicateur de progression.</p>
+<p>Nous allons d'abord ajouter une barre de progression en HTML</p>
 <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
   &lt;canvas id="c"&gt;&lt;/canvas&gt;
 +  &lt;div id="loading"&gt;
@@ -182,7 +207,7 @@ const materials = [
 +  &lt;/div&gt;
 &lt;/body&gt;
 </pre>
-<p>et un peu de CSS</p>
+<p>et le CSS associé</p>
 <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#loading {
     position: fixed;
     top: 0;
@@ -206,7 +231,9 @@ const materials = [
     transform: scaleX(0);
 }
 </pre>
-<p>Ensuite, dans le code, nous mettrons à jour la <code class="notranslate" translate="no">progressbar</code> dans notre fonction de rappel <code class="notranslate" translate="no">onProgress</code>. Elle est appelée avec l'URL du dernier élément chargé, le nombre d'éléments chargés jusqu'à présent et le nombre total d'éléments chargés.</p>
+<p>Ensuite, dans le code, nous mettrons à jour l'échelle de la <code class="notranslate" translate="no">progressbar</code> dans notre rappel <code class="notranslate" translate="no">onProgress</code>. Il est
+appelé avec l'URL du dernier élément chargé, le nombre d'éléments chargés jusqu'à présent et le nombre total
+d'éléments à charger.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadingElem = document.querySelector('#loading');
 +const progressBarElem = loadingElem.querySelector('.progressbar');
 
@@ -214,7 +241,7 @@ loadManager.onLoad = () =&gt; {
 +  loadingElem.style.display = 'none';
   const cube = new THREE.Mesh(geometry, materials);
   scene.add(cube);
-  cubes.push(cube);  // ajouter à la liste des cubes
+  cubes.push(cube);  // add to our list of cubes to rotate
 };
 
 +loadManager.onProgress = (urlOfLastItemLoaded, itemsLoaded, itemsTotal) =&gt; {
@@ -222,86 +249,123 @@ loadManager.onLoad = () =&gt; {
 +  progressBarElem.style.transform = `scaleX(${progress})`;
 +};
 </pre>
-<p>À moins que vous ne vidiez votre cache et que votre connexion soit lente, vous ne verrez peut-être pas la barre de chargement.</p>
+<p>À moins que vous ne vidiez votre cache et que vous ayez une connexion lente, il est possible que vous ne voyiez
+pas la barre de chargement.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-all-textures.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-all-textures.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-all-textures.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<h2 id="-a-name-cors-a-chargement-de-textures-d-autres-origines"><a name="cors"></a> Chargement de textures d'autres origines</h2>
-<p>Pour utiliser des images d'autres serveurs, ces serveurs doivent envoyer les en-têtes corrects. Si ce n'est pas le cas, vous ne pouvez pas utiliser les images dans Three.js et vous obtiendrez une erreur. Si vous utilisez un serveur distant, assurez-vous qu'il envoie <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">les bons en-têtes</a>. Sinon, vous ne pourrez pas utiliser les images provenant de ce serveur.</p>
-<p>Par exemple <a href="https://imgur.com">imgur</a>, <a href="https://flickr.com">flickr</a>, et
-<a href="https://github.com">github</a> envoient des en-têtes vous permettant d'utiliser des images hébergées sur leurs serveurs avec Three.js. La plupart des autres sites web ne le font pas.</p>
-<h2 id="-a-name-memory-a-utilisation-de-la-m-moire"><a name="memory"></a> Utilisation de la mémoire</h2>
-<p>Les textures sont souvent la partie d'une application Three.js qui utilise le plus de mémoire. Il est important de comprendre qu'en <em>général</em>, les textures prennent <code class="notranslate" translate="no">width * height * 4 * 1.33</code> octets de mémoire.</p>
-<p>Remarquez que cela ne dit rien sur la compression. Je peux créer une image .jpg et régler sa compression à un niveau très élevé. Par exemple, disons que je souhaite créer une maison. A l'intérieur de la maison il y a une table et je décide de mettre cette texture de bois sur la surface supérieure de la table.</p>
+<h2 id="-a-name-cors-a-loading-textures-from-other-origins"><a name="cors"></a> Charger des textures depuis d'autres origines</h2>
+<p>Pour utiliser des images provenant d'autres serveurs, ces serveurs doivent envoyer les en-têtes corrects.
+S'ils ne le font pas, vous ne pouvez pas utiliser les images dans three.js et vous obtiendrez une erreur.
+Si vous gérez le serveur fournissant les images, assurez-vous qu'il
+<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">envoie les en-têtes corrects</a>.
+Si vous ne contrôlez pas le serveur hébergeant les images et qu'il n'envoie pas les
+en-têtes d'autorisation, vous ne pouvez pas utiliser les images de ce serveur.</p>
+<p>Par exemple, <a href="https://imgur.com">imgur</a>, <a href="https://flickr.com">flickr</a> et
+<a href="https://github.com">github</a> envoient tous des en-têtes vous permettant d'utiliser les images
+hébergées sur leurs serveurs dans three.js. La plupart des autres sites web ne le font pas.</p>
+<h2 id="-a-name-memory-a-memory-usage"><a name="memory"></a> Utilisation de la mémoire</h2>
+<p>Les textures sont souvent la partie d'une application three.js qui utilise le plus de mémoire. Il est important de comprendre
+qu'<em>en général</em>, les textures prennent <code class="notranslate" translate="no">largeur * hauteur * 4 * 1.33</code> octets de mémoire.</p>
+<p>Notez que cela ne dit rien sur la compression. Je peux créer une image .jpg et régler sa compression très élevée.
+Par exemple, disons que je créais une scène d'une maison. À l'intérieur de la maison, il y a une table
+et je décide de mettre cette texture de bois sur la surface supérieure de la table</p>
 <div class="threejs_center"><img class="border" src="../resources/images/compressed-but-large-wood-texture.jpg" align="center" style="width: 300px"></div>
 
-<p>Cette image ne pèse que 157ko, elle sera donc téléchargée relativement vite mais <a href="resources/images/compressed-but-large-wood-texture.jpg">sa taille est en réalité de 3024 x 3761 pixels</a>.
-En suivant l'équation ci-dessus</p>
+<p>Cette image ne fait que 157k, elle se téléchargera donc relativement rapidement, mais <a href="resources/images/compressed-but-large-wood-texture.jpg">sa taille est en réalité
+de 3024 x 3761 pixels</a>.
+En suivant l'équation ci-dessus, cela donne</p>
 <pre class="prettyprint showlinemods notranslate notranslate" translate="no">3024 * 3761 * 4 * 1.33 = 60505764.5
-</pre><p>Cette image prendra <strong>60 MEGA de MÉMOIRE !</strong> dans Three.js.
-Encore quelques textures comme celle-là et vous serez à court de mémoire.</p>
-<p>J'en parle car il est important de savoir que l'utilisation de textures a un coût caché. Pour que Three.js utilise la texture, il doit la transmettre au GPU et le GPU <em>en général</em> nécessite que les données de texture soient décompressées.</p>
-<p>La morale de l'histoire, c'est d'utiliser des textures petites en dimensions, pas seulement petites en taille de fichier. Petite en taille de fichier = rapide à télécharger. Petite en dimensions = prend moins de mémoire. Quelle est la bonne taille ? Aussi petite que possible et toujours aussi belle que nécessaire.</p>
-<h2 id="-a-name-format-a-jpg-ou-png"><a name="format"></a> JPG ou PNG</h2>
-<p>C'est à peu près la même chose qu'en HTML, en ce sens que les JPG ont une compression avec perte, les PNG ont une compression sans perte, donc les PNG sont généralement plus lents à télécharger. Mais, les PNG prennent en charge la transparence. Les PNG sont aussi probablement le format approprié pour les données non-image comme les normal maps, et d'autres types de map non-image que nous verrons plus tard.</p>
-<p>Il est important de se rappeler qu'un JPG n'utilise pas moins de mémoire qu'un PNG en WebGL. Voir ci-dessus.</p>
-<h2 id="-a-name-filtering-and-mips-a-filtrage-et-mips"><a name="filtering-and-mips"></a> Filtrage et Mips</h2>
+</pre><p>Cette image prendra <strong>60 MÉGAOCTETS DE MÉMOIRE !</strong> dans three.js.
+Quelques textures comme celle-là et vous serez à court de mémoire.</p>
+<p>Je soulève ce point car il est important de savoir que l'utilisation des textures a un coût caché.
+Pour que three.js puisse utiliser la texture, il doit la transmettre au GPU, et le
+GPU <em>en général</em> nécessite que les données de la texture soient décompressées.</p>
+<p>La morale de l'histoire est de rendre vos textures petites en dimensions, pas seulement petites
+en taille de fichier. Petite taille de fichier = téléchargement rapide. Petites dimensions = prend
+moins de mémoire. Quelle taille devraient-elles avoir ?
+Aussi petites que possible tout en conservant l'apparence dont vous avez besoin.</p>
+<h2 id="-a-name-format-a-jpg-vs-png"><a name="format"></a> JPG vs PNG</h2>
+<p>C'est à peu près la même chose qu'en HTML classique : les JPG ont une compression avec perte,
+les PNG ont une compression sans perte, donc les PNG sont généralement plus lents à télécharger.
+Mais les PNG supportent la transparence. Les PNG sont également probablement le format
+approprié pour les données non-image comme les normal maps et d'autres types de maps non-image que nous verrons plus tard.</p>
+<p>Il est important de se rappeler qu'un JPG n'utilise
+pas moins de mémoire qu'un PNG dans WebGL. Voir ci-dessus.</p>
+<h2 id="-a-name-filtering-and-mips-a-filtering-and-mips"><a name="filtering-and-mips"></a> Filtrage et Mips</h2>
 <p>Appliquons cette texture 16x16</p>
 <div class="threejs_center"><img src="../resources/images/mip-low-res-enlarged.png" class="nobg" align="center"></div>
 
-<p>sur un cube</p>
+<p>À un cube</p>
 <div class="spread"><div data-diagram="filterCube"></div></div>
 
-<p>Rétrécissons-le au max</p>
+<p>Dessinons ce cube très petit</p>
 <div class="spread"><div data-diagram="filterCubeSmall"></div></div>
 
-<p>Hmmm, je suppose que c'est trop difficile à voir. Agrandissons-le un peu</p>
+<p>Hmmm, je suppose que c'est difficile à voir. Agrandissons ce tout petit cube</p>
 <div class="spread"><div data-diagram="filterCubeSmallLowRes"></div></div>
 
-<p>Comment le GPU sait-il quelles couleurs créer pour chaque pixel qu'il dessine pour le petit cube ? Et si le cube était si petit qu'il ne faisait que 1 ou 2 pixels ?</p>
+<p>Comment le GPU sait-il quelles couleurs donner à chaque pixel qu'il dessine pour le petit cube ?
+Que se passerait-il si le cube était si petit qu'il ne fasse qu'un ou deux pixels ?</p>
 <p>C'est à cela que sert le filtrage.</p>
-<p>S'il s'agissait de Photoshop, il ferait la moyenne de presque tous les pixels ensemble pour déterminer la couleur de ces 1 ou 2 pixels. Ce serait une opération très lente. Les GPU résolvent ce problème à l'aide de mipmaps.</p>
-<p>Le MIP mapping consiste à envoyer au processeur graphique (GPU) des échantillons de texture de résolutions décroissantes qui seront utilisés à la place de la texture originale, en fonction de la distance du point de vue à l'objet texturé et du niveau de détails nécessaire. Pour l'image précédente seront produites les mêmes images avec des résolutions inférieures jusqu'à obtenir 1 x 1 pixel.</p>
+<p>Si c'était Photoshop, Photoshop ferait la moyenne de presque tous les pixels pour déterminer la couleur
+à donner à ces 1 ou 2 pixels. Ce serait une opération très lente. Les GPU résolvent ce problème
+en utilisant les mipmaps.</p>
+<p>Les mips sont des copies de la texture, chacune faisant la moitié de la largeur et la moitié de la hauteur du mip précédent,
+où les pixels ont été mélangés pour créer le mip suivant plus petit. Les mips sont créés
+jusqu'à ce que l'on arrive à un mip de 1x1 pixel. Pour l'image ci-dessus, tous les mips ressembleraient
+à ceci</p>
 <div class="threejs_center"><img src="../resources/images/mipmap-low-res-enlarged.png" class="nobg" align="center"></div>
 
-<p>Désormais, lorsque le cube est dessiné si petit qu'il ne fait que 1 ou 2 pixels de large, le GPU peut choisir d'utiliser uniquement le plus petit ou le deuxième plus petit niveau de mip pour décider de la couleur du petit cube.</p>
-<p>Dans Three.js, vous pouvez choisir ce qui se passe à la fois lorsque la texture est dessinée plus grande que sa taille d'origine et ce qui se passe lorsqu'elle est dessinée plus petite que sa taille d'origine.</p>
-<p>Pour définir le filtre lorsque la texture est dessinée plus grande que sa taille d'origine, définissez la propriété <a href="/docs/#api/en/textures/Texture#magFilter"><code class="notranslate" translate="no">texture.magFilter</code></a> sur <code class="notranslate" translate="no">THREE.NearestFilter</code> ou
- <code class="notranslate" translate="no">THREE.LinearFilter</code>.  <code class="notranslate" translate="no">NearestFilter</code> signifie simplement choisir le pixel le plus proche dans la texture d'origine. Avec une texture basse résolution, cela vous donne un look très pixelisé comme Minecraft.</p>
-<p><code class="notranslate" translate="no">LinearFilter</code> signifie choisir les 4 pixels de la texture qui sont les plus proches de l'endroit où nous devrions choisir une couleur et les mélanger dans les proportions appropriées par rapport à la distance entre le point réel et chacun des 4 pixels.</p>
+<p>Maintenant, lorsque le cube est dessiné si petit qu'il ne fait qu'un ou deux pixels, le GPU peut choisir
+d'utiliser uniquement le mip le plus petit ou le mip juste avant le plus petit pour décider de la couleur
+à donner au petit cube.</p>
+<p>Dans three.js, vous pouvez choisir ce qui se passe à la fois lorsque la texture est dessinée
+plus grande que sa taille d'origine et ce qui se passe lorsqu'elle est dessinée plus petite que sa
+taille d'origine.</p>
+<p>Pour définir le filtre lorsque la texture est dessinée plus grande que sa taille d'origine,
+vous définissez la propriété <a href="/docs/#api/en/textures/Texture#magFilter"><code class="notranslate" translate="no">texture.magFilter</code></a> sur <code class="notranslate" translate="no">THREE.NearestFilter</code> ou
+ <code class="notranslate" translate="no">THREE.LinearFilter</code>.  <code class="notranslate" translate="no">NearestFilter</code> signifie
+simplement choisir le pixel unique le plus proche de la texture d'origine. Avec une texture
+basse résolution, cela donne un aspect très pixélisé comme dans Minecraft.</p>
+<p><code class="notranslate" translate="no">LinearFilter</code> signifie choisir les 4 pixels de la texture qui sont les plus proches
+de l'endroit où nous devrions choisir une couleur et les mélanger dans les
+proportions appropriées par rapport à la distance entre le point réel et
+chacun des 4 pixels.</p>
 <div class="spread">
   <div>
     <div data-diagram="filterCubeMagNearest" style="height: 250px;"></div>
-    <div class="code">Nearest</div>
+    <div class="code">Plus proche</div>
   </div>
   <div>
     <div data-diagram="filterCubeMagLinear" style="height: 250px;"></div>
-    <div class="code">Linear</div>
+    <div class="code">Linéaire</div>
   </div>
 </div>
 
-<p>Pour définir le filtre lorsque la texture est dessinée plus petite que sa taille d'origine, définissez la propriété <a href="/docs/#api/en/textures/Texture#minFilter"><code class="notranslate" translate="no">texture.minFilter</code></a> sur l'une des 6 valeurs :</p>
+<p>Pour définir le filtre lorsque la texture est dessinée plus petite que sa taille d'origine,
+vous définissez la propriété <a href="/docs/#api/en/textures/Texture#minFilter"><code class="notranslate" translate="no">texture.minFilter</code></a> sur l'une des 6 valeurs suivantes.</p>
 <ul>
 <li><p><code class="notranslate" translate="no">THREE.NearestFilter</code></p>
-<p> comme ci-dessus, choisissez le pixel le plus proche dans la texture</p>
+<p> identique à ci-dessus, choisir le pixel le plus proche dans la texture</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.LinearFilter</code></p>
-<p> comme ci-dessus, choisissez 4 pixels dans la texture et mélangez-les</p>
+<p> identique à ci-dessus, choisir 4 pixels de la texture et les mélanger</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.NearestMipmapNearestFilter</code></p>
-<p> choisissez le mip approprié puis choisissez un pixel</p>
+<p> choisir le mip approprié puis choisir un pixel</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.NearestMipmapLinearFilter</code></p>
-<p> choisissez 2 mips, choisissez un pixel de chacun, mélangez les 2 pixels</p>
+<p> choisir 2 mips, choisir un pixel de chaque, mélanger les 2 pixels</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.LinearMipmapNearestFilter</code></p>
-<p> choisissez le mip approprié puis choisissez 4 pixels et mélangez-les</p>
+<p> choisir le mip approprié puis choisir 4 pixels et les mélanger</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.LinearMipmapLinearFilter</code></p>
-<p>choisissez 2 mips, choisissez 4 pixels de chacun et mélangez les 8 en 1 pixel</p>
+<p>choisir 2 mips, choisir 4 pixels de chaque et mélanger les 8 en 1 pixel</p>
 </li>
 </ul>
 <p>Voici un exemple montrant les 6 paramètres</p>
@@ -325,70 +389,97 @@ Encore quelques textures comme celle-là et vous serez à court de mémoire.</p>
         font-size: small;
         border-radius: .5em;
         line-height: 1.2;
-        user-select: none;">click to<br>change<br>texture</div>
+        user-select: none;">cliquer pour<br>changer la<br>texture</div>
     </div>
-    <div class="filter-caption" style="left: 0.5em; top: 0.5em;">nearest</div>
-    <div class="filter-caption" style="width: 100%; text-align: center; top: 0.5em;">linear</div>
-    <div class="filter-caption" style="right: 0.5em; text-align: right; top: 0.5em;">nearest<br>mipmap<br>nearest</div>
-    <div class="filter-caption" style="left: 0.5em; text-align: left; bottom: 0.5em;">nearest<br>mipmap<br>linear</div>
-    <div class="filter-caption" style="width: 100%; text-align: center; bottom: 0.5em;">linear<br>mipmap<br>nearest</div>
-    <div class="filter-caption" style="right: 0.5em; text-align: right; bottom: 0.5em;">linear<br>mipmap<br>linear</div>
+    <div class="filter-caption" style="left: 0.5em; top: 0.5em;">plus proche</div>
+    <div class="filter-caption" style="width: 100%; text-align: center; top: 0.5em;">linéaire</div>
+    <div class="filter-caption" style="right: 0.5em; text-align: right; top: 0.5em;">plus proche<br>mipmap<br>plus proche</div>
+    <div class="filter-caption" style="left: 0.5em; text-align: left; bottom: 0.5em;">plus proche<br>mipmap<br>linéaire</div>
+    <div class="filter-caption" style="width: 100%; text-align: center; bottom: 0.5em;">linéaire<br>mipmap<br>plus proche</div>
+    <div class="filter-caption" style="right: 0.5em; text-align: right; bottom: 0.5em;">linéaire<br>mipmap<br>linéaire</div>
   </div>
 </div>
 
-<p>Une chose à noter est que la texture en haut/gauche et la haut/milieu utilisent NearestFilter et LinearFilter et pas les mips. À cause de cela, ils scintillent au loin car le GPU sélectionne les pixels de la texture d'origine. Sur la gauche, un seul pixel est choisi et au milieu, 4 sont choisis et mélangés, mais cela ne suffit pas pour obtenir une couleur représentative. Les 4 autres bandes s'en sortent mieux, avec la meilleure qualité obtenue en bas à droite grâce au LinearMipmapLinearFilter.</p>
-<p>Si vous cliquez sur l'image ci-dessus, elle basculera entre la texture que nous avons utilisée ci-dessus et une texture où chaque niveau de mip est d'une couleur différente.</p>
+<p>Une chose à remarquer est que le coin supérieur gauche et le milieu supérieur utilisant <code class="notranslate" translate="no">NearestFilter</code> et <code class="notranslate" translate="no">LinearFilter</code>
+n'utilisent pas les mips. De ce fait, ils scintillent au loin car le GPU sélectionne
+des pixels de la texture d'origine. À gauche, un seul pixel est choisi et
+au milieu, 4 sont choisis et mélangés, mais ce n'est pas suffisant pour obtenir une bonne
+couleur représentative. Les 4 autres bandes s'en sortent mieux,
+celle en bas à droite, <code class="notranslate" translate="no">LinearMipmapLinearFilter</code>, étant la meilleure.</p>
+<p>Si vous cliquez sur l'image ci-dessus, elle basculera entre la texture que nous avons utilisée ci-dessus
+et une texture où chaque niveau de mip est d'une couleur différente.</p>
 <div class="threejs_center">
   <div data-texture-diagram="differentColoredMips"></div>
 </div>
 
-<p>Cela clarifie les choses. Vous pouvez voir en haut à gauche et en haut au milieu que le premier mip est utilisé sur toute la distance. En haut à droite et en bas au milieu, vous pouvez clairement voir où un mip différent est utilisé.</p>
-<p>En revenant à la texture d'origine, vous pouvez voir que celle en bas à droite est la plus douce avec la plus haute qualité. Vous pourriez vous demander pourquoi ne pas toujours utiliser ce mode. La raison la plus évidente, c'est que parfois vous voulez que les choses soient pixelisées pour un look rétro ou pour une autre raison. La deuxième raison la plus courante, c' est que lire 8 pixels et les mélanger est plus lent que lire 1 pixel et mélanger. Bien qu'il soit peu probable qu'une seule texture fasse la différence entre rapide et lente à mesure que nous progressons dans ces articles, nous finirons par avoir des matériaux qui utilisent 4 ou 5 textures à la fois. 4 textures * 8 pixels par texture correspondent à 32 pixels pour chaque pixel rendu. Cela peut être particulièrement important à considérer sur les appareils mobiles.</p>
-<h2 id="-a-name-uvmanipulation-a-r-p-tition-d-calage-rotation-emballage-d-une-texture"><a name="uvmanipulation"></a> Répétition, décalage, rotation, emballage d'une texture</h2>
+<p>Cela rend plus clair
+ce qui se passe. Vous pouvez voir en haut à gauche et au milieu supérieur que le premier mip est utilisé jusqu'au loin.
+En haut à droite et au milieu inférieur, vous pouvez clairement voir où un mip différent
+est utilisé.</p>
+<p>En revenant à la texture d'origine, vous pouvez voir que celle en bas à droite est la plus lisse,
+de la plus haute qualité. Vous pourriez vous demander pourquoi ne pas toujours utiliser ce mode. La raison
+la plus évidente est que parfois vous voulez que les choses soient pixélisées pour un look rétro ou pour une autre raison.
+La raison suivante la plus courante est que lire 8 pixels et les mélanger est plus lent
+que de lire 1 pixel et de le mélanger. Bien qu'il soit peu probable qu'une seule texture
+fasse la différence entre rapide et lent, à mesure que nous progresserons dans ces articles,
+nous aurons finalement des matériaux qui utilisent 4 ou 5 textures en même temps.
+4 textures * 8 pixels par texture, c'est rechercher 32 pixels pour chaque pixel rendu.
+Cela peut être particulièrement important à considérer sur les appareils mobiles.</p>
+<h2 id="-a-name-uvmanipulation-a-repeating-offseting-rotating-wrapping-a-texture"><a name="uvmanipulation"></a> Répétition, décalage, rotation, habillage d'une texture</h2>
 <p>Les textures ont des paramètres pour la répétition, le décalage et la rotation d'une texture.</p>
-<p>Par défaut, les textures dans three.js ne se répètent pas. Pour définir si une texture se répète ou non, il existe 2 propriétés, <a href="/docs/#api/en/textures/Texture#wrapS"><code class="notranslate" translate="no">wrapS</code></a> pour un habillage horizontal et <a href="/docs/#api/en/textures/Texture#wrapT"><code class="notranslate" translate="no">wrapT</code></a> pour un habillage vertical.  </p>
-<p>Elles peuvent être définies sur les valeurs suivantes :</p>
+<p>Par défaut, les textures dans three.js ne se répètent pas. Pour définir si une
+texture se répète ou non, il existe 2 propriétés : <a href="/docs/#api/en/textures/Texture#wrapS"><code class="notranslate" translate="no">wrapS</code></a> pour l'habillage horizontal
+et <a href="/docs/#api/en/textures/Texture#wrapT"><code class="notranslate" translate="no">wrapT</code></a> pour l'habillage vertical.</p>
+<p>Ils peuvent être définis sur l'une des valeurs suivantes :</p>
 <ul>
 <li><p><code class="notranslate" translate="no">THREE.ClampToEdgeWrapping</code></p>
-<p> le dernier pixel de chaque bord est répété indéfiniment</p>
+<p> le dernier pixel sur chaque bord est répété indéfiniment</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.RepeatWrapping</code></p>
 <p> la texture est répétée</p>
 </li>
 <li><p><code class="notranslate" translate="no">THREE.MirroredRepeatWrapping</code></p>
-<p> la texture est reflétée et répétée</p>
+<p> la texture est mise en miroir et répétée</p>
 </li>
 </ul>
-<p>Par exemple pour activer le wrapping dans les deux sens :</p>
+<p>Par exemple, pour activer l'habillage dans les deux directions :</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.wrapS = THREE.RepeatWrapping;
 someTexture.wrapT = THREE.RepeatWrapping;
 </pre>
-<p>La répétition est définie avec la propriété <code class="notranslate" translate="no">repeat</code>.</p>
+<p>La répétition est définie avec la propriété [repeat] repeat.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const timesToRepeatHorizontally = 4;
 const timesToRepeatVertically = 2;
 someTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);
 </pre>
-<p>Le décalage de la texture peut être effectué en définissant la propriété <code class="notranslate" translate="no">offset</code>. Les textures sont décalées avec des unités où 1 unité = 1 taille de texture. En d'autres termes 0 = aucun décalage et 1 = décalage d'une quantité de texture complète.</p>
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const xOffset = .5;   // décalage de la moitié de la texture
-const yOffset = .25;  // décalage d'un quart
+<p>Le décalage de la texture peut être effectué en définissant la propriété <code class="notranslate" translate="no">offset</code>. Les textures
+sont décalées avec des unités où 1 unité = 1 taille de texture. Autrement dit, 0 = pas de décalage
+et 1 = décalage d'une quantité de texture complète.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const xOffset = .5;   // offset by half the texture
+const yOffset = .25;  // offset by 1/4 the texture
 someTexture.offset.set(xOffset, yOffset);
 </pre>
-<p>La rotation de la texture peut être définie en définissant la propriété <code class="notranslate" translate="no">rotation</code> en radians ainsi que la propriété <code class="notranslate" translate="no">center</code> pour choisir le centre de rotation. La valeur par défaut est 0,0 qui tourne à partir du coin inférieur gauche. Comme l'<code class="notranslate" translate="no">offset</code>, l'unité est la taille de texture, donc régler <code class="notranslate" translate="no">center</code> sur <code class="notranslate" translate="no">.5, .5</code> tournerait autour du centre de la texture.</p>
+<p>La rotation de la texture peut être définie en définissant la propriété <code class="notranslate" translate="no">rotation</code> en radians
+ainsi que la propriété <code class="notranslate" translate="no">center</code> pour choisir le centre de rotation.
+Elle est par défaut à 0,0, ce qui correspond à une rotation depuis le coin inférieur gauche. Comme pour le décalage,
+ces unités sont en taille de texture, donc les définir à <code class="notranslate" translate="no">.5, .5</code> effectuerait une rotation
+autour du centre de la texture.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.center.set(.5, .5);
 someTexture.rotation = THREE.MathUtils.degToRad(45);
 </pre>
-<p>Modifions l'échantillon supérieur ci-dessus pour jouer avec ces valeurs.</p>
-<p>Tout d'abord, nous allons garder une référence à la texture afin que nous puissions la manipuler</p>
+<p>Modifions l'exemple du haut ci-dessus pour jouer avec ces valeurs</p>
+<p>Tout d'abord, nous allons conserver une référence à la texture afin de pouvoir la manipuler</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const texture = loader.load('resources/images/wall.jpg');
 const material = new THREE.MeshBasicMaterial({
 -  map: loader.load('resources/images/wall.jpg');
 +  map: texture,
 });
 </pre>
-<p>Ensuite, utilisons <a href="https://github.com/georgealways/lil-gui">lil-gui</a> pour fournir une interface simple.</p>
+<p>Ensuite, nous utiliserons à nouveau <a href="https://github.com/georgealways/lil-gui">lil-gui</a> pour fournir une interface simple.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
 </pre>
-<p>Comme nous l'avons fait dans les exemples précédents avec lil-gui, nous utiliserons une classe simple pour donner à lil-gui un objet qu'il peut manipuler en degrés mais qu'il définira en radians.</p>
+<p>Comme nous l'avons fait dans les exemples précédents avec lil-gui, nous utiliserons une classe simple pour
+donner à lil-gui un objet qu'il peut manipuler en degrés
+mais qui définira une propriété en radians.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DegRadHelper {
   constructor(obj, prop) {
     this.obj = obj;
@@ -402,7 +493,9 @@ const material = new THREE.MeshBasicMaterial({
   }
 }
 </pre>
-<p>Nous avons également besoin d'une classe qui convertira une chaîne telle que <code class="notranslate" translate="no">"123"</code> en un nombre tel que 123, car Three.js nécessite des nombres pour les paramètres d'énumération tels que <code class="notranslate" translate="no">wrapS</code> et <code class="notranslate" translate="no">wrapT</code>, mais lil-gui n'utilise que des chaînes pour les énumérations.</p>
+<p>Nous avons également besoin d'une classe qui convertira une chaîne de caractères comme <code class="notranslate" translate="no">"123"</code> en un
+nombre comme <code class="notranslate" translate="no">123</code>, car three.js nécessite des nombres pour les paramètres d'énumération
+comme <code class="notranslate" translate="no">wrapS</code> et <code class="notranslate" translate="no">wrapT</code>, mais lil-gui n'utilise que des chaînes de caractères pour les énumérations.</p>
 <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class StringToNumberHelper {
   constructor(obj, prop) {
     this.obj = obj;
@@ -443,27 +536,20 @@ gui.add(texture.center, 'y', -.5, 1.5, .01).name('texture.center.y');
 gui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)
   .name('texture.rotation');
 </pre>
-<p>La dernière chose à noter à propos de l'exemple est que si vous modifiez <code class="notranslate" translate="no">wrapS</code> ou <code class="notranslate" translate="no">wrapT</code> sur la texture, vous devez également définir <a href="/docs/#api/en/textures/Texture#needsUpdate"><code class="notranslate" translate="no">texture.needsUpdate</code></a>
-afin que three.js sache qu'il faut mettre à jour ces paramètres. Les autres paramètres sont automatiquement appliqués.</p>
+<p>La dernière chose à noter à propos de l'exemple est que si vous changez <code class="notranslate" translate="no">wrapS</code> ou
+<code class="notranslate" translate="no">wrapT</code> sur la texture, vous devez également définir <a href="/docs/#api/en/textures/Texture#needsUpdate"><code class="notranslate" translate="no">texture.needsUpdate</code></a>
+afin que three.js sache qu'il doit appliquer ces paramètres. Les autres paramètres sont appliqués automatiquement.</p>
 <p></p><div translate="no" class="threejs_example_container notranslate">
   <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-adjust.html"></iframe></div>
-  <a class="threejs_center" href="/manual/examples/textured-cube-adjust.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
+  <a class="threejs_center" href="/manual/examples/textured-cube-adjust.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
 </div>
 
 <p></p>
-<p>Ce n'est qu'une étape dans le sujet des textures. À un moment donné, nous passerons en revue les coordonnées de texture ainsi que 9 autres types de textures pouvant être appliquées aux matériaux. </p>
-<p>Pour le moment, passons aux <a href="lights.html">lumières</a>.</p>
-<!--
-alpha
-ao
-env
-light
-specular
-bumpmap ?
-normalmap ?
-metalness
-roughness
--->
+<p>Ce n'est qu'une étape dans le sujet des textures. À un moment donné, nous aborderons
+les coordonnées de texture ainsi que 9 autres types de textures qui peuvent être appliqués
+aux matériaux.</p>
+<p>Pour l'instant, passons aux <a href="lights.html">lumières</a>.</p>
+<!-- alpha ao env light specular bumpmap ? normalmap ? metalness roughness -->
 <p><link rel="stylesheet" href="../resources/threejs-textures.css"></p>
 <script type="module" src="../resources/threejs-textures.js"></script>
 
@@ -477,4 +563,4 @@ roughness
 
 
 
-</body></html>
+</body></html>

+ 306 - 4
manual/fr/tips.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Tips</title>
+    <title>Conseils</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,12 +22,314 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Tips</h1>
+        <h1>Conseils</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/tips.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Cet article est une collection de petits problèmes que vous pourriez rencontrer
+en utilisant three.js, qui semblaient trop mineurs pour avoir leur propre article.</p>
+<hr>
+<p><a id="screenshot" data-toc="Faire une capture d'écran"></a></p>
+<h1 id="taking-a-screenshot-of-the-canvas">Faire une capture d'écran du Canvas</h1>
+<p>Dans le navigateur, il y a effectivement 2 fonctions qui permettent de prendre une capture d'écran.
+L'ancienne
+<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL"><code class="notranslate" translate="no">canvas.toDataURL</code></a>
+et la nouvelle, meilleure,
+<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob"><code class="notranslate" translate="no">canvas.toBlob</code></a></p>
+<p>On pourrait donc penser qu'il serait facile de prendre une capture d'écran en ajoutant simplement du code comme</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
++&lt;button id="screenshot" type="button"&gt;Enregistrer...&lt;/button&gt;
+</pre>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
+elem.addEventListener('click', () =&gt; {
+  canvas.toBlob((blob) =&gt; {
+    saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
+  });
+});
+
+const saveBlob = (function() {
+  const a = document.createElement('a');
+  document.body.appendChild(a);
+  a.style.display = 'none';
+  return function saveData(blob, fileName) {
+     const url = window.URL.createObjectURL(blob);
+     a.href = url;
+     a.download = fileName;
+     a.click();
+  };
+}());
+</pre>
+<p>Voici l'exemple de <a href="responsive.html">l'article sur la réactivité</a>
+avec le code ci-dessus ajouté et un peu de CSS pour positionner le bouton</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-bad.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-screenshot-bad.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Lorsque je l'ai essayé, j'ai obtenu cette capture d'écran</p>
+<div class="threejs_center"><img src="../resources/images/screencapture-413x313.png"></div>
+
+<p>Oui, c'est juste une image noire.</p>
+<p>Il est possible que cela ait fonctionné pour vous selon votre navigateur/OS mais en général,
+il est peu probable que cela fonctionne.</p>
+<p>Le problème est que, pour des raisons de performance et de compatibilité, par défaut, le navigateur
+efface le buffer de dessin d'un canvas WebGL après y avoir dessiné.</p>
+<p>La solution est d'appeler votre code de rendu juste avant la capture.</p>
+<p>Dans notre code, nous devons ajuster quelques éléments. D'abord, séparons
+le code de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const state = {
++  time: 0,
++};
+
+-function render(time) {
+-  time *= 0.001;
++function render() {
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+  cubes.forEach((cube, ndx) =&gt; {
+    const speed = 1 + ndx * .1;
+-    const rot = time * speed;
++    const rot = state.time * speed;
+    cube.rotation.x = rot;
+    cube.rotation.y = rot;
+  });
+
+  renderer.render(scene, camera);
+
+-  requestAnimationFrame(render);
+}
+
++function animate(time) {
++  state.time = time * 0.001;
++
++  render();
++
++  requestAnimationFrame(animate);
++}
++requestAnimationFrame(animate);
+</pre>
+<p>Maintenant que <code class="notranslate" translate="no">render</code> ne s'occupe que du rendu effectif,
+nous pouvons l'appeler juste avant de capturer le canvas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
+elem.addEventListener('click', () =&gt; {
++  render();
+  canvas.toBlob((blob) =&gt; {
+    saveBlob(blob, `screencapture-${canvas.width}x-${canvas.height}.png`);
+  });
+});
+</pre>
+<p>Et maintenant, ça devrait marcher.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-good.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-screenshot-good.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Pour une solution différente, voir l'élément suivant.</p>
+<hr>
+<p><a id="preservedrawingbuffer" data-toc="Empêcher l'effacement du Canvas"></a></p>
+<h1 id="preventing-the-canvas-being-cleared">Empêcher l'effacement du canvas</h1>
+<p>Supposons que vous vouliez permettre à l'utilisateur de peindre avec un objet
+animé. Vous devez passer <code class="notranslate" translate="no">preserveDrawingBuffer: true</code> lorsque
+vous créez le <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a>. Cela empêche le navigateur d'effacer
+le canvas. Vous devez également dire à three.js de ne pas effacer
+le canvas.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
+-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
++const renderer = new THREE.WebGLRenderer({
++  canvas,
++  preserveDrawingBuffer: true,
++  alpha: true,
++});
++renderer.autoClearColor = false;
+</pre>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-preservedrawingbuffer.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-preservedrawingbuffer.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Notez que si vous étiez sérieux au sujet de la création d'un programme de dessin, ce ne serait pas une
+solution car le navigateur effacera toujours le canvas chaque fois que sa
+résolution change. Nous modifions sa résolution en fonction de sa taille d'affichage. Sa taille d'affichage
+change lorsque la fenêtre change de taille. Cela inclut lorsque l'utilisateur télécharge
+un fichier, même dans un autre onglet, et que le navigateur ajoute une barre d'état. Cela inclut également lorsque
+l'utilisateur tourne son téléphone et que le navigateur passe du mode portrait au mode paysage.</p>
+<p>Si vous vouliez vraiment créer un programme de dessin, vous devriez
+<a href="rendertargets.html">faire le rendu sur une texture en utilisant une cible de rendu</a>.</p>
+<hr>
+<p><a id="tabindex" data-toc="Obtenir la saisie clavier d'un Canvas"></a></p>
+<h1 id="getting-keyboard-input">Obtenir la saisie clavier</h1>
+<p>Tout au long de ces tutoriels, nous avons souvent attaché des écouteurs d'événements au <code class="notranslate" translate="no">canvas</code>.
+Bien que de nombreux événements fonctionnent, un qui ne fonctionne pas par défaut est l'événement
+clavier.</p>
+<p>Pour obtenir les événements clavier, définissez le <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex"><code class="notranslate" translate="no">tabindex</code></a>
+du canvas à 0 ou plus. Par exemple :</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas tabindex="0"&gt;&lt;/canvas&gt;
+</pre>
+<p>Cela finit cependant par causer un nouveau problème. Tout élément ayant un <code class="notranslate" translate="no">tabindex</code> défini
+sera mis en évidence lorsqu'il aura le focus. Pour résoudre ce problème, définissez son contour de focus CSS
+à none (aucun)</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">canvas:focus {
+  outline:none;
+}
+</pre>
+<p>Pour démontrer, voici 3 canvas</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c1"&gt;&lt;/canvas&gt;
+&lt;canvas id="c2" tabindex="0"&gt;&lt;/canvas&gt;
+&lt;canvas id="c3" tabindex="1"&gt;&lt;/canvas&gt;
+</pre>
+<p>et un peu de css juste pour le dernier canvas</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c3:focus {
+    outline: none;
+}
+</pre>
+<p>Notez que vous ne pouvez pas faire en sorte que le premier canvas accepte les saisies clavier.
+Le deuxième canvas le peut, mais il est mis en évidence. Le 3ème
+canvas a les deux solutions appliquées.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-tabindex.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-tabindex.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<hr>
+<p><a id="transparent-canvas" data-toc="Rendre le Canvas Transparent"></a></p>
+<h1 id="making-the-canvas-transparent">Rendre le Canvas Transparent</h1>
+<p>Par défaut, THREE.js rend le canvas opaque. Si vous voulez que le
+canvas soit transparent, passez <a href="/docs/#api/en/renderers/WebGLRenderer#alpha"><code class="notranslate" translate="no">alpha:true</code></a> lorsque vous créez
+le <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
+-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
++const renderer = new THREE.WebGLRenderer({
++  canvas,
++  alpha: true,
++});
+</pre>
+<p>Vous voudrez probablement aussi lui dire que vos résultats <strong>n'utilisent pas</strong> l'alpha prémultiplié</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
+const renderer = new THREE.WebGLRenderer({
+  canvas,
+  alpha: true,
++  premultipliedAlpha: false,
+});
+</pre>
+<p>Three.js utilise par défaut <a href="/docs/#api/en/renderers/WebGLRenderer#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: true</code></a> pour le canvas,
+mais par défaut, les matériaux génèrent <a href="/docs/#api/en/materials/Material#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: false</code></a>.</p>
+<p>Si vous souhaitez mieux comprendre quand utiliser ou non l'alpha prémultiplié,
+voici <a href="https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre">un bon article à ce sujet</a>.</p>
+<p>En tout cas, configurons un exemple simple avec un canvas transparent.</p>
+<p>Nous avons appliqué les paramètres ci-dessus à l'exemple de <a href="responsive.html">l'article sur la réactivité</a>.
+Rendons également les matériaux plus transparents.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
+-  const material = new THREE.MeshPhongMaterial({color});
++  const material = new THREE.MeshPhongMaterial({
++    color,
++    opacity: 0.5,
++  });
+
+...
+</pre>
+<p>Et ajoutons un peu de contenu HTML</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="content"&gt;
++    &lt;div&gt;
++      &lt;h1&gt;Cubes-R-Us !&lt;/h1&gt;
++      &lt;p&gt;Nous fabriquons les meilleurs cubes !&lt;/p&gt;
++    &lt;/div&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>ainsi qu'un peu de CSS pour placer le canvas devant</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">body {
+    margin: 0;
+}
+#c {
+    width: 100%;
+    height: 100%;
+    display: block;
++    position: fixed;
++    left: 0;
++    top: 0;
++    z-index: 2;
++    pointer-events: none;
+}
++#content {
++  font-size: 7vw;
++  font-family: sans-serif;
++  text-align: center;
++  width: 100%;
++  height: 100%;
++  display: flex;
++  justify-content: center;
++  align-items: center;
++}
+</pre>
+<p>Notez que <code class="notranslate" translate="no">pointer-events: none</code> rend le canvas invisible aux événements de souris
+et tactiles afin que vous puissiez sélectionner le texte en dessous.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-transparent-canvas.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-transparent-canvas.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<hr>
+<p><a id="html-background" data-toc="Utiliser three.js comme fond en HTML"></a></p>
+<h1 id="making-your-background-a-three-js-animation">Faire de votre arrière-plan une animation three.js</h1>
+<p>Une question courante est de savoir comment faire en sorte qu'une animation three.js serve d'arrière-plan à
+une page web.</p>
+<p>Il y a 2 façons évidentes.</p>
+<ul>
+<li>Définir la propriété CSS <code>position</code> du canvas sur <code>fixed</code> comme dans</li>
+</ul>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
+ position: fixed;
+ left: 0;
+ top: 0;
+ ...
+}
+</pre>
+<p>Vous pouvez voir cette solution exacte dans l'exemple précédent. Il suffit de définir <code>z-index</code> à -1
+et les cubes apparaîtront derrière le texte.</p>
+<p>Un petit inconvénient de cette solution est que votre JavaScript doit s'intégrer à la page
+et si vous avez une page complexe, vous devez vous assurer qu'aucun des scripts JavaScript de votre
+visualisation three.js n'entre en conflit avec le JavaScript effectuant d'autres tâches dans la page.</p>
+<ul>
+<li>Utiliser une <code>iframe</code></li>
+</ul>
+<p>C'est la solution utilisée sur <a href="/">la page d'accueil de ce site</a>.</p>
+<p>Dans votre page web, insérez simplement une iframe, par exemple :</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;iframe id="background" src="responsive.html"&gt;
+&lt;div&gt;
+  Votre contenu ici.
+&lt;/div&gt;
+</pre>
+<p>Ensuite, stylisez l'iframe pour qu'elle remplisse la fenêtre et soit en arrière-plan,
+ce qui est essentiellement le même code que nous avons utilisé ci-dessus pour le canvas,
+sauf que nous devons également définir <code>border</code> à <code>none</code> car les iframes ont
+une bordure par défaut.</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">#background {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
+    z-index: -1;
+    border: none;
+    pointer-events: none;
+}
+</pre><p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-html-background.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/tips-html-background.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
 
         </div>
       </div>

+ 364 - 5
manual/fr/transparency.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Transparency</title>
+    <title>Transparence</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Transparency">
+    <meta name="twitter:title" content="Three.js – Transparence">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,371 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Transparency</h1>
+        <h1>Transparence</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/transparency.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>La transparence dans three.js est à la fois facile et difficile.</p>
+<p>Nous allons d'abord aborder la partie facile. Créons une
+scène avec 8 cubes placés sur une grille 2x2x2.</p>
+<p>Nous allons commencer par l'exemple de
+<a href="rendering-on-demand.html">l'article sur le rendu à la demande</a>
+qui contenait 3 cubes et le modifier pour en avoir 8. Modifions d'abord notre
+fonction <code class="notranslate" translate="no">makeInstance</code> pour qu'elle prenne
+x, y et z</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function makeInstance(geometry, color) {
++function makeInstance(geometry, color, x, y, z) {
+  const material = new THREE.MeshPhongMaterial({color});
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+-  cube.position.x = x;
++  cube.position.set(x, y, z);
+
+  return cube;
+}
+</pre>
+<p>Ensuite, nous pouvons créer 8 cubes.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function hsl(h, s, l) {
++  return (new THREE.Color()).setHSL(h, s, l);
++}
+
+-makeInstance(geometry, 0x44aa88,  0);
+-makeInstance(geometry, 0x8844aa, -2);
+-makeInstance(geometry, 0xaa8844,  2);
+
++{
++  const d = 0.8;
++  makeInstance(geometry, hsl(0 / 8, 1, .5), -d, -d, -d);
++  makeInstance(geometry, hsl(1 / 8, 1, .5),  d, -d, -d);
++  makeInstance(geometry, hsl(2 / 8, 1, .5), -d,  d, -d);
++  makeInstance(geometry, hsl(3 / 8, 1, .5),  d,  d, -d);
++  makeInstance(geometry, hsl(4 / 8, 1, .5), -d, -d,  d);
++  makeInstance(geometry, hsl(5 / 8, 1, .5),  d, -d,  d);
++  makeInstance(geometry, hsl(6 / 8, 1, .5), -d,  d,  d);
++  makeInstance(geometry, hsl(7 / 8, 1, .5),  d,  d,  d);
++}
+</pre>
+<p>J'ai aussi ajusté la caméra.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
+const aspect = 2;  // the canvas default
+const near = 0.1;
+-const far = 5;
++const far = 25;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+-camera.position.z = 4;
++camera.position.z = 2;
+</pre>
+<p>Définissez le fond en blanc.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
++scene.background = new THREE.Color('white');
+</pre>
+<p>Et ajouté une deuxième lumière pour que toutes les faces des cubes reçoivent de l'éclairage.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-{
++function addLight(...pos) {
+  const color = 0xFFFFFF;
+  const intensity = 1;
+  const light = new THREE.DirectionalLight(color, intensity);
+-  light.position.set(-1, 2, 4);
++  light.position.set(...pos);
+  scene.add(light);
+}
++addLight(-1, 2, 4);
++addLight( 1, -1, -2);
+</pre>
+<p>Pour rendre les cubes transparents, il suffit de définir le
+drapeau <a href="/docs/#api/en/materials/Material#transparent"><code class="notranslate" translate="no">transparent</code></a> et de définir un
+niveau d'<a href="/docs/#api/en/materials/Material#opacity"><code class="notranslate" translate="no">opacity</code></a>, 1 étant complètement opaque
+et 0 étant complètement transparent.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x, y, z) {
+-  const material = new THREE.MeshPhongMaterial({color});
++  const material = new THREE.MeshPhongMaterial({
++    color,
++    opacity: 0.5,
++    transparent: true,
++  });
+
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  cube.position.set(x, y, z);
+
+  return cube;
+}
+</pre>
+<p>et avec cela, nous obtenons 8 cubes transparents.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Faites glisser sur l'exemple pour faire pivoter la vue. </p>
+<p>Cela semble donc facile mais... regardez de plus près. Les cubes n'ont pas
+de faces arrière.</p>
+<div class="threejs_center"><img src="../resources/images/transparency-cubes-no-backs.png" style="width: 416px;"></div>
+<div class="threejs_center">pas de faces arrière</div>
+
+<p>Nous avons découvert la propriété de matériau <a href="/docs/#api/en/materials/Material#side"><code class="notranslate" translate="no">side</code></a> dans
+<a href="materials.html">l'article sur les matériaux</a>.
+Définissons-la donc sur <code class="notranslate" translate="no">THREE.DoubleSide</code> pour que les deux faces de chaque cube soient dessinées.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial({
+  color,
+  map: loader.load(url),
+  opacity: 0.5,
+  transparent: true,
++  side: THREE.DoubleSide,
+});
+</pre>
+<p>Et nous obtenons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency-doubleside.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency-doubleside.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Faites-le tourner. Cela semble fonctionner car nous pouvons voir les faces arrière,
+sauf qu'en y regardant de plus près, parfois ce n'est pas le cas.</p>
+<div class="threejs_center"><img src="../resources/images/transparency-cubes-some-backs.png" style="width: 368px;"></div>
+<div class="threejs_center">la face arrière gauche de chaque cube est manquante</div>
+
+<p>Cela se produit en raison de la manière dont les objets 3D sont généralement dessinés. Pour chaque géométrie,
+chaque triangle est dessiné un par un. Lorsque chaque pixel du triangle est dessiné,
+2 choses sont enregistrées. Premièrement, la couleur de ce pixel, et deuxièmement, la profondeur de ce pixel.
+Lorsque le triangle suivant est dessiné, pour chaque pixel, si la profondeur est plus importante que la
+profondeur précédemment enregistrée, aucun pixel n'est dessiné.</p>
+<p>Cela fonctionne très bien pour les objets opaques, mais échoue pour les objets transparents.</p>
+<p>La solution consiste à trier les objets transparents et à dessiner ceux situés à l'arrière avant
+de dessiner ceux à l'avant. THREE.js le fait pour les objets comme les <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>,
+sinon le tout premier exemple aurait échoué entre les cubes, certains cubes bloquant les autres.
+Malheureusement, pour les triangles individuels, le tri serait extrêmement lent. </p>
+<p>Le cube a 12 triangles, 2 pour chaque face, et l'ordre dans lequel ils sont dessinés est
+<a href="custom-buffergeometry.html">le même que celui dans lequel ils sont construits dans la géométrie</a>.
+Ainsi, selon la direction dans laquelle nous regardons, les triangles les plus proches de la caméra
+peuvent être dessinés en premier. Dans ce cas, les triangles à l'arrière ne sont pas dessinés.
+C'est pourquoi parfois nous ne voyons pas les faces arrière.</p>
+<p>Pour un objet convexe comme une sphère ou un cube, une sorte de solution consiste à ajouter
+chaque cube à la scène deux fois. Une fois avec un matériau qui dessine
+uniquement les triangles orientés vers l'arrière, et une autre fois avec un matériau qui dessine uniquement
+les triangles orientés vers l'avant.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x, y, z) {
++  [THREE.BackSide, THREE.FrontSide].forEach((side) =&gt; {
+    const material = new THREE.MeshPhongMaterial({
+      color,
+      opacity: 0.5,
+      transparent: true,
++      side,
+    });
+
+    const cube = new THREE.Mesh(geometry, material);
+    scene.add(cube);
+
+    cube.position.set(x, y, z);
++  });
+}
+</pre>
+<p>Et avec cela, cela <em>semble</em> fonctionner.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency-doubleside-hack.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency-doubleside-hack.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela suppose que le tri de three.js est stable. C'est-à-dire que parce que nous
+avons ajouté le mesh <code class="notranslate" translate="no">side: THREE.BackSide</code> en premier et parce qu'il est exactement à la même
+position, il sera dessiné avant le mesh <code class="notranslate" translate="no">side: THREE.FrontSide</code>.</p>
+<p>Créons 2 plans qui se croisent (après avoir supprimé tout le code relatif aux cubes).
+Nous allons <a href="textures.html">ajouter une texture</a> à chaque plan.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeWidth = 1;
+const planeHeight = 1;
+const geometry = new THREE.PlaneGeometry(planeWidth, planeHeight);
+
+const loader = new THREE.TextureLoader();
+
+function makeInstance(geometry, color, rotY, url) {
+  const texture = loader.load(url, render);
+  const material = new THREE.MeshPhongMaterial({
+    color,
+    map: texture,
+    opacity: 0.5,
+    transparent: true,
+    side: THREE.DoubleSide,
+  });
+
+  const mesh = new THREE.Mesh(geometry, material);
+  scene.add(mesh);
+
+  mesh.rotation.y = rotY;
+}
+
+makeInstance(geometry, 'pink',       0,             'resources/images/happyface.png');
+makeInstance(geometry, 'lightblue',  Math.PI * 0.5, 'resources/images/hmmmface.png');
+</pre>
+<p>Cette fois, nous pouvons utiliser <code class="notranslate" translate="no">side: THREE.DoubleSide</code> car nous ne pouvons jamais voir qu'une
+seule face d'un plan à la fois. Notez également que nous passons notre fonction <code class="notranslate" translate="no">render</code> à la fonction de chargement de texture
+afin que lorsque la texture a fini de charger, nous rendions à nouveau la scène.
+C'est parce que cet exemple effectue un <a href="rendering-on-demand.html">rendu à la demande</a>
+au lieu d'un rendu continu.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency-intersecting-planes.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency-intersecting-planes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Et encore une fois, nous voyons un problème similaire.</p>
+<div class="threejs_center"><img src="../resources/images/transparency-planes.png" style="width: 408px;"></div>
+<div class="threejs_center">la moitié d'une face est manquante</div>
+
+<p>La solution ici est de diviser manuellement chaque plan en 2 plans
+afin qu'il n'y ait réellement aucune intersection.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, rotY, url) {
++  const base = new THREE.Object3D();
++  scene.add(base);
++  base.rotation.y = rotY;
+
++  [-1, 1].forEach((x) =&gt; {
+    const texture = loader.load(url, render);
++    texture.offset.x = x &lt; 0 ? 0 : 0.5;
++    texture.repeat.x = .5;
+    const material = new THREE.MeshPhongMaterial({
+      color,
+      map: texture,
+      opacity: 0.5,
+      transparent: true,
+      side: THREE.DoubleSide,
+    });
+
+    const mesh = new THREE.Mesh(geometry, material);
+-    scene.add(mesh);
++    base.add(mesh);
+
+-    mesh.rotation.y = rotY;
++    mesh.position.x = x * .25;
+  });
+}
+</pre>
+<p>La manière d'y parvenir dépend de vous. Si j'utilisais un logiciel de modélisation comme
+<a href="https://blender.org">Blender</a>, je le ferais probablement manuellement en ajustant
+les coordonnées de texture. Ici cependant, nous utilisons <a href="/docs/#api/en/geometries/PlaneGeometry"><code class="notranslate" translate="no">PlaneGeometry</code></a> qui, par défaut,
+étire la texture sur tout le plan. Comme nous l'avons <a href="textures.html">vu précédemment</a>,
+en définissant <a href="/docs/#api/en/textures/Texture#repeat"><code class="notranslate" translate="no">texture.repeat</code></a>
+et <a href="/docs/#api/en/textures/Texture#offset"><code class="notranslate" translate="no">texture.offset</code></a>, nous pouvons mettre à l'échelle et déplacer la texture pour obtenir
+la bonne moitié de la texture de face sur chaque plan.</p>
+<p>Le code ci-dessus crée également un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> et lui attache les 2 plans en tant qu'enfants.
+Il semblait plus facile de faire pivoter un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> parent que de faire les calculs nécessaires
+pour le faire sans. </p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency-intersecting-planes-fixed.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency-intersecting-planes-fixed.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cette solution ne fonctionne vraiment que pour des choses simples comme 2 plans dont
+la position d'intersection ne change pas.</p>
+<p>Pour les objets texturés, une autre solution consiste à définir un test alpha.</p>
+<p>Un test alpha est un niveau d'<em>alpha</em> en dessous duquel three.js ne dessinera pas
+le pixel. Si nous ne dessinons pas du tout un pixel, alors les problèmes de profondeur
+mentionnés ci-dessus disparaissent. Pour les textures aux bords relativement nets,
+cela fonctionne assez bien. Les exemples incluent les textures de feuilles sur une plante ou un arbre,
+ou souvent une parcelle d'herbe.</p>
+<p>Essayons sur les 2 plans. Utilisons d'abord des textures différentes.
+Les textures ci-dessus étaient 100% opaques. Ces 2 utilisent la transparence.</p>
+<div class="spread">
+  <div><img class="checkerboard" src="../examples/resources/images/tree-01.png"></div>
+  <div><img class="checkerboard" src="../examples/resources/images/tree-02.png"></div>
+</div>
+
+<p>Retournons aux 2 plans qui se croisent (avant de les diviser) et utilisons
+ces textures et définissons un <a href="/docs/#api/en/materials/Material#alphaTest"><code class="notranslate" translate="no">alphaTest</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, rotY, url) {
+  const texture = loader.load(url, render);
+  const material = new THREE.MeshPhongMaterial({
+    color,
+    map: texture,
+-    opacity: 0.5,
+    transparent: true,
++    alphaTest: 0.5,
+    side: THREE.DoubleSide,
+  });
+
+  const mesh = new THREE.Mesh(geometry, material);
+  scene.add(mesh);
+
+  mesh.rotation.y = rotY;
+}
+
+-makeInstance(geometry, 'pink',       0,             'resources/images/happyface.png');
+-makeInstance(geometry, 'lightblue',  Math.PI * 0.5, 'resources/images/hmmmface.png');
++makeInstance(geometry, 'white', 0,             'resources/images/tree-01.png');
++makeInstance(geometry, 'white', Math.PI * 0.5, 'resources/images/tree-02.png');
+</pre>
+<p>Avant d'exécuter cela, ajoutons une petite interface utilisateur pour pouvoir jouer plus facilement avec les paramètres <code class="notranslate" translate="no">alphaTest</code>
+et <code class="notranslate" translate="no">transparent</code>. Nous utiliserons lil-gui comme nous l'avons présenté
+dans <a href="scenegraph.html">l'article sur le graphe de scène de three.js</a>.</p>
+<p>Nous allons d'abord créer une aide pour lil-gui qui définit une valeur pour chaque matériau de la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class AllMaterialPropertyGUIHelper {
+  constructor(prop, scene) {
+    this.prop = prop;
+    this.scene = scene;
+  }
+  get value() {
+    const {scene, prop} = this;
+    let v;
+    scene.traverse((obj) =&gt; {
+      if (obj.material &amp;&amp; obj.material[prop] !== undefined) {
+        v = obj.material[prop];
+      }
+    });
+    return v;
+  }
+  set value(v) {
+    const {scene, prop} = this;
+    scene.traverse((obj) =&gt; {
+      if (obj.material &amp;&amp; obj.material[prop] !== undefined) {
+        obj.material[prop] = v;
+        obj.material.needsUpdate = true;
+      }
+    });
+  }
+}
+</pre>
+<p>Ensuite, nous allons ajouter l'interface graphique.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
+gui.add(new AllMaterialPropertyGUIHelper('alphaTest', scene), 'value', 0, 1)
+    .name('alphaTest')
+    .onChange(requestRenderIfNotRequested);
+gui.add(new AllMaterialPropertyGUIHelper('transparent', scene), 'value')
+    .name('transparent')
+    .onChange(requestRenderIfNotRequested);
+</pre>
+<p>et bien sûr, nous devons inclure lil-gui.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
++import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
+</pre>
+<p>et voici les résultats.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/transparency-intersecting-planes-alphatest.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/transparency-intersecting-planes-alphatest.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Vous pouvez voir que cela fonctionne, mais zoomez et vous verrez qu'un plan a des lignes blanches.</p>
+<div class="threejs_center"><img src="../resources/images/transparency-alphatest-issues.png" style="width: 532px;"></div>
+
+<p>C'est le même problème de profondeur qu'auparavant. Ce plan a été dessiné en premier,
+donc le plan situé derrière n'est pas dessiné. Il n'y a pas de solution parfaite.
+Ajustez l'<code class="notranslate" translate="no">alphaTest</code> et/ou désactivez <code class="notranslate" translate="no">transparent</code> pour trouver une solution
+qui correspond à votre cas d'utilisation.</p>
+<p>La conclusion de cet article est que la transparence parfaite est difficile.
+Il y a des problèmes, des compromis et des solutions de contournement.</p>
+<p>Par exemple, disons que vous avez une voiture.
+Les voitures ont généralement des pare-brise sur les 4 côtés. Si vous voulez éviter les problèmes de tri
+ci-dessus, vous devrez faire de chaque fenêtre son propre objet afin que three.js puisse
+trier les fenêtres et les dessiner dans le bon ordre.</p>
+<p>Si vous créez des plantes ou de l'herbe, la solution du test alpha est courante.</p>
+<p>La solution que vous choisissez dépend de vos besoins. </p>
 
         </div>
       </div>

+ 254 - 0
manual/fr/uniform-types.html

@@ -0,0 +1,254 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Types d'uniformes</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Types d'uniformes">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Types d'uniformes</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p>
+            Chaque uniforme doit avoir une propriété `value`. Le type de la valeur doit
+            correspondre au type de la variable uniforme dans le code GLSL tel que
+            spécifié pour les types GLSL primitifs dans le tableau ci-dessous. Les structures et
+            tableaux d'uniformes sont également pris en charge. Les tableaux GLSL de type primitif
+            doivent être spécifiés soit comme un tableau des objets THREE correspondants, soit
+            comme un tableau plat contenant les données de tous les objets. En d'autres termes,
+            les primitives GLSL dans les tableaux ne doivent pas être représentées par des tableaux. Cette règle
+            ne s'applique pas de manière transitive. Un tableau de tableaux `vec2`, chacun d'une longueur
+            de cinq vecteurs, doit être un tableau de tableaux, soit de cinq objets `Vector2`,
+            soit de dix `number`s.
+          </p>
+      
+          <table>
+            <thead>
+              <tr>
+                <th>GLSL type</th>
+                <th>JavaScript type</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr>
+                <td>int</td>
+                <td>Number</td>
+              </tr>
+              <tr>
+                <td>uint</td>
+                <td>Number</td>
+              </tr>
+              <tr>
+                <td>float</td>
+                <td>Number</td>
+              </tr>
+              <tr>
+                <td>bool</td>
+                <td>Boolean</td>
+              </tr>
+              <tr>
+                <td>bool</td>
+                <td>Number</td>
+              </tr>
+              <tr>
+                <td>vec2</td>
+                <td>Vector2</td>
+              </tr>
+              <tr>
+                <td>vec2</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>vec2</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>vec3</td>
+                <td>Vector3</td>
+              </tr>
+              <tr>
+                <td>vec3</td>
+                <td>Color</td>
+              </tr>
+              <tr>
+                <td>vec3</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>vec3</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>vec4</td>
+                <td>Vector4</td>
+              </tr>
+              <tr>
+                <td>vec4</td>
+                <td>Quaternion</td>
+              </tr>
+              <tr>
+                <td>vec4</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>vec4</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat2</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat2</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat3</td>
+                <td>Matrix3</td>
+              </tr>
+              <tr>
+                <td>mat3</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat3</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat4</td>
+                <td>Matrix4</td>
+              </tr>
+              <tr>
+                <td>mat4</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>mat4</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec2, bvec2</td>
+                <td>Float32Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec2, bvec2</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec3, bvec3</td>
+                <td>Int32Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec3, bvec3</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec4, bvec4</td>
+                <td>Int32Array (*)</td>
+              </tr>
+              <tr>
+                <td>ivec4, bvec4</td>
+                <td>Array (*)</td>
+              </tr>
+              <tr>
+                <td>sampler2D</td>
+                <td>Texture</td>
+              </tr>
+              <tr>
+                <td>samplerCube</td>
+                <td>CubeTexture</td>
+              </tr>
+            </tbody>
+          </table>
+      
+          <p>
+            (*) De même pour un tableau (le plus interne) (dimension) du même type GLSL,
+            contenant les composants de tous les vecteurs ou matrices du tableau.
+          </p>
+      
+          <h2>Uniforms structurés</h2>
+      
+          <p>
+            Parfois, vous voulez organiser les uniformes en tant que `structs` dans votre code de shader.
+            Le style suivant doit être utilisé pour que `three.js` puisse traiter
+            les données d'uniformes structurées.
+          </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+uniforms = {
+  data: { 
+    value: {
+      position: new Vector3(), 
+      direction: new Vector3( 0, 0, 1 ) 
+    } 
+  } 
+};
+</pre>
+          Cette définition peut être mappée sur le code GLSL suivant :
+<pre class="prettyprint notranslate lang-js" translate="no">
+struct Data { 
+  vec3 position;
+  vec3 direction;
+};
+uniform Data data;
+</pre>
+      
+          <h2>Uniforms structurés avec tableaux</h2>
+      
+          <p>
+            Il est également possible de gérer des `structs` dans des tableaux. La syntaxe pour ce cas d'utilisation
+            est la suivante :
+          </p>
+<pre class="prettyprint notranslate lang-js" translate="no">
+const entry1 = {
+  position: new Vector3(),
+  direction: new Vector3( 0, 0, 1 )
+};
+const entry2 = {
+  position: new Vector3( 1, 1, 1 ),
+  direction: new Vector3( 0, 1, 0 )
+};
+
+uniforms = {
+  data: {
+    value: [ entry1, entry2 ]
+  }
+};
+</pre>
+          Cette définition peut être mappée sur le code GLSL suivant :
+<pre class="prettyprint notranslate lang-js" translate="no">
+struct Data { 
+  vec3 position; 
+  vec3 direction; 
+};
+uniform Data data[ 2 ];
+</pre>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 193 - 0
manual/fr/useful-links.html

@@ -0,0 +1,193 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Liens utiles</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – Liens utiles">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Liens utiles</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p class="desc">
+            Voici une collection de liens qui pourraient vous être utiles pour apprendre three.js.<br />
+            Si vous trouvez quelque chose que vous aimeriez ajouter ici, ou si vous pensez que l'un des liens ci-dessous n'est plus pertinent ou ne fonctionne plus, n'hésitez pas à cliquer sur le bouton 'edit' en bas à droite et à apporter des modifications !<br /><br />
+      
+            Notez également qu'étant donné que three.js est en développement rapide, de nombreux liens contiendront des informations obsolètes. Si quelque chose ne fonctionne pas comme prévu ou comme l'indique l'un de ces liens, vérifiez la console du navigateur pour les avertissements ou les erreurs. Consultez également les pages de documentation pertinentes.
+          </p>
+      
+          <h2>Forums d'aide</h2>
+          <p>
+            Three.js utilise officiellement le [link:https://discourse.threejs.org/ forum] et [link:http://stackoverflow.com/tags/three.js/info Stack Overflow] pour les demandes d'aide.
+            Si vous avez besoin d'aide pour quelque chose, c'est l'endroit où aller. NE PAS ouvrir de problème sur Github pour les demandes d'aide.
+          </p>
+      
+          <h2>Tutoriels et cours</h2>
+      
+          <h3>Pour commencer avec three.js</h3>
+          <ul>
+            <li>
+              [link:https://threejs.org/manual/#en/fundamentals Leçon d'introduction aux Fondamentaux de Three.js]
+            </li>
+            <li>
+              [link:https://codepen.io/rachsmith/post/beginning-with-3d-webgl-pt-1-the-scene Pour commencer avec la 3D WebGL] par [link:https://codepen.io/rachsmith/ Rachel Smith].
+            </li>
+            <li>
+              [link:https://www.august.com.au/blog/animating-scenes-with-webgl-three-js/ Animer des scènes avec WebGL et three.js]
+            </li>
+          </ul>
+      
+          <h3>Articles et cours plus approfondis / avancés</h3>
+          <ul>
+            <li>
+              [link:https://threejs-journey.com/ Three Journey] Cours par [link:https://bruno-simon.com/ Bruno Simon] - Apprend aux débutants à utiliser Three.js étape par étape
+            </li>
+            <li>
+              [link:https://discoverthreejs.com/ Découvrir three.js]
+            </li>
+            <li>
+              [link:http://blog.cjgammon.com/ Collection de tutoriels] par [link:http://www.cjgammon.com/ CJ Gammon].
+            </li>
+            <li>
+              [link:https://medium.com/soffritti.pierfrancesco/glossy-spheres-in-three-js-bfd2785d4857 Sphères brillantes dans three.js].
+            </li>
+           <li>
+             [link:https://www.udacity.com/course/interactive-3d-graphics--cs291 Graphismes 3D Interactifs] - un cours gratuit sur Udacity qui enseigne les fondamentaux des graphismes 3D et utilise three.js comme outil de codage.
+           </li>
+           <li>
+            [Link:https://aerotwist.com/tutorials/ Aerotwist] tutoriels par [link:https://github.com/paullewis/ Paul Lewis].
+           </li>
+           <li>
+             [link:https://discourse.threejs.org/t/three-js-bookshelf/2468 Étagère à livres Three.js] - Vous cherchez plus de ressources sur three.js ou les graphismes par ordinateur en général ? Consultez la sélection de littérature recommandée par la communauté.
+           </li>
+          </ul>
+      
+          <h2>Nouvelles et mises à jour</h2>
+          <ul>
+            <li>
+              [link:https://twitter.com/hashtag/threejs Three.js sur Twitter]
+            </li>
+            <li>
+              [link:http://www.reddit.com/r/threejs/ Three.js sur reddit]
+            </li>
+            <li>
+              [link:http://www.reddit.com/r/webgl/ WebGL sur reddit]
+            </li>
+          </ul>
+      
+          <h2>Exemples</h2>
+          <ul>
+            <li>
+              [link:https://github.com/edwinwebb/three-seed/ three-seed] - projet de démarrage three.js avec ES6 et Webpack
+            </li>
+            <li>
+              [link:http://stemkoski.github.io/Three.js/index.html Exemples du Professeur Stemkoski] - une collection d'exemples adaptés aux débutants construits à l'aide de three.js r60.
+            </li>
+            <li>
+              [link:https://threejs.org/examples/ Exemples officiels de three.js] - ces exemples sont maintenus dans le cadre du dépôt three.js et utilisent toujours la dernière version de three.js.
+            </li>
+            <li>
+              [link:https://raw.githack.com/mrdoob/three.js/dev/examples/ Exemples officiels de la branche de développement three.js] - Identiques aux exemples ci-dessus, sauf qu'ils utilisent la branche de développement de three.js et sont utilisés pour vérifier que tout fonctionne correctement pendant le développement de three.js.
+           </li>
+          </ul>
+      
+        <h2>Outils</h2>
+        <ul>
+          <li>
+            [link:https://github.com/tbensky/physgl physgl.org] - Interface front-end JavaScript avec des wrappers pour three.js, pour apporter les graphismes WebGL aux étudiants apprenant la physique et les mathématiques.
+          </li>
+          <li>
+            [link:https://whsjs.readme.io/ Whitestorm.js] – Framework three.js modulaire avec plugin physique AmmoNext.
+          </li>
+          <li>
+            [link:http://zz85.github.io/zz85-bookmarklets/threelabs.html Inspecteur Three.js]
+          </li>
+          <li>
+            [link:http://idflood.github.io/ThreeNodes.js/ ThreeNodes.js].
+          </li>
+          <li>
+            [link:https://marketplace.visualstudio.com/items?itemName=slevesque.shader vscode shader] - Colorateur syntaxique pour le langage de shader.
+            <br />
+            [link:https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates vscode comment-tagged-templates] - Coloration syntaxique pour les chaînes de gabarit marquées utilisant des commentaires pour le langage de shader, comme : glsl.js.
+          </li>
+          <li>
+            [link:https://github.com/MozillaReality/WebXR-emulator-extension WebXR-emulator-extension]
+          </li>
+        </ul>
+      
+        <h2>Références WebGL</h2>
+          <ul>
+            <li>
+            [link:https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf webgl-reference-card.pdf] - Référence de tous les mots-clés, terminologie, syntaxe et définitions de WebGL et GLSL.
+            </li>
+          </ul>
+      
+        <h2>Anciens liens</h2>
+        <p>
+          Ces liens sont conservés à des fins historiques - vous pouvez toujours les trouver utiles, mais sachez qu'ils peuvent contenir des informations relatives à de très anciennes versions de three.js.
+        </p>
+      
+        <ul>
+          <li>
+            [link:https://www.youtube.com/watch?v=Dir4KO9RdhM AlterQualia at WebGL Camp 3]
+          </li>
+          <li>
+            [link:http://yomotsu.github.io/threejs-examples/ Yomotsus Examples] - une collection d'exemples utilisant three.js r45.
+          </li>
+          <li>
+            [link:http://fhtr.org/BasicsOfThreeJS/#1 Introduction à Three.js] par [link:http://github.com/kig/ Ilmari Heikkinen] (diaporama).
+          </li>
+          <li>
+            [link:http://www.slideshare.net/yomotsu/webgl-and-threejs WebGL and Three.js] par [link:http://github.com/yomotsu Akihiro Oyamada] (diaporama).
+          </li>
+          <li>
+            [link:https://www.youtube.com/watch?v=VdQnOaolrPA Trigger Rally] par [link:https://github.com/jareiko jareiko] (vidéo).
+          </li>
+          <li>
+            [link:http://blackjk3.github.io/threefab/ ThreeFab] - éditeur de scène, maintenu jusqu'à environ three.js r50.
+          </li>
+          <li>
+            [link:http://bkcore.com/blog/3d/webgl-three-js-workflow-tips.html Max to Three.js workflow tips and tricks] par [link:https://github.com/BKcore BKcore]
+          </li>
+          <li>
+            [link:http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/ Un aperçu rapide de Three.js]
+            par [link:http://github.com/nrocy Paul King]
+          </li>
+          <li>
+            [link:http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html Lueur sélective animée dans Three.js]
+            par [link:https://github.com/BKcore BKcore]
+          </li>
+          <li>
+            [link:http://www.natural-science.or.jp/article/20120220155529.php Building A Physics Simulation Environment] - tutoriel three.js en japonais
+          </li>
+         </ul>
+
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 1079 - 5
manual/fr/voxel-geometry.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>Voxel(Minecraft Like) Geometry</title>
+    <title>Géométrie Voxel (type Minecraft)</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – Voxel(Minecraft Like) Geometry">
+    <meta name="twitter:title" content="Three.js – Géométrie Voxel (type Minecraft)">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,1086 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>Voxel(Minecraft Like) Geometry</h1>
+        <h1>Géométrie Voxel (type Minecraft)</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/voxel-geometry.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>J'ai vu ce sujet revenir plus d'une fois à divers endroits.
+C'est fondamentalement, "Comment faire un affichage de voxels comme Minecraft".</p>
+<p>La plupart des gens essaient d'abord en créant une géométrie de cube, puis
+en faisant un maillage à chaque position de voxel. Juste pour le plaisir, j'ai essayé
+cela. J'ai créé un <code class="notranslate" translate="no">Uint8Array</code> de 16777216 éléments pour représenter
+un cube de voxels de 256x256x256.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellSize = 256;
+const cell = new Uint8Array(cellSize * cellSize * cellSize);
+</pre>
+<p>J'ai ensuite fait une seule couche avec une sorte de collines de
+vagues sinusoïdales comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (let y = 0; y &lt; cellSize; ++y) {
+  for (let z = 0; z &lt; cellSize; ++z) {
+    for (let x = 0; x &lt; cellSize; ++x) {
+      const height = (Math.sin(x / cellSize * Math.PI * 4) + Math.sin(z / cellSize * Math.PI * 6)) * 20 + cellSize / 2;
+      if (height &gt; y &amp;&amp; height &lt; y + 1) {
+        const offset = y * cellSize * cellSize +
+                       z * cellSize +
+                       x;
+        cell[offset] = 1;
+      }
+    }
+  }
+}
+</pre>
+<p>J'ai ensuite parcouru toutes les cellules et si elles n'étaient pas
+à 0, j'ai créé un maillage avec un cube.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.BoxGeometry(1, 1, 1);
+const material = new THREE.MeshPhongMaterial({color: 'green'});
+
+for (let y = 0; y &lt; cellSize; ++y) {
+  for (let z = 0; z &lt; cellSize; ++z) {
+    for (let x = 0; x &lt; cellSize; ++x) {
+      const offset = y * cellSize * cellSize +
+                     z * cellSize +
+                     x;
+      const block = cell[offset];
+      const mesh = new THREE.Mesh(geometry, material);
+      mesh.position.set(x, y, z);
+      scene.add(mesh);
+    }
+  }
+}
+</pre>
+<p>Le reste du code est basé sur l'exemple de
+<a href="rendering-on-demand.html">l'article sur le rendu à la demande</a>.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/voxel-geometry-separate-cubes.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/voxel-geometry-separate-cubes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela prend un certain temps pour démarrer et si vous essayez de bouger la caméra
+c'est probablement trop lent. Comme dans <a href="optimize-lots-of-objects.html">l'article sur l'optimisation de nombreux objets</a>
+le problème est qu'il y a juste beaucoup trop d'objets. 256x256
+fait 65536 boîtes !</p>
+<p>L'utilisation de <a href="rendering-on-demand.html">la technique de fusion de la géométrie</a>
+résoudra le problème pour cet exemple, mais que se passerait-il si, au lieu de faire une simple couche, nous remplissions tout ce qui se trouve sous le sol avec des voxels ?
+En d'autres termes, changez la boucle qui remplit les voxels comme ceci :</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (let y = 0; y &lt; cellSize; ++y) {
+  for (let z = 0; z &lt; cellSize; ++z) {
+    for (let x = 0; x &lt; cellSize; ++x) {
+      const height = (Math.sin(x / cellSize * Math.PI * 4) + Math.sin(z / cellSize * Math.PI * 6)) * 20 + cellSize / 2;
+-      if (height &gt; y &amp;&amp; height &lt; y + 1) {
++      if (height &lt; y + 1) {
+        const offset = y * cellSize * cellSize +
+                       z * cellSize +
+                       x;
+        cell[offset] = 1;
+      }
+    }
+  }
+}
+</pre>
+<p>J'ai essayé une fois juste pour voir les résultats. Ça a mouliné pendant
+environ une minute, puis ça a planté avec un message <em>manque de mémoire</em> 😅</p>
+<p>Il y a plusieurs problèmes, mais le plus important est
+que nous créons toutes ces faces à l'intérieur des cubes que
+nous ne pouvons en fait jamais voir.</p>
+<p>En d'autres termes, disons que nous avons une boîte de voxels
+3x2x2. En fusionnant les cubes, nous obtenons ceci :</p>
+<div class="spread">
+  <div data-diagram="mergedCubes" style="height: 300px;"></div>
+</div>
+
+<p>mais nous voulons vraiment ceci</p>
+<div class="spread">
+  <div data-diagram="culledCubes" style="height: 300px;"></div>
+</div>
+
+<p>Dans la boîte du haut, il y a des faces entre les voxels. Des faces
+qui sont un gâchis car elles ne peuvent pas être vues. Ce n'est pas seulement
+une face entre chaque voxel, il y a 2 faces, une pour
+chaque voxel faisant face à son voisin qui sont un gâchis. Toutes ces faces supplémentaires,
+surtout pour un grand volume de voxels, tueront les performances.</p>
+<p>Il devrait être clair que nous ne pouvons pas simplement fusionner la géométrie.
+Nous devons la construire nous-mêmes, en tenant compte du fait
+que si un voxel a un voisin adjacent, il n'a pas besoin de la
+face qui fait face à ce voisin.</p>
+<p>Le problème suivant est que 256x256x256 est tout simplement trop grand. 16 Mo représentent beaucoup de mémoire et
+si rien d'autre n'y est, une grande partie de l'espace est vide, ce qui représente beaucoup de mémoire gaspillée. C'est aussi un nombre énorme de voxels, 16 millions ! C'est trop à
+considérer d'un coup.</p>
+<p>Une solution consiste à diviser la zone en zones plus petites.
+Toute zone qui ne contient rien n'a pas besoin de stockage. Utilisons
+des zones de 32x32x32 (soit 32k) et ne créons une zone que si elle contient quelque chose.
+Nous appellerons l'une de ces zones plus grandes de 32x32x32 une "cellule".</p>
+<p>Découpons cela en morceaux. Tout d'abord, créons une classe pour gérer les données de voxel.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
+  }
+}
+</pre>
+<p>Créons la fonction qui génère la géométrie pour une cellule.
+Supposons que vous passiez une position de cellule.
+En d'autres termes, si vous voulez la géométrie pour la cellule qui couvre les voxels (0-31x, 0-31y, 0-31z)
+alors vous passerez 0,0,0. Pour la cellule qui couvre les voxels (32-63x, 0-31y, 0-31z), vous passerez
+1,0,0.</p>
+<p>Nous devons pouvoir vérifier les voxels voisins, alors supposons que notre classe
+dispose d'une fonction <code class="notranslate" translate="no">getVoxel</code> qui, étant donné une position de voxel, renvoie la valeur
+du voxel à cet endroit. En d'autres termes, si vous lui passez 35,0,0 et que la cellSize est de 32,
+elle regardera la cellule 1,0,0 et dans cette cellule, elle regardera le voxel 3,0,0.
+En utilisant cette fonction, nous pouvons regarder les voxels voisins d'un voxel, même s'ils
+se trouvent dans des cellules voisines.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
+  }
++  generateGeometryDataForCell(cellX, cellY, cellZ) {
++    const {cellSize} = this;
++    const startX = cellX * cellSize;
++    const startY = cellY * cellSize;
++    const startZ = cellZ * cellSize;
++
++    for (let y = 0; y &lt; cellSize; ++y) {
++      const voxelY = startY + y;
++      for (let z = 0; z &lt; cellSize; ++z) {
++        const voxelZ = startZ + z;
++        for (let x = 0; x &lt; cellSize; ++x) {
++          const voxelX = startX + x;
++          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);
++          if (voxel) {
++            for (const {dir} of VoxelWorld.faces) {
++              const neighbor = this.getVoxel(
++                  voxelX + dir[0],
++                  voxelY + dir[1],
++                  voxelZ + dir[2]);
++              if (!neighbor) {
++                // ce voxel n'a pas de voisin dans cette direction, nous avons donc besoin d'une face ici.
++                // here.
++              }
++            }
++          }
++        }
++      }
++    }
++  }
+}
+
++VoxelWorld.faces = [
++  { // gauche
++    dir: [ -1,  0,  0, ],
++  },
++  { // droite
++    dir: [  1,  0,  0, ],
++  },
++  { // bas
++    dir: [  0, -1,  0, ],
++  },
++  { // haut
++    dir: [  0,  1,  0, ],
++  },
++  { // arrière
++    dir: [  0,  0, -1, ],
++  },
++  { // avant
++    dir: [  0,  0,  1, ],
++  },
++];
+</pre>
+<p>Donc, en utilisant le code ci-dessus, nous savons quand nous avons besoin d'une face. Générons les faces.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
+  }
+  generateGeometryDataForCell(cellX, cellY, cellZ) {
+    const {cellSize} = this;
++    const positions = [];
++    const normals = [];
++    const indices = [];
+    const startX = cellX * cellSize;
+    const startY = cellY * cellSize;
+    const startZ = cellZ * cellSize;
+
+    for (let y = 0; y &lt; cellSize; ++y) {
+      const voxelY = startY + y;
+      for (let z = 0; z &lt; cellSize; ++z) {
+        const voxelZ = startZ + z;
+        for (let x = 0; x &lt; cellSize; ++x) {
+          const voxelX = startX + x;
+          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);
+          if (voxel) {
+-            for (const {dir} of VoxelWorld.faces) {
++            for (const {dir, corners} of VoxelWorld.faces) {
+              const neighbor = this.getVoxel(
+                  voxelX + dir[0],
+                  voxelY + dir[1],
+                  voxelZ + dir[2]);
+              if (!neighbor) {
+                // ce voxel n'a pas de voisin dans cette direction, nous avons donc besoin d'une face.
++                const ndx = positions.length / 3;
++                for (const pos of corners) {
++                  positions.push(pos[0] + x, pos[1] + y, pos[2] + z);
++                  normals.push(...dir);
++                }
++                indices.push(
++                  ndx, ndx + 1, ndx + 2,
++                  ndx + 2, ndx + 1, ndx + 3,
++                );
+              }
+            }
+          }
+        }
+      }
+    }
++    return {
++      positions,
++      normals,
++      indices,
+    };
+  }
+}
+
+VoxelWorld.faces = [
+  { // gauche
+    dir: [ -1,  0,  0, ],
++    corners: [
++      [ 0, 1, 0 ],
++      [ 0, 0, 0 ],
++      [ 0, 1, 1 ],
++      [ 0, 0, 1 ],
++    ],
+  },
+  { // droite
+    dir: [  1,  0,  0, ],
++    corners: [
++      [ 1, 1, 1 ],
++      [ 1, 0, 1 ],
++      [ 1, 1, 0 ],
++      [ 1, 0, 0 ],
++    ],
+  },
+  { // bas
+    dir: [  0, -1,  0, ],
++    corners: [
++      [ 1, 0, 1 ],
++      [ 0, 0, 1 ],
++      [ 1, 0, 0 ],
++      [ 0, 0, 0 ],
++    ],
+  },
+  { // haut
+    dir: [  0,  1,  0, ],
++    corners: [
++      [ 0, 1, 1 ],
++      [ 1, 1, 1 ],
++      [ 0, 1, 0 ],
++      [ 1, 1, 0 ],
++    ],
+  },
+  { // arrière
+    dir: [  0,  0, -1, ],
++    corners: [
++      [ 1, 0, 0 ],
++      [ 0, 0, 0 ],
++      [ 1, 1, 0 ],
++      [ 0, 1, 0 ],
++    ],
+  },
+  { // avant
+    dir: [  0,  0,  1, ],
++    corners: [
++      [ 0, 0, 1 ],
++      [ 1, 0, 1 ],
++      [ 0, 1, 1 ],
++      [ 1, 1, 1 ],
++    ],
+  },
+];
+</pre>
+<p>Le code ci-dessus générerait des données de géométrie de base pour nous. Il suffit de fournir
+la fonction <code class="notranslate" translate="no">getVoxel</code>. Commençons par une seule cellule codée en dur.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
++    this.cell = new Uint8Array(cellSize * cellSize * cellSize);
+  }
++  getCellForVoxel(x, y, z) {
++    const {cellSize} = this;
++    const cellX = Math.floor(x / cellSize);
++    const cellY = Math.floor(y / cellSize);
++    const cellZ = Math.floor(z / cellSize);
++    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {
++      return null
++    }
++    return this.cell;
++  }
++  getVoxel(x, y, z) {
++    const cell = this.getCellForVoxel(x, y, z);
++    if (!cell) {
++      return 0;
++    }
++    const {cellSize} = this;
++    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
++    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
++    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
++    const voxelOffset = voxelY * cellSize * cellSize +
++                        voxelZ * cellSize +
++                        voxelX;
++    return cell[voxelOffset];
++  }
+  generateGeometryDataForCell(cellX, cellY, cellZ) {
+
+  ...
+}
+</pre>
+<p>Cela semble fonctionner. Créons une fonction <code class="notranslate" translate="no">setVoxel</code>
+pour pouvoir définir des données.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
+    this.cell = new Uint8Array(cellSize * cellSize * cellSize);
+  }
+  getCellForVoxel(x, y, z) {
+    const {cellSize} = this;
+    const cellX = Math.floor(x / cellSize);
+    const cellY = Math.floor(y / cellSize);
+    const cellZ = Math.floor(z / cellSize);
+    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {
+      return null
+    }
+    return this.cell;
+  }
++  setVoxel(x, y, z, v) {
++    let cell = this.getCellForVoxel(x, y, z);
++    if (!cell) {
++      return;  // TODO : ajouter une nouvelle cellule ?
++    }
++    const {cellSize} = this;
++    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
++    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
++    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
++    const voxelOffset = voxelY * cellSize * cellSize +
++                        voxelZ * cellSize +
++                        voxelX;
++    cell[voxelOffset] = v;
++  }
+  getVoxel(x, y, z) {
+    const cell = this.getCellForVoxel(x, y, z);
+    if (!cell) {
+      return 0;
+    }
+    const {cellSize} = this;
+    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
+    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
+    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
+    const voxelOffset = voxelY * cellSize * cellSize +
+                        voxelZ * cellSize +
+                        voxelX;
+    return cell[voxelOffset];
+  }
+  generateGeometryDataForCell(cellX, cellY, cellZ) {
+
+  ...
+}
+</pre>
+<p>Hmmm, je vois beaucoup de code répété. Arrangeons ça</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(cellSize) {
+    this.cellSize = cellSize;
++    this.cellSliceSize = cellSize * cellSize;
+    this.cell = new Uint8Array(cellSize * cellSize * cellSize);
+  }
+  getCellForVoxel(x, y, z) {
+    const {cellSize} = this;
+    const cellX = Math.floor(x / cellSize);
+    const cellY = Math.floor(y / cellSize);
+    const cellZ = Math.floor(z / cellSize);
+    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {
+      return null;
+    }
+    return this.cell;
+  }
++  computeVoxelOffset(x, y, z) {
++    const {cellSize, cellSliceSize} = this;
++    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
++    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
++    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
++    return voxelY * cellSliceSize +
++           voxelZ * cellSize +
++           voxelX;
++  }
+  setVoxel(x, y, z, v) {
+    const cell = this.getCellForVoxel(x, y, z);
+    if (!cell) {
+      return;  // TODO : ajouter une nouvelle cellule ?
+    }
+-    const {cellSize} = this;
+-    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
+-    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
+-    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
+-    const voxelOffset = voxelY * cellSize * cellSize +
+-                        voxelZ * cellSize +
+-                        voxelX;
++    const voxelOffset = this.computeVoxelOffset(x, y, z);
+    cell[voxelOffset] = v;
+  }
+  getVoxel(x, y, z) {
+    const cell = this.getCellForVoxel(x, y, z);
+    if (!cell) {
+      return 0;
+    }
+-    const {cellSize} = this;
+-    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
+-    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;
+-    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;
+-    const voxelOffset = voxelY * cellSize * cellSize +
+-                        voxelZ * cellSize +
+-                        voxelX;
++    const voxelOffset = this.computeVoxelOffset(x, y, z);
+    return cell[voxelOffset];
+  }
+  generateGeometryDataForCell(cellX, cellY, cellZ) {
+
+  ...
+}
+</pre>
+<p>Maintenant, créons du code pour remplir la première cellule avec des voxels.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellSize = 32;
+
+const world = new VoxelWorld(cellSize);
+
+for (let y = 0; y &lt; cellSize; ++y) {
+  for (let z = 0; z &lt; cellSize; ++z) {
+    for (let x = 0; x &lt; cellSize; ++x) {
+      const height = (Math.sin(x / cellSize * Math.PI * 2) + Math.sin(z / cellSize * Math.PI * 3)) * (cellSize / 6) + (cellSize / 2);
+      if (y &lt; height) {
+        world.setVoxel(x, y, z, 1);
+      }
+    }
+  }
+}
+</pre>
+<p>et du code pour effectivement générer la géométrie comme nous l'avons vu dans
+<a href="custom-buffergeometry.html">l'article sur BufferGeometry personnalisé</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const {positions, normals, indices} = world.generateGeometryDataForCell(0, 0, 0);
+const geometry = new THREE.BufferGeometry();
+const material = new THREE.MeshLambertMaterial({color: 'green'});
+
+const positionNumComponents = 3;
+const normalNumComponents = 3;
+geometry.setAttribute(
+    'position',
+    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+geometry.setAttribute(
+    'normal',
+    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+geometry.setIndex(indices);
+const mesh = new THREE.Mesh(geometry, material);
+scene.add(mesh);
+</pre>
+<p>essayons</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/voxel-geometry-culled-faces.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/voxel-geometry-culled-faces.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Cela semble fonctionner ! D'accord, ajoutons des textures.</p>
+<p>En cherchant sur le net, j'ai trouvé <a href="https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/resource-packs/1245961-16x-1-7-4-wip-flourish">cet ensemble</a>
+de textures minecraft sous licence <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC-BY-NC-SA</a> par <a href="https://www.minecraftforum.net/members/Joshtimus">Joshtimus</a>.
+J'en ai choisi quelques-unes au hasard et j'ai construit cette <a href="https://www.google.com/search?q=texture+atlas">texture atlas</a>.</p>
+<div class="threejs_center"><img class="checkerboard" src="../examples/resources/images/minecraft/flourish-cc-by-nc-sa.png" style="width: 512px; image-rendering: pixelated;"></div>
+
+<p>Pour simplifier les choses, elles sont arrangées un type de voxel par colonne,
+où la rangée supérieure est le côté d'un voxel. La 2ème rangée est
+le dessus du voxel, et la 3ème rangée est le dessous du voxel.</p>
+<p>Sachant cela, nous pouvons ajouter des informations à nos données <code class="notranslate" translate="no">VoxelWorld.faces</code>
+pour spécifier pour chaque face quelle rangée utiliser et les UVs à utiliser
+pour cette face.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">VoxelWorld.faces = [
+  { // gauche
++    uvRow: 0,
+    dir: [ -1,  0,  0, ],
+    corners: [
+-      [ 0, 1, 0 ],
+-      [ 0, 0, 0 ],
+-      [ 0, 1, 1 ],
+-      [ 0, 0, 1 ],
++      { pos: [ 0, 1, 0 ], uv: [ 0, 1 ], },
++      { pos: [ 0, 0, 0 ], uv: [ 0, 0 ], },
++      { pos: [ 0, 1, 1 ], uv: [ 1, 1 ], },
++      { pos: [ 0, 0, 1 ], uv: [ 1, 0 ], },
+    ],
+  },
+  { // droite
++    uvRow: 0,
+    dir: [  1,  0,  0, ],
+    corners: [
+-      [ 1, 1, 1 ],
+-      [ 1, 0, 1 ],
+-      [ 1, 1, 0 ],
+-      [ 1, 0, 0 ],
++      { pos: [ 1, 1, 1 ], uv: [ 0, 1 ], },
++      { pos: [ 1, 0, 1 ], uv: [ 0, 0 ], },
++      { pos: [ 1, 1, 0 ], uv: [ 1, 1 ], },
++      { pos: [ 1, 0, 0 ], uv: [ 1, 0 ], },
++    ],
+  },
+  { // bas
++    uvRow: 1,
+    dir: [  0, -1,  0, ],
+    corners: [
+-      [ 1, 0, 1 ],
+-      [ 0, 0, 1 ],
+-      [ 1, 0, 0 ],
+-      [ 0, 0, 0 ],
++      { pos: [ 1, 0, 1 ], uv: [ 1, 0 ], },
++      { pos: [ 0, 0, 1 ], uv: [ 0, 0 ], },
++      { pos: [ 1, 0, 0 ], uv: [ 1, 1 ], },
++      { pos: [ 0, 0, 0 ], uv: [ 0, 1 ], },
++    ],
+  },
+  { // haut
++    uvRow: 2,
+    dir: [  0,  1,  0, ],
+    corners: [
+-      [ 0, 1, 1 ],
+-      [ 1, 1, 1 ],
+-      [ 0, 1, 0 ],
+-      [ 1, 1, 0 ],
++      { pos: [ 0, 1, 1 ], uv: [ 1, 1 ], },
++      { pos: [ 1, 1, 1 ], uv: [ 0, 1 ], },
++      { pos: [ 0, 1, 0 ], uv: [ 1, 0 ], },
++      { pos: [ 1, 1, 0 ], uv: [ 0, 0 ], },
++    ],
+  },
+  { // arrière
++    uvRow: 0,
+    dir: [  0,  0, -1, ],
+    corners: [
+-      [ 1, 0, 0 ],
+-      [ 0, 0, 0 ],
+-      [ 1, 1, 0 ],
+-      [ 0, 1, 0 ],
++      { pos: [ 1, 0, 0 ], uv: [ 0, 0 ], },
++      { pos: [ 0, 0, 0 ], uv: [ 1, 0 ], },
++      { pos: [ 1, 1, 0 ], uv: [ 0, 1 ], },
++      { pos: [ 0, 1, 0 ], uv: [ 1, 1 ], },
++    ],
+  },
+  { // avant
++    uvRow: 0,
+    dir: [  0,  0,  1, ],
+    corners: [
+-      [ 0, 0, 1 ],
+-      [ 1, 0, 1 ],
+-      [ 0, 1, 1 ],
+-      [ 1, 1, 1 ],
++      { pos: [ 0, 0, 1 ], uv: [ 0, 0 ], },
++      { pos: [ 1, 0, 1 ], uv: [ 1, 0 ], },
++      { pos: [ 0, 1, 1 ], uv: [ 0, 1 ], },
++      { pos: [ 1, 1, 1 ], uv: [ 1, 1 ], },
++    ],
+  },
+];
+</pre>
+<p>Et nous pouvons mettre à jour le code pour utiliser ces données. Nous devons
+connaître la taille d'une tuile dans la texture atlas et les dimensions
+de la texture.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+-  constructor(cellSize) {
+-    this.cellSize = cellSize;
++  constructor(options) {
++    this.cellSize = options.cellSize;
++    this.tileSize = options.tileSize;
++    this.tileTextureWidth = options.tileTextureWidth;
++    this.tileTextureHeight = options.tileTextureHeight;
++    const {cellSize} = this;
++    this.cellSliceSize = cellSize * cellSize;
++    this.cell = new Uint8Array(cellSize * cellSize * cellSize);
++  }
+
+  ...
+
+  generateGeometryDataForCell(cellX, cellY, cellZ) {
+-    const {cellSize} = this;
++    const {cellSize, tileSize, tileTextureWidth, tileTextureHeight} = this;
+    const positions = [];
+    const normals = [];
++    const uvs = [];
+    const indices = [];
+    const startX = cellX * cellSize;
+    const startY = cellY * cellSize;
+    const startZ = cellZ * cellSize;
+
+    for (let y = 0; y &lt; cellSize; ++y) {
+      const voxelY = startY + y;
+      for (let z = 0; z &lt; cellSize; ++z) {
+        const voxelZ = startZ + z;
+        for (let x = 0; x &lt; cellSize; ++x) {
+          const voxelX = startX + x;
+          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);
+          if (voxel) {
+            const uvVoxel = voxel - 1;  // le voxel 0 est le ciel, donc pour les UVs nous commençons à 0
+            // There is a voxel here but do we need faces for it?
+-            for (const {dir, corners} of VoxelWorld.faces) {
++            for (const {dir, corners, uvRow} of VoxelWorld.faces) {
+              const neighbor = this.getVoxel(
+                  voxelX + dir[0],
+                  voxelY + dir[1],
+                  voxelZ + dir[2]);
+              if (!neighbor) {
+                // ce voxel n'a pas de voisin dans cette direction, nous avons donc besoin d'une face.
+                const ndx = positions.length / 3;
+-                for (const pos of corners) {
++                for (const {pos, uv} of corners) {
+                  positions.push(pos[0] + x, pos[1] + y, pos[2] + z);
+                  normals.push(...dir);
++                  uvs.push(
++                        (uvVoxel +   uv[0]) * tileSize / tileTextureWidth,
++                    1 - (uvRow + 1 - uv[1]) * tileSize / tileTextureHeight);
+                }
+                indices.push(
+                  ndx, ndx + 1, ndx + 2,
+                  ndx + 2, ndx + 1, ndx + 3,
+                );
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return {
+      positions,
+      normals,
+      uvs,
+      indices,
+    };
+  }
+}
+</pre>
+<p>Nous devons ensuite <a href="textures.html">charger la texture</a></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
+const texture = loader.load('resources/images/minecraft/flourish-cc-by-nc-sa.png', render);
+texture.magFilter = THREE.NearestFilter;
+texture.minFilter = THREE.NearestFilter;
+texture.colorSpace = THREE.SRGBColorSpace;
+</pre>
+<p>et passer les paramètres à la classe <code class="notranslate" translate="no">VoxelWorld</code></p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const tileSize = 16;
++const tileTextureWidth = 256;
++const tileTextureHeight = 64;
+-const world = new VoxelWorld(cellSize);
++const world = new VoxelWorld({
++  cellSize,
++  tileSize,
++  tileTextureWidth,
++  tileTextureHeight,
++});
+</pre>
+<p>Utilisons réellement les UVs lors de la création de la géométrie
+et la texture lorsque nous fabriquons le matériau</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const {positions, normals, indices} = world.generateGeometryDataForCell(0, 0, 0);
++const {positions, normals, uvs, indices} = world.generateGeometryDataForCell(0, 0, 0);
+const geometry = new THREE.BufferGeometry();
+-const material = new THREE.MeshLambertMaterial({color: 'green'});
++const material = new THREE.MeshLambertMaterial({
++  map: texture,
++  side: THREE.DoubleSide,
++  alphaTest: 0.1,
++  transparent: true,
++});
+
+const positionNumComponents = 3;
+const normalNumComponents = 3;
++const uvNumComponents = 2;
+geometry.setAttribute(
+    'position',
+    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+geometry.setAttribute(
+    'normal',
+    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
++geometry.setAttribute(
++    'uv',
++    new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+geometry.setIndex(indices);
+const mesh = new THREE.Mesh(geometry, material);
+scene.add(mesh);
+</pre>
+<p>Une dernière chose, nous devons réellement définir certains voxels
+pour utiliser différentes textures.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (let y = 0; y &lt; cellSize; ++y) {
+  for (let z = 0; z &lt; cellSize; ++z) {
+    for (let x = 0; x &lt; cellSize; ++x) {
+      const height = (Math.sin(x / cellSize * Math.PI * 2) + Math.sin(z / cellSize * Math.PI * 3)) * (cellSize / 6) + (cellSize / 2);
+      if (y &lt; height) {
+-        world.setVoxel(x, y, z, 1);
++        world.setVoxel(x, y, z, randInt(1, 17));
++      }
++    }
++  }
++}
++
++function randInt(min, max) {
++  return Math.floor(Math.random() * (max - min) + min);
++}
+</pre>
+<p>et avec cela, nous obtenons des textures !</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/voxel-geometry-culled-faces-with-textures.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/voxel-geometry-culled-faces-with-textures.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Supportons maintenant plus d'une cellule.</p>
+<p>Pour ce faire, stockons les cellules dans un objet en utilisant des cell ids.
+Un cell id sera simplement les coordonnées d'une cellule séparées par
+une virgule. En d'autres termes, si nous demandons le voxel 35,0,0,
+qui est dans la cellule 1,0,0, son id est donc <code class="notranslate" translate="no">"1,0,0"</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class VoxelWorld {
+  constructor(options) {
+    this.cellSize = options.cellSize;
+    this.tileSize = options.tileSize;
+    this.tileTextureWidth = options.tileTextureWidth;
+    this.tileTextureHeight = options.tileTextureHeight;
+    const {cellSize} = this;
+    this.cellSliceSize = cellSize * cellSize;
+-    this.cell = new Uint8Array(cellSize * cellSize * cellSize);
++    this.cells = {};
+  }
++  computeCellId(x, y, z) {
++    const {cellSize} = this;
++    const cellX = Math.floor(x / cellSize);
++    const cellY = Math.floor(y / cellSize);
++    const cellZ = Math.floor(z / cellSize);
++    return `${cellX},${cellY},${cellZ}`;
++  }
++  getCellForVoxel(x, y, z) {
+-    const cellX = Math.floor(x / cellSize);
+-    const cellY = Math.floor(y / cellSize);
+-    const cellZ = Math.floor(z / cellSize);
+-    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {
+-      return null;
+-    }
+-    return this.cell;
++    return this.cells[this.computeCellId(x, y, z)];
+  }
+
+   ...
+}
+</pre>
+<p>et maintenant nous pouvons faire en sorte que <code class="notranslate" translate="no">setVoxel</code> ajoute de nouvelles cellules si
+nous essayons de définir un voxel dans une cellule qui n'existe pas encore</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">  setVoxel(x, y, z, v) {
+-    const cell = this.getCellForVoxel(x, y, z);
++    let cell = this.getCellForVoxel(x, y, z);
+    if (!cell) {
+-      return 0;
++      cell = this.addCellForVoxel(x, y, z);
+    }
+    const voxelOffset = this.computeVoxelOffset(x, y, z);
+    cell[voxelOffset] = v;
+  }
++  addCellForVoxel(x, y, z) {
++    const cellId = this.computeCellId(x, y, z);
++    let cell = this.cells[cellId];
++    if (!cell) {
++      const {cellSize} = this;
++      cell = new Uint8Array(cellSize * cellSize * cellSize);
++      this.cells[cellId] = cell;
++    }
++    return cell;
++  }
+</pre>
+<p>Rendons cela modifiable.</p>
+<p>Tout d'abord, nous ajouterons une UI. En utilisant des boutons radio, nous pouvons créer un tableau de tuiles 8x2</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div id="ui"&gt;
++    &lt;div class="tiles"&gt;
++      &lt;input type="radio" name="voxel" id="voxel1" value="1"&gt;&lt;label for="voxel1" style="background-position:   -0% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel2" value="2"&gt;&lt;label for="voxel2" style="background-position: -100% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel3" value="3"&gt;&lt;label for="voxel3" style="background-position: -200% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel4" value="4"&gt;&lt;label for="voxel4" style="background-position: -300% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel5" value="5"&gt;&lt;label for="voxel5" style="background-position: -400% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel6" value="6"&gt;&lt;label for="voxel6" style="background-position: -500% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel7" value="7"&gt;&lt;label for="voxel7" style="background-position: -600% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel8" value="8"&gt;&lt;label for="voxel8" style="background-position: -700% -0%"&gt;&lt;/label&gt;
++    &lt;/div&gt;
++    &lt;div class="tiles"&gt;
++      &lt;input type="radio" name="voxel" id="voxel9"  value="9" &gt;&lt;label for="voxel9"  style="background-position:  -800% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel10" value="10"&gt;&lt;label for="voxel10" style="background-position:  -900% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel11" value="11"&gt;&lt;label for="voxel11" style="background-position: -1000% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel12" value="12"&gt;&lt;label for="voxel12" style="background-position: -1100% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel13" value="13"&gt;&lt;label for="voxel13" style="background-position: -1200% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel14" value="14"&gt;&lt;label for="voxel14" style="background-position: -1300% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel15" value="15"&gt;&lt;label for="voxel15" style="background-position: -1400% -0%"&gt;&lt;/label&gt;
++      &lt;input type="radio" name="voxel" id="voxel16" value="16"&gt;&lt;label for="voxel16" style="background-position: -1500% -0%"&gt;&lt;/label&gt;
++    &lt;/div&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>Et ajouter du CSS pour le styliser, afficher les tuiles et mettre en évidence
+la sélection actuelle</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">body {
+    margin: 0;
+}
+#c {
+    width: 100%;
+    height: 100%;
+    display: block;
+}
++#ui {
++    position: absolute;
++    left: 10px;
++    top: 10px;
++    background: rgba(0, 0, 0, 0.8);
++    padding: 5px;
++}
++#ui input[type=radio] {
++  width: 0;
++  height: 0;
++  display: none;
++}
++#ui input[type=radio] + label {
++  background-image: url('resources/images/minecraft/flourish-cc-by-nc-sa.png');
++  background-size: 1600% 400%;
++  image-rendering: pixelated;
++  width: 64px;
++  height: 64px;
++  display: inline-block;
++}
++#ui input[type=radio]:checked + label {
++  outline: 3px solid red;
++}
++@media (max-width: 600px), (max-height: 600px) {
++  #ui input[type=radio] + label {
++    width: 32px;
++    height: 32px;
++  }
++}
+</pre>
+<p>L'expérience utilisateur (UX) sera la suivante. Si aucune tuile n'est sélectionnée et que vous cliquez sur un voxel, ce voxel sera effacé, ou si vous cliquez sur un voxel et que vous maintenez la touche Maj enfoncée, il sera effacé. Sinon, si une tuile est sélectionnée, elle sera ajoutée. Vous pouvez désélectionner le type de tuile sélectionné en cliquant à nouveau dessus.</p>
+<p>Ce code permettra à l'utilisateur de désélectionner le
+bouton radio surligné.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">let currentVoxel = 0;
+let currentId;
+
+document.querySelectorAll('#ui .tiles input[type=radio][name=voxel]').forEach((elem) =&gt; {
+  elem.addEventListener('click', allowUncheck);
+});
+
+function allowUncheck() {
+  if (this.id === currentId) {
+    this.checked = false;
+    currentId = undefined;
+    currentVoxel = 0;
+  } else {
+    currentId = this.id;
+    currentVoxel = parseInt(this.value);
+  }
+}
+</pre>
+<p>Et le code ci-dessous nous permettra de définir un voxel en fonction de l'endroit
+où l'utilisateur clique. Il utilise un code similaire à celui que nous avons
+fait dans <a href="picking.html">l'article sur la sélection</a>
+mais il n'utilise pas le <code class="notranslate" translate="no">RayCaster</code> intégré. Au lieu de cela,
+il utilise <code class="notranslate" translate="no">VoxelWorld.intersectRay</code> qui renvoie
+la position d'intersection et la normale de la face
+touchée.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: (event.clientX - rect.left) * canvas.width  / rect.width,
+    y: (event.clientY - rect.top ) * canvas.height / rect.height,
+  };
+}
+
+function placeVoxel(event) {
+  const pos = getCanvasRelativePosition(event);
+  const x = (pos.x / canvas.width ) *  2 - 1;
+  const y = (pos.y / canvas.height) * -2 + 1;  // notez que nous inversons Y
+
+  const start = new THREE.Vector3();
+  const end = new THREE.Vector3();
+  start.setFromMatrixPosition(camera.matrixWorld);
+  end.set(x, y, 1).unproject(camera);
+
+  const intersection = world.intersectRay(start, end);
+  if (intersection) {
+    const voxelId = event.shiftKey ? 0 : currentVoxel;
+    // le point d'intersection est sur la face. Cela signifie
+    // que l'imprécision mathématique pourrait nous placer de chaque côté de la face.
+    // alors allons à la moitié de la normale DANS le voxel si nous supprimons (currentVoxel = 0)
+    // ou HORS du voxel si nous ajoutons (currentVoxel > 0)
+    const pos = intersection.position.map((v, ndx) =&gt; {
+      return v + intersection.normal[ndx] * (voxelId &gt; 0 ? 0.5 : -0.5);
+    });
+    world.setVoxel(...pos, voxelId);
+    updateVoxelGeometry(...pos);
+    requestRenderIfNotRequested();
+  }
+}
+
+const mouse = {
+  x: 0,
+  y: 0,
+};
+
+function recordStartPosition(event) {
+  mouse.x = event.clientX;
+  mouse.y = event.clientY;
+  mouse.moveX = 0;
+  mouse.moveY = 0;
+}
+function recordMovement(event) {
+  mouse.moveX += Math.abs(mouse.x - event.clientX);
+  mouse.moveY += Math.abs(mouse.y - event.clientY);
+}
+function placeVoxelIfNoMovement(event) {
+  if (mouse.moveX &lt; 5 &amp;&amp; mouse.moveY &lt; 5) {
+    placeVoxel(event);
+  }
+  window.removeEventListener('pointermove', recordMovement);
+  window.removeEventListener('pointerup', placeVoxelIfNoMovement);
+}
+canvas.addEventListener('pointerdown', (event) =&gt; {
+  event.preventDefault();
+  recordStartPosition(event);
+  window.addEventListener('pointermove', recordMovement);
+  window.addEventListener('pointerup', placeVoxelIfNoMovement);
+}, {passive: false});
+canvas.addEventListener('touchstart', (event) =&gt; {
+  // arrêter le défilement
+  event.preventDefault();
+}, {passive: false});
+</pre>
+<p>Il se passe beaucoup de choses dans le code ci-dessus. En gros, la souris a une double fonction. L'une est de déplacer la caméra. L'autre est d'éditer le monde. Placer/Effacer un voxel se produit lorsque vous relâchez la souris, mais uniquement si vous n'avez pas bougé la souris depuis que vous avez appuyé pour la première fois. C'est juste une supposition que si vous avez bougé la souris, vous essayiez de déplacer la caméra, pas de placer un bloc. <code class="notranslate" translate="no">moveX</code> et <code class="notranslate" translate="no">moveY</code> sont en mouvement absolu, donc si vous vous déplacez de 10 vers la gauche puis de 10 vers la droite, vous aurez parcouru 20 unités. Dans ce cas, l'utilisateur était probablement juste en train de faire pivoter le modèle d'avant en arrière et ne voulait pas placer de bloc. Je n'ai pas fait de tests pour voir si <code class="notranslate" translate="no">5</code> est une bonne valeur ou non.</p>
+<p>Dans le code, nous appelons <code class="notranslate" translate="no">world.setVoxel</code> pour définir un voxel et
+ensuite <code class="notranslate" translate="no">updateVoxelGeometry</code> pour mettre à jour la géométrie three.js
+en fonction de ce qui a changé.</p>
+<p>Faisons cela maintenant. Si l'utilisateur clique sur un
+voxel au bord d'une cellule, la géométrie du voxel
+dans la cellule adjacente pourrait avoir besoin d'une nouvelle géométrie. Cela signifie
+que nous devons vérifier la cellule du voxel que nous venons d'éditer
+ainsi que dans les 6 directions à partir de cette cellule.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const neighborOffsets = [
+  [ 0,  0,  0], // soi-même
+  [-1,  0,  0], // gauche
+  [ 1,  0,  0], // droite
+  [ 0, -1,  0], // bas
+  [ 0,  1,  0], // haut
+  [ 0,  0, -1], // arrière
+  [ 0,  0,  1], // avant
+];
+function updateVoxelGeometry(x, y, z) {
+  const updatedCellIds = {};
+  for (const offset of neighborOffsets) {
+    const ox = x + offset[0];
+    const oy = y + offset[1];
+    const oz = z + offset[2];
+    const cellId = world.computeCellId(ox, oy, oz);
+    if (!updatedCellIds[cellId]) {
+      updatedCellIds[cellId] = true;
+      updateCellGeometry(ox, oy, oz);
+    }
+  }
+}
+</pre>
+<p>J'ai pensé à vérifier les cellules adjacentes comme </p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;
+if (voxelX === 0) {
+  // mettre à jour la cellule à gauche
+} else if (voxelX === cellSize - 1) {
+  // mettre à jour la cellule à droite
+}
+</pre>
+<p>et il y aurait 4 vérifications supplémentaires pour les 4 autres directions,
+mais il m'est apparu que le code serait beaucoup plus simple avec
+juste un tableau d'offsets et en sauvegardant les cell ids des
+cellules que nous avons déjà mises à jour. Si le voxel mis à jour n'est pas
+au bord d'une cellule, le test rejettera rapidement la mise à jour
+de la même cellule.</p>
+<p>Pour <code class="notranslate" translate="no">updateCellGeometry</code>, nous allons simplement prendre le code que nous
+avions auparavant et qui générait la géométrie pour une cellule
+et le faire gérer plusieurs cellules.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellIdToMesh = {};
+function updateCellGeometry(x, y, z) {
+  const cellX = Math.floor(x / cellSize);
+  const cellY = Math.floor(y / cellSize);
+  const cellZ = Math.floor(z / cellSize);
+  const cellId = world.computeCellId(x, y, z);
+  let mesh = cellIdToMesh[cellId];
+  const geometry = mesh ? mesh.geometry : new THREE.BufferGeometry();
+
+  const {positions, normals, uvs, indices} = world.generateGeometryDataForCell(cellX, cellY, cellZ);
+  const positionNumComponents = 3;
+  geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+  const normalNumComponents = 3;
+  geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+  const uvNumComponents = 2;
+  geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+  geometry.setIndex(indices);
+  geometry.computeBoundingSphere();
+
+  if (!mesh) {
+    mesh = new THREE.Mesh(geometry, material);
+    mesh.name = cellId;
+    cellIdToMesh[cellId] = mesh;
+    scene.add(mesh);
+    mesh.position.set(cellX * cellSize, cellY * cellSize, cellZ * cellSize);
+  }
+}
+</pre>
+<p>Le code ci-dessus vérifie une map de cell ids vers les maillages. Si
+nous demandons une cellule qui n'existe pas, un nouveau <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> est créé
+et ajouté au bon endroit dans l'espace monde.
+À la fin, nous mettons à jour les attributes et les indices avec les nouvelles données.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/voxel-geometry-culled-faces-ui.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/voxel-geometry-culled-faces-ui.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Quelques notes :</p>
+<p>Le <code class="notranslate" translate="no">RayCaster</code> aurait peut-être fonctionné très bien. Je n'ai pas essayé.
+Au lieu de cela, j'ai trouvé <a href="https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf">un raycaster spécifique aux voxels</a>.
+qui est optimisé pour les voxels.</p>
+<p>J'ai fait de <code class="notranslate" translate="no">intersectRay</code> une partie de VoxelWorld car il semblait
+que si cela devenait trop lent, nous pourrions lancer des rayons contre les cellules
+avant de le faire sur les voxels comme une simple accélération si cela devenait
+trop lent.</p>
+<p>Vous pourriez vouloir changer la longueur du raycast
+car actuellement, elle va jusqu'au Z-far. Je suppose que si l'
+utilisateur clique sur quelque chose de trop éloigné, il ne veut pas vraiment
+placer des blocs de l'autre côté du monde qui font 1 ou 2 pixels.</p>
+<p>Appeler <code class="notranslate" translate="no">geometry.computeBoundingSphere</code> pourrait être lent.
+Nous pourrions simplement définir manuellement la bounding sphere pour qu'elle s'adapte
+à la cellule entière.</p>
+<p>Voulons-nous supprimer les cellules si tous les voxels de cette cellule sont à 0 ?
+Ce serait probablement un changement raisonnable si nous voulions livrer ceci.</p>
+<p>En réfléchissant à la manière dont cela fonctionne, il est clair que le
+pire des cas absolu est un damier de voxels activés et désactivés. Je ne
+sais pas d'emblée quelles autres stratégies utiliser
+si les choses deviennent trop lentes. Peut-être que devenir trop lent
+encouragerait simplement l'utilisateur à ne pas créer d'énormes zones en damier.</p>
+<p>Pour simplifier, la texture atlas n'a qu'une seule colonne
+par type de voxel. Il serait préférable de faire quelque chose de plus
+flexible où nous aurions un tableau de types de voxels et chaque
+type pourrait spécifier où se trouvent les textures de ses faces dans l'atlas.
+Tel quel, beaucoup d'espace est gaspillé.</p>
+<p>En regardant le vrai minecraft, il y a des tuiles qui ne sont pas
+des voxels, pas des cubes. Comme une tuile de clôture ou des fleurs. Pour faire cela,
+nous aurions à nouveau besoin d'un tableau de types de voxels et pour chaque
+voxel, s'il s'agit d'un cube ou d'une autre géométrie. S'il ne s'agit pas d'un cube,
+la vérification des voisins lors de la génération de la géométrie
+devrait également changer. Un voxel de fleur à côté d'un autre
+voxel ne devrait pas supprimer les faces entre eux.</p>
+<p>Si vous voulez créer quelque chose de similaire à minecraft en utilisant three.js,
+j'espère que cela vous a donné quelques idées pour commencer et comment
+générer une géométrie quelque peu efficace.</p>
+<p><canvas id="c"></canvas></p>
+<script type="module" src="../resources/threejs-voxel-geometry.js"></script>
+
 
         </div>
       </div>

+ 62 - 0
manual/fr/webgl-compatibility-check.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html><html lang="fr"><head>
+    <meta charset="utf-8">
+    <title>Vérification de la compatibilité WebGL</title>
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@threejs">
+    <meta name="twitter:title" content="Three.js – WebGL Compatibility Check">
+    <meta property="og:image" content="https://threejs.org/files/share.png">
+    <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
+    <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
+
+    <link rel="stylesheet" href="../resources/lesson.css">
+    <link rel="stylesheet" href="../resources/lang.css">
+<script type="importmap">
+{
+  "imports": {
+    "three": "../../build/three.module.js"
+  }
+}
+</script>
+  </head>
+  <body>
+    <div class="container">
+      <div class="lesson-title">
+        <h1>Vérification de la compatibilité WebGL</h1>
+      </div>
+      <div class="lesson">
+        <div class="lesson-main">
+          
+          <p>
+            Bien que cela devienne de moins en moins un problème, certains appareils ou navigateurs peuvent encore ne pas prendre en charge WebGL 2.
+            La méthode suivante vous permet de vérifier s'il est pris en charge et d'afficher un message à l'utilisateur si ce n'est pas le cas.
+            Importez le module de détection de la prise en charge WebGL et exécutez le code suivant avant de tenter de rendre quoi que ce soit.
+          </p>
+      
+<pre class="prettyprint notranslate lang-js" translate="no">
+import WebGL from 'three/addons/capabilities/WebGL.js';
+
+if ( WebGL.isWebGL2Available() ) {
+
+  // Initialisez la fonction ou d'autres initialisations ici
+  animate();
+
+} else {
+
+  const warning = WebGL.getWebGL2ErrorMessage();
+  document.getElementById( 'container' ).appendChild( warning );
+
+}
+</pre>
+          
+        </div>
+      </div>
+    </div>
+
+  <script src="../resources/prettify.js"></script>
+  <script src="../resources/lesson.js"></script>
+
+
+
+
+</body></html>

+ 338 - 4
manual/fr/webxr-basics.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>VR</title>
+    <title>RV</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,12 +22,346 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>VR</h1>
+        <h1>RV</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/webxr.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p>Créer une application RV dans three.js est assez simple. Il suffit essentiellement de dire à
+three.js que vous souhaitez utiliser WebXR. Si vous y réfléchissez, quelques points concernant WebXR
+devraient être clairs. La direction vers laquelle la caméra pointe est fournie par le système RV
+lui-même, puisque l'utilisateur tourne la tête pour choisir une direction à regarder. De même,
+le champ de vision et l'aspect seront fournis par le système RV, car chaque système a un champ
+de vision et un aspect d'affichage différents.</p>
+<p>Prenons un exemple de l'article sur <a href="responsive.html">la création d'une page web responsive</a>
+et rendons-le compatible avec la RV.</p>
+<p>Avant de commencer, vous aurez besoin d'un dispositif compatible RV comme un smartphone Android,
+Google Daydream, Oculus Go, Oculus Rift, Vive, Samsung Gear VR, un iPhone avec un
+<a href="https://apps.apple.com/us/app/webxr-viewer/id1295998056">navigateur WebXR</a>.</p>
+<p>Ensuite, si vous exécutez localement, vous devez exécuter un simple serveur web, comme
+expliqué dans <a href="setup.html">l'article sur la configuration</a>.</p>
+<p>Si l'appareil que vous utilisez pour visualiser la RV n'est pas le même ordinateur sur lequel vous
+exécutez, vous devez servir votre page web via https, sinon le navigateur ne permettra pas d'utiliser
+l'API WebXR. Le serveur mentionné dans <a href="setup.html">l'article sur la configuration</a>
+appelé <a href="https://greggman.github.io/servez">Servez</a> a une option pour utiliser https.
+Cochez-le et démarrez le serveur.</p>
+<div class="threejs_center"><img src="../resources/images/servez-https.png" class="nobg" style="width: 912px;"></div>
+
+<p>Notez les URL. Vous avez besoin de celle qui correspond à l'adresse IP locale de votre ordinateur.
+Elle commencera généralement par <code class="notranslate" translate="no">192</code>, <code class="notranslate" translate="no">172</code> ou <code class="notranslate" translate="no">10</code>. Saisissez cette adresse complète, y compris la partie <code class="notranslate" translate="no">https://</code>
+dans le navigateur de votre appareil RV. Note : Votre ordinateur et votre appareil RV doivent être sur le même réseau local
+ou WiFi, et vous devez probablement être sur un réseau domestique. note : De nombreux cafés sont configurés pour interdire ce type
+de connexion machine à machine.</p>
+<p>Vous serez accueilli par une erreur ressemblant à celle ci-dessous. Cliquez sur "avancé" puis cliquez sur
+<em>continuer</em>.</p>
+<div class="threejs_center"><img src="../resources/images/https-warning.gif"></div>
+
+<p>Vous pouvez maintenant exécuter vos exemples.</p>
+<p>Si vous vous lancez vraiment dans le développement WebXR, une autre chose que vous devriez apprendre est
+<a href="https://developers.google.com/web/tools/chrome-devtools/remote-debugging/">le débogage à distance</a>
+afin de pouvoir voir les avertissements, les erreurs de console, et bien sûr, réellement
+<a href="debugging-javascript.html">déboguer votre code</a>.</p>
+<p>Si vous voulez juste voir le code fonctionner ci-dessous, vous pouvez simplement exécuter le code depuis
+ce site.</p>
+<p>La première chose à faire est d'inclure le support RV après
+avoir inclus three.js</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
++import {VRButton} from 'three/addons/webxr/VRButton.js';
+</pre>
+<p>Ensuite, nous devons activer le support WebXR de three.js et ajouter son
+bouton RV à notre page</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
++  renderer.xr.enabled = true;
++  document.body.appendChild(VRButton.createButton(renderer));
+</pre>
+<p>Nous devons laisser three.js exécuter notre boucle de rendu. Jusqu'à présent, nous avons utilisé une
+boucle <code class="notranslate" translate="no">requestAnimationFrame</code>, mais pour supporter la RV, nous devons laisser three.js gérer
+notre boucle de rendu pour nous. Nous pouvons le faire en appelant
+<a href="/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop"><code class="notranslate" translate="no">WebGLRenderer.setAnimationLoop</code></a> et en passant une fonction à appeler pour la boucle.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    camera.aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.updateProjectionMatrix();
+  }
+
+  cubes.forEach((cube, ndx) =&gt; {
+    const speed = 1 + ndx * .1;
+    const rot = time * speed;
+    cube.rotation.x = rot;
+    cube.rotation.y = rot;
+  });
+
+  renderer.render(scene, camera);
+
+-  requestAnimationFrame(render);
+}
+
+-requestAnimationFrame(render);
++renderer.setAnimationLoop(render);
+</pre>
+<p>Il y a un détail de plus. Nous devrions probablement définir une hauteur de caméra
+qui soit à peu près moyenne pour un utilisateur debout.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
++camera.position.set(0, 1.6, 0);
+</pre>
+<p>et déplacer les cubes pour qu'ils soient devant la caméra</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cube = new THREE.Mesh(geometry, material);
+scene.add(cube);
+
+cube.position.x = x;
++cube.position.y = 1.6;
++cube.position.z = -2;
+</pre>
+<p>Nous les avons définis à <code class="notranslate" translate="no">z = -2</code> car la caméra sera maintenant à <code class="notranslate" translate="no">z = 0</code> et
+la caméra par défaut regarde vers l'axe -z.</p>
+<p>Cela soulève un point extrêmement important. <strong>Les unités en RV sont des mètres</strong>.
+En d'autres termes, <strong>Une Unité = Un Mètre</strong>. Cela signifie que la caméra est à 1,6 mètres au-dessus de 0.
+Les centres des cubes sont à 2 mètres devant la caméra. Chaque cube
+a une taille de 1x1x1 mètre. C'est important car la RV doit ajuster les choses à l'utilisateur
+<em>dans le monde réel</em>. Cela signifie que les unités utilisées dans three.js doivent correspondre
+aux mouvements de l'utilisateur lui-même.</p>
+<p>Et avec cela, nous devrions obtenir 3 cubes tournant devant
+la caméra avec un bouton pour entrer en RV.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-basic.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-basic.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Je trouve que la RV fonctionne mieux si nous avons quelque chose entourant la caméra, comme
+une pièce pour référence, alors ajoutons une simple cubemap en grille comme nous l'avons vu dans
+<a href="backgrounds.html">l'article sur les arrière-plans</a>. Nous utiliserons simplement la même texture de grille
+pour chaque côté du cube, ce qui donnera une salle en grille.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
++{
++  const loader = new THREE.CubeTextureLoader();
++  const texture = loader.load([
++    'resources/images/grid-1024.png',
++    'resources/images/grid-1024.png',
++    'resources/images/grid-1024.png',
++    'resources/images/grid-1024.png',
++    'resources/images/grid-1024.png',
++    'resources/images/grid-1024.png',
++  ]);
++  scene.background = texture;
++}
+</pre>
+<p>C'est mieux.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-basic-w-background.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-basic-w-background.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Note : Pour voir réellement la RV, vous aurez besoin d'un appareil compatible WebXR.
+Je crois que la plupart des téléphones Android peuvent supporter WebXR en utilisant Chrome ou Firefox.
+Pour iOS, vous pourriez pouvoir utiliser cette <a href="https://apps.apple.com/us/app/webxr-viewer/id1295998056">application WebXR</a>,
+bien qu'en général, le support WebXR sur iOS ne soit pas supporté en mai 2019.</p>
+<p>Pour utiliser WebXR sur Android ou iPhone, vous aurez besoin d'un <em>Casque RV</em>
+pour téléphones. Vous pouvez en trouver entre 5$ pour un fait de carton
+et 100$. Malheureusement, je ne sais pas lesquels recommander. J'en ai acheté
+6 au fil des ans et ils sont tous de qualité variable. Je n'ai
+jamais payé plus d'environ 25$.</p>
+<p>Juste pour mentionner quelques-uns des problèmes</p>
+<ol>
+<li><p>Sont-ils compatibles avec votre téléphone</p>
+<p>Les téléphones existent en différentes tailles et les casques RV doivent donc correspondre.
+De nombreux casques prétendent correspondre à une grande variété de tailles. Mon expérience
+est que plus ils correspondent à de tailles, moins ils sont performants, car au lieu
+d'être conçus pour une taille spécifique, ils doivent faire des compromis
+pour correspondre à plus de tailles. Malheureusement, les casques multi-tailles sont le type le plus courant.</p>
+</li>
+<li><p>Peuvent-ils faire la mise au point pour votre visage</p>
+<p>Certains appareils ont plus d'ajustements que d'autres. Généralement, il y a
+au maximum 2 ajustements. La distance entre les lentilles et vos yeux
+et la distance entre les lentilles.</p>
+</li>
+<li><p>Sont-ils trop réfléchissants</p>
+<p>De nombreux casques ont un cône en plastique entre votre œil et le téléphone.
+Si ce plastique est brillant ou réfléchissant, il agira comme
+un miroir reflétant l'écran et sera très distrayant.</p>
+<p>Peu de critiques, voire aucune, ne semblent couvrir ce problème.</p>
+</li>
+<li><p>Sont-ils confortables sur votre visage.</p>
+<p>La plupart des appareils reposent sur votre nez comme une paire de lunettes.
+Cela peut faire mal après quelques minutes. Certains ont des sangles qui passent
+autour de votre tête. D'autres ont une 3ème sangle qui passe par-dessus votre tête. Cela
+peut aider ou non à maintenir l'appareil au bon endroit.</p>
+<p>Il s'avère que pour la plupart (tous ?) des appareils, vos yeux doivent être centrés
+avec les lentilles. Si les lentilles sont légèrement au-dessus ou en dessous de vos
+yeux, l'image devient floue. Cela peut être très frustrant
+car les choses peuvent commencer nettes, mais 45 à 60 secondes plus tard, l'appareil
+s'est déplacé de 1 millimètre vers le haut ou vers le bas et vous réalisez soudain que vous avez
+lutté pour faire la mise au point sur une image floue.</p>
+</li>
+<li><p>Sont-ils compatibles avec vos lunettes.</p>
+<p>Si vous portez des lunettes, vous devrez lire les critiques pour voir
+si un casque particulier fonctionne bien avec les lunettes.</p>
+</li>
+</ol>
+<p>Je ne peux vraiment pas faire de recommandations malheureusement. <a href="https://vr.google.com/cardboard/get-cardboard/">Google propose quelques recommandations
+bon marché faites en carton</a>,
+certains à partir de 5$, alors peut-être commencer par là et si vous aimez,
+envisagez de passer à la vitesse supérieure. 5$ c'est le prix d'un café, alors sérieusement, essayez !</p>
+<p>Il existe également 3 types de dispositifs de base.</p>
+<ol>
+<li><p>3 degrés de liberté (3dof), pas de dispositif d'entrée</p>
+<p>C'est généralement le style téléphone, bien que parfois vous puissiez
+acheter un dispositif d'entrée tiers. Les 3 degrés de liberté
+signifient que vous pouvez regarder vers le haut/bas (1), gauche/droite (2) et que vous pouvez incliner
+la tête gauche et droite (3).</p>
+</li>
+<li><p>3 degrés de liberté (3dof) avec 1 dispositif d'entrée (3dof)</p>
+<p>C'est fondamentalement Google Daydream et Oculus GO</p>
+<p>Ceux-ci permettent également 3 degrés de liberté et incluent un petit
+contrôleur qui agit comme un pointeur laser dans la RV.
+Le pointeur laser n'a également que 3 degrés de liberté. Le
+système peut dire dans quelle direction le dispositif d'entrée pointe, mais
+il ne peut pas dire où se trouve le dispositif.</p>
+</li>
+<li><p>6 degrés de liberté (6dof) avec dispositifs d'entrée (6dof)</p>
+<p>Ceux-ci sont <em>le vrai truc</em> haha. 6 degrés de liberté
+signifie que non seulement ces appareils savent dans quelle direction vous regardez,
+mais ils savent aussi où se trouve réellement votre tête. Cela signifie que
+si vous vous déplacez de gauche à droite ou d'avant en arrière ou si vous vous levez / vous asseyez,
+les appareils peuvent enregistrer cela et tout dans la RV se déplace en conséquence.
+C'est incroyablement et étonnamment réaliste. Avec une bonne démo,
+vous serez époustouflé, ou du moins je l'ai été et je le suis toujours.</p>
+<p>De plus, ces appareils incluent généralement 2 contrôleurs, un
+pour chaque main, et le système peut dire exactement où se trouvent vos
+mains et dans quelle orientation elles sont, de sorte que vous pouvez
+manipuler des choses en RV en tendant simplement la main, touchant,
+poussant, tournant, etc...</p>
+<p>Les appareils à 6 degrés de liberté incluent le Vive et Vive Pro,
+l'Oculus Rift et Quest, et je crois tous les appareils Windows MR.</p>
+</li>
+</ol>
+<p>Avec tout cela couvert, je ne sais pas avec certitude quels appareils fonctionneront avec WebXR.
+Je suis sûr à 99% que la plupart des téléphones Android fonctionneront avec Chrome. Vous pourriez
+avoir besoin d'activer le support WebXR dans <a href="about:flags"><code class="notranslate" translate="no">about:flags</code></a>. Je sais aussi que Google
+Daydream fonctionnera également et de même, vous devez activer le support WebXR dans
+<a href="about:flags"><code class="notranslate" translate="no">about:flags</code></a>. Oculus Rift, Vive et Vive Pro fonctionneront via
+Chrome ou Firefox. Je suis moins sûr pour Oculus Go et Oculus Quest car les deux
+utilisent des systèmes d'exploitation personnalisés, mais selon Internet, ils semblent tous deux fonctionner.</p>
+<p>Bien, après cette longue digression sur les dispositifs RV et WebXR, il y a certaines choses à couvrir</p>
+<ul>
+<li><p>Prise en charge de la RV et de la non-RV</p>
+<p>Pour autant que je sache, du moins depuis la version r112, il n'y a pas de moyen simple de prendre en charge
+les modes RV et non-RV avec three.js. Idéalement,
+si vous n'êtes pas en mode RV, vous devriez pouvoir contrôler la caméra en utilisant
+les moyens que vous souhaitez, par exemple les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>,
+et vous devriez obtenir un événement lors du passage en mode RV et
+de la sortie du mode RV afin que vous puissiez activer/désactiver les contrôles.</p>
+</li>
+</ul>
+<p>Si three.js ajoute un support pour faire les deux, j'essaierai de mettre à jour
+cet article. En attendant, vous pourriez avoir besoin de 2 versions de votre
+site OU de passer un drapeau dans l'URL, quelque chose comme</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">https://mysite.com/mycooldemo?allowvr=true
+</pre><p>Alors nous pourrions ajouter des liens pour changer de mode</p>
+<pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
+  &lt;canvas id="c"&gt;&lt;/canvas&gt;
++  &lt;div class="mode"&gt;
++    &lt;a href="?allowvr=true" id="vr"&gt;Autoriser la RV&lt;/a&gt;
++    &lt;a href="?" id="nonvr"&gt;Utiliser le mode non-RV&lt;/a&gt;
++  &lt;/div&gt;
+&lt;/body&gt;
+</pre>
+<p>et du CSS pour les positionner</p>
+<pre class="prettyprint showlinemods notranslate lang-css" translate="no">body {
+    margin: 0;
+}
+#c {
+    width: 100%;
+    height: 100%;
+    display: block;
+}
++.mode {
++  position: absolute;
++  right: 1em;
++  top: 1em;
++}
+</pre>
+<p>dans votre code, vous pourriez utiliser ce paramètre comme ceci</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+-  renderer.xr.enabled = true;
+-  document.body.appendChild(VRButton.createButton(renderer));
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 5;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.set(0, 1.6, 0);
+
++  const params = (new URL(document.location)).searchParams;
++  const allowvr = params.get('allowvr') === 'true';
++  if (allowvr) {
++    renderer.xr.enabled = true;
++    document.body.appendChild(VRButton.createButton(renderer));
++    document.querySelector('#vr').style.display = 'none';
++  } else {
++    // no VR, add some controls
++    const controls = new OrbitControls(camera, canvas);
++    controls.target.set(0, 1.6, -2);
++    controls.update();
++    document.querySelector('#nonvr').style.display = 'none';
++  }
+</pre>
+<p>Que ce soit bon ou mauvais, je ne sais pas. J'ai l'impression que les différences
+entre ce qui est nécessaire pour la RV et ce qui est nécessaire pour la non-RV sont souvent
+très différentes, donc pour tout sauf les choses les plus simples, peut-être que 2 pages séparées
+sont meilleures ? Vous devrez décider.</p>
+<p>Note : pour diverses raisons, cela ne fonctionnera pas dans l'éditeur en direct
+sur ce site, donc si vous voulez le vérifier,
+<a href="../examples/webxr-basic-vr-optional.html" target="_blank">cliquez ici</a>.
+Il devrait démarrer en mode non-RV et vous pouvez utiliser la souris ou les doigts pour déplacer
+la caméra. Cliquer sur "Autoriser la RV" devrait basculer pour permettre le mode RV et vous devriez
+pouvoir cliquer sur "Entrer en RV" si vous êtes sur un dispositif RV.</p>
+<ul>
+<li><p>Décider du niveau de support RV</p>
+<p>Ci-dessus, nous avons couvert 3 types de dispositifs RV.</p>
+<ul>
+<li>3DOF sans entrée</li>
+<li>3DOF + entrée 3DOF</li>
+<li>6DOF + entrée 6DOF</li>
+</ul>
+<p>Vous devez décider combien d'efforts vous êtes prêt à investir
+pour supporter chaque type de dispositif.</p>
+<p>Par exemple, le dispositif le plus simple n'a pas d'entrée. Le mieux que vous puissiez
+généralement faire est de faire en sorte qu'il y ait des boutons ou des objets dans la vue de l'utilisateur
+et si l'utilisateur aligne un marqueur au centre de l'affichage
+sur ces objets pendant une demi-seconde environ, alors ce bouton est cliqué.
+Une UX courante consiste à afficher un petit minuteur qui apparaîtra au-dessus de l'objet indiquant
+que si vous maintenez le marqueur à cet endroit pendant un moment, l'objet/bouton sera sélectionné.</p>
+<p>Puisqu'il n'y a pas d'autre entrée, c'est à peu près le mieux que vous puissiez faire</p>
+<p>Au niveau supérieur, vous avez un dispositif d'entrée 3DOF. Généralement, il
+peut pointer vers des choses et l'utilisateur dispose d'au moins 2 boutons. Le Daydream
+possède également un pavé tactile qui fournit des entrées tactiles normales.</p>
+<p>Dans tous les cas, si un utilisateur dispose de ce type d'appareil, il est beaucoup plus
+confortable pour l'utilisateur de pouvoir pointer les choses avec
+son contrôleur que de devoir le faire avec sa tête en regardant les choses.</p>
+<p>Un niveau similaire pourrait être un appareil 3DOF ou 6DOF avec un
+contrôleur de console de jeu. Vous devrez décider quoi faire ici.
+Je soupçonne que la chose la plus courante est que l'utilisateur doit toujours regarder
+pour pointer et le contrôleur est juste utilisé pour les boutons.</p>
+<p>Le dernier niveau est un utilisateur avec un casque 6DOF et 2 contrôleurs 6DOF.
+Ces utilisateurs trouveront souvent une expérience qui n'est que 3DOF
+frustrante. De même, ils s'attendent généralement à pouvoir
+manipuler virtuellement des choses avec leurs mains en RV, donc vous devrez
+décider si vous voulez supporter cela ou non.</p>
+</li>
+</ul>
+<p>Comme vous pouvez le voir, commencer en RV est assez facile, mais réaliser quelque chose de livrable en RV
+nécessitera beaucoup de décisions et de conception.</p>
+<p>Ceci était une brève introduction à la RV avec three.js. Nous aborderons
+certaines méthodes d'entrée dans des <a href="webxr-look-to-select.html">articles futurs</a>.</p>
 
         </div>
       </div>

+ 350 - 4
manual/fr/webxr-look-to-select.html

@@ -1,6 +1,6 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>VR - Look to Select</title>
+    <title>VR - Sélection par le regard</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
@@ -22,12 +22,358 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>VR - Look to Select</h1>
+        <h1>VR - Sélection par le regard</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/webxr-look-to-select.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p><strong>NOTE : Les exemples de cette page nécessitent un appareil compatible VR. Sans cela, ils ne fonctionneront pas. Voir <a href="webxr.html">l'article précédent</a> pour comprendre pourquoi.</strong></p>
+<p>Dans <a href="webxr.html">l'article précédent</a>, nous avons abordé un exemple VR très simple utilisant three.js et nous avons discuté des différents types de systèmes VR.</p>
+<p>Le plus simple et probablement le plus courant est le style VR Google Cardboard qui consiste essentiellement en un téléphone placé dans un masque facial coûtant entre 5 et 50 dollars. Ce type de VR n'a pas de contrôleur, les gens doivent donc trouver des solutions créatives pour permettre l'entrée utilisateur.</p>
+<p>La solution la plus courante est la "sélection par le regard" où si l'utilisateur pointe sa tête vers quelque chose pendant un moment, cela est sélectionné.</p>
+<p>Implémentons la "sélection par le regard" ! Nous allons commencer par <a href="webxr.html">un exemple de l'article précédent</a> et pour ce faire, nous ajouterons le <code class="notranslate" translate="no">PickHelper</code> que nous avons créé dans <a href="picking.html">l'article sur le picking</a>. Le voici.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class PickHelper {
+  constructor() {
+    this.raycaster = new THREE.Raycaster();
+    this.pickedObject = null;
+    this.pickedObjectSavedColor = 0;
+  }
+  pick(normalizedPosition, scene, camera, time) {
+    // restaurer la couleur s'il y a un objet sélectionné
+    if (this.pickedObject) {
+      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
+      this.pickedObject = undefined;
+    }
+
+    // lancer un rayon à travers le frustum
+    this.raycaster.setFromCamera(normalizedPosition, camera);
+    // obtenir la liste des objets intersectés par le rayon
+    const intersectedObjects = this.raycaster.intersectObjects(scene.children);
+    if (intersectedObjects.length) {
+      // sélectionner le premier objet. C'est le plus proche
+      this.pickedObject = intersectedObjects[0].object;
+      // sauvegarder sa couleur
+      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+      // définir sa couleur d'émission sur rouge/jaune clignotant
+      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+    }
+  }
+}
+</pre>
+<p>Pour une explication de ce code, <a href="picking.html">voir l'article sur le picking</a>.</p>
+<p>Pour l'utiliser, il suffit de créer une instance et de l'appeler dans notre boucle de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const pickHelper = new PickHelper();
+
+...
+function render(time) {
+  time *= 0.001;
+
+  ...
+
++  // 0, 0 est le centre de la vue en coordonnées normalisées.
++  pickHelper.pick({x: 0, y: 0}, scene, camera, time);
+</pre>
+<p>Dans l'exemple de picking original, nous avons converti les coordonnées de la souris des pixels CSS en coordonnées normalisées qui vont de -1 à +1 sur le canevas.</p>
+<p>Dans ce cas, cependant, nous sélectionnerons toujours l'endroit où la caméra est dirigée, c'est-à-dire le centre de l'écran, nous passons donc <code class="notranslate" translate="no">0</code> pour <code class="notranslate" translate="no">x</code> et <code class="notranslate" translate="no">y</code>, ce qui correspond au centre en coordonnées normalisées.</p>
+<p>Et avec cela, les objets clignoteront lorsque nous les regarderons.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-look-to-select.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-look-to-select.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Généralement, nous ne voulons pas que la sélection soit immédiate. Au lieu de cela, nous demandons à l'utilisateur de maintenir la caméra sur l'objet qu'il souhaite sélectionner pendant quelques instants afin de lui donner une chance de ne pas sélectionner quelque chose par accident.</p>
+<p>Pour ce faire, nous avons besoin d'une sorte de compteur ou de jauge ou d'un moyen quelconque pour indiquer que l'utilisateur doit continuer à regarder et pendant combien de temps.</p>
+<p>Une façon simple de procéder est de créer une texture à 2 couleurs et d'utiliser un décalage de texture pour faire glisser la texture sur un modèle.</p>
+<p>Faisons cela séparément pour voir comment cela fonctionne avant de l'ajouter à l'exemple VR.</p>
+<p>Tout d'abord, nous créons une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = -2;    // Utiliser les valeurs pour gauche
+const right = 2;    // droite, haut et bas
+const top = 1;      // qui correspondent à la taille
+const bottom = -1;  // par défaut du canevas.
+const near = -1;
+const far = 1;
+const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
+</pre>
+<p>Et bien sûr, la mettre à jour si la taille du canevas change.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    const aspect = canvas.clientWidth / canvas.clientHeight;
++    camera.left = -aspect;
++    camera.right = aspect;
+    camera.updateProjectionMatrix();
+  }
+  ...
+</pre>
+<p>Nous avons maintenant une caméra qui montre 2 unités au-dessus et en dessous du centre et des unités d'aspect à gauche et à droite.</p>
+<p>Ensuite, créons une texture à 2 couleurs. Nous utiliserons une <a href="/docs/#api/en/textures/DataTexture"><code class="notranslate" translate="no">DataTexture</code></a> que nous avons utilisée à <a href="indexed-textures.html">quelques autres</a> <a href="post-processing-3dlut.html">endroits</a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeDataTexture(data, width, height) {
+  const texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat);
+  texture.minFilter = THREE.NearestFilter;
+  texture.magFilter = THREE.NearestFilter;
+  texture.needsUpdate = true;
+  return texture;
+}
+
+const cursorColors = new Uint8Array([
+  64, 64, 64, 64,       // gris foncé
+  255, 255, 255, 255,   // blanc
+]);
+const cursorTexture = makeDataTexture(cursorColors, 2, 1);
+</pre>
+<p>Nous utiliserons ensuite cette texture sur une <a href="/docs/#api/en/geometries/TorusGeometry"><code class="notranslate" translate="no">TorusGeometry</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const ringRadius = 0.4;
+const tubeRadius = 0.1;
+const tubeSegments = 4;
+const ringSegments = 64;
+const cursorGeometry = new THREE.TorusGeometry(
+    ringRadius, tubeRadius, tubeSegments, ringSegments);
+
+const cursorMaterial = new THREE.MeshBasicMaterial({
+  color: 'white',
+  map: cursorTexture,
+  transparent: true,
+  blending: THREE.CustomBlending,
+  blendSrc: THREE.OneMinusDstColorFactor,
+  blendDst: THREE.OneMinusSrcColorFactor,
+});
+const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial);
+scene.add(cursor);
+</pre>
+<p>et ensuite dans <code class="notranslate" translate="no">render</code>, ajustons le décalage de la texture.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+  time *= 0.001;
+
+  if (resizeRendererToDisplaySize(renderer)) {
+    const canvas = renderer.domElement;
+    const aspect = canvas.clientWidth / canvas.clientHeight;
+    camera.left = -aspect;
+    camera.right = aspect;
+    camera.updateProjectionMatrix();
+  }
+
++  const fromStart = 0;
++  const fromEnd = 2;
++  const toStart = -0.5;
++  const toEnd = 0.5;
++  cursorTexture.offset.x = THREE.MathUtils.mapLinear(
++      time % 2,
++      fromStart, fromEnd,
++      toStart, toEnd);
+
+  renderer.render(scene, camera);
+}
+</pre>
+<p><code class="notranslate" translate="no">THREE.MathUtils.mapLinear</code> prend une valeur qui se situe entre <code class="notranslate" translate="no">fromStart</code> et <code class="notranslate" translate="no">fromEnd</code> et la mappe à une valeur entre <code class="notranslate" translate="no">toStart</code> et <code class="notranslate" translate="no">toEnd</code>. Dans le cas ci-dessus, nous prenons <code class="notranslate" translate="no">time % 2</code>, ce qui signifie une valeur qui va de 0 à 2 et la mappons à une valeur qui va de -0.5 à 0.5.</p>
+<p>Les <a href="textures.html">textures</a> sont mappées à la géométrie en utilisant des coordonnées de texture normalisées qui vont de 0 à 1. Cela signifie que notre image de 2x1 pixels, définie sur le mode de répétition par défaut de <code class="notranslate" translate="no">THREE.ClampToEdge</code>, si nous ajustons les coordonnées de texture de -0.5, alors toute la maille sera de la première couleur et si nous ajustons les coordonnées de texture de +0.5, toute la maille sera de la deuxième couleur. Entre les deux, avec le filtrage défini sur <code class="notranslate" translate="no">THREE.NearestFilter</code>, nous pourrons déplacer la transition entre les 2 couleurs à travers la géométrie.</p>
+<p>Ajoutons une texture d'arrière-plan tant que nous y sommes, comme nous l'avons vu dans <a href="backgrounds.html">l'article sur les arrière-plans</a>. Nous utiliserons simplement un ensemble de couleurs 2x2, mais définirons les paramètres de répétition de la texture pour nous donner une grille 8x8. Cela donnera à notre curseur quelque chose sur lequel être rendu afin que nous puissions le vérifier par rapport à différentes couleurs.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const backgroundColors = new Uint8Array([
++    0,   0,   0, 255,  // noir
++   90,  38,  38, 255,  // rouge foncé
++  100, 175, 103, 255,  // vert moyen
++  255, 239, 151, 255,  // jaune clair
++]);
++const backgroundTexture = makeDataTexture(backgroundColors, 2, 2);
++backgroundTexture.wrapS = THREE.RepeatWrapping;
++backgroundTexture.wrapT = THREE.RepeatWrapping;
++backgroundTexture.repeat.set(4, 4);
+
+const scene = new THREE.Scene();
++scene.background = backgroundTexture;
+</pre>
+<p>Maintenant, si nous exécutons cela, vous verrez que nous obtenons une jauge en forme de cercle et que nous pouvons définir où se trouve la jauge.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-look-to-select-selector.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-look-to-select-selector.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Quelques points à noter <strong>et à essayer</strong>.</p>
+<ul>
+<li><p>Nous avons défini les propriétés <code class="notranslate" translate="no">blending</code>, <code class="notranslate" translate="no">blendSrc</code> et <code class="notranslate" translate="no">blendDst</code> du <code class="notranslate" translate="no">cursorMaterial</code> comme suit :</p>
+<pre class="prettyprint showlinemods notranslate notranslate" translate="no">  blending: THREE.CustomBlending,
+  blendSrc: THREE.OneMinusDstColorFactor,
+  blendDst: THREE.OneMinusSrcColorFactor,
+</pre><p>Cela donne un effet de type <em>inverse</em>. Commentez ces 3 lignes et vous verrez la différence. Je suppose simplement que l'effet inverse est le meilleur ici, car de cette façon, nous pouvons, espérons-le, voir le curseur quelles que soient les couleurs sur lesquelles il se trouve.</p>
+</li>
+<li><p>Nous utilisons une <a href="/docs/#api/en/geometries/TorusGeometry"><code class="notranslate" translate="no">TorusGeometry</code></a> et non une <a href="/docs/#api/en/geometries/RingGeometry"><code class="notranslate" translate="no">RingGeometry</code></a>.</p>
+<p>Pour une raison quelconque, la <a href="/docs/#api/en/geometries/RingGeometry"><code class="notranslate" translate="no">RingGeometry</code></a> utilise un schéma de mappage UV plat. De ce fait, si nous utilisons une <a href="/docs/#api/en/geometries/RingGeometry"><code class="notranslate" translate="no">RingGeometry</code></a>, la texture glisse horizontalement sur l'anneau au lieu de l'entourer comme c'est le cas ci-dessus.</p>
+<p>Essayez, changez la <a href="/docs/#api/en/geometries/TorusGeometry"><code class="notranslate" translate="no">TorusGeometry</code></a> en une <a href="/docs/#api/en/geometries/RingGeometry"><code class="notranslate" translate="no">RingGeometry</code></a> (elle est simplement commentée dans l'exemple ci-dessus) et vous verrez ce que je veux dire.</p>
+<p>La chose la plus <em>correcte</em> à faire (selon une certaine définition de <em>correct</em>) serait soit d'utiliser la <a href="/docs/#api/en/geometries/RingGeometry"><code class="notranslate" translate="no">RingGeometry</code></a> mais de corriger les coordonnées de texture pour qu'elles fassent le tour de l'anneau. Ou bien, générer notre propre géométrie d'anneau. Mais, le tore fonctionne très bien. Placé directement devant la caméra avec un <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>, il ressemblera exactement à un anneau et les coordonnées de texture font le tour de l'anneau, donc cela fonctionne pour nos besoins.</p>
+</li>
+</ul>
+<p>Intégrons-le avec notre code VR ci-dessus.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class PickHelper {
+-  constructor() {
++  constructor(camera) {
+    this.raycaster = new THREE.Raycaster();
+    this.pickedObject = null;
+-    this.pickedObjectSavedColor = 0;
+
++    const cursorColors = new Uint8Array([
++      64, 64, 64, 64,       // gris foncé
++      255, 255, 255, 255,   // blanc
++    ]);
++    this.cursorTexture = makeDataTexture(cursorColors, 2, 1);
++
++    const ringRadius = 0.4;
++    const tubeRadius = 0.1;
++    const tubeSegments = 4;
++    const ringSegments = 64;
++    const cursorGeometry = new THREE.TorusGeometry(
++        ringRadius, tubeRadius, tubeSegments, ringSegments);
++
++    const cursorMaterial = new THREE.MeshBasicMaterial({
++      color: 'white',
++      map: this.cursorTexture,
++      transparent: true,
++      blending: THREE.CustomBlending,
++      blendSrc: THREE.OneMinusDstColorFactor,
++      blendDst: THREE.OneMinusSrcColorFactor,
++    });
++    const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial);
++    // ajouter le curseur comme enfant de la caméra
++    camera.add(cursor);
++    // et le déplacer devant la caméra
++    cursor.position.z = -1;
++    const scale = 0.05;
++    cursor.scale.set(scale, scale, scale);
++    this.cursor = cursor;
++
++    this.selectTimer = 0;
++    this.selectDuration = 2;
++    this.lastTime = 0;
+  }
+  pick(normalizedPosition, scene, camera, time) {
++    const elapsedTime = time - this.lastTime;
++    this.lastTime = time;
+
+-    // restore the color if there is a picked object
+-    if (this.pickedObject) {
+-      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
+-      this.pickedObject = undefined;
+-    }
+
++    const lastPickedObject = this.pickedObject;
++    this.pickedObject = undefined;
+
+    // lancer un rayon à travers le frustum
+    this.raycaster.setFromCamera(normalizedPosition, camera);
+    // obtenir la liste des objets intersectés par le rayon
+    const intersectedObjects = this.raycaster.intersectObjects(scene.children);
+    if (intersectedObjects.length) {
+      // sélectionner le premier objet. C'est le plus proche
+      this.pickedObject = intersectedObjects[0].object;
+-      // save its color
+-      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
+-      // set its emissive color to flashing red/yellow
+-      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);
+    }
+
++    // afficher le curseur uniquement s'il touche quelque chose
++    this.cursor.visible = this.pickedObject ? true : false;
++
++    let selected = false;
++
++    // si nous regardons le même objet qu'avant
++    // incrémenter le minuteur de sélection
++    if (this.pickedObject &amp;&amp; lastPickedObject === this.pickedObject) {
++      this.selectTimer += elapsedTime;
++      if (this.selectTimer &gt;= this.selectDuration) {
++        this.selectTimer = 0;
++        selected = true;
++      }
++    } else {
++      this.selectTimer = 0;
++    }
++
++    // définir le matériau du curseur pour afficher l'état du minuteur
++    const fromStart = 0;
++    const fromEnd = this.selectDuration;
++    const toStart = -0.5;
++    const toEnd = 0.5;
++    this.cursorTexture.offset.x = THREE.MathUtils.mapLinear(
++        this.selectTimer,
++        fromStart, fromEnd,
++        toStart, toEnd);
++
++    return selected ? this.pickedObject : undefined;
+  }
+}
+</pre>
+<p>Vous pouvez voir dans le code ci-dessus que nous avons ajouté tout le code pour créer la géométrie, la texture et le matériau du curseur, et nous l'avons ajouté comme enfant de la caméra afin qu'il soit toujours devant la caméra. Notez que nous devons ajouter la caméra à la scène, sinon le curseur ne sera pas rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+scene.add(camera);
+</pre>
+<p>Nous vérifions ensuite si l'objet que nous sélectionnons cette fois est le même que la dernière fois. Si c'est le cas, nous ajoutons le temps écoulé à un minuteur et si le minuteur atteint sa limite, nous retournons l'élément sélectionné.</p>
+<p>Maintenant, utilisons cela pour sélectionner les cubes. Comme simple exemple, nous allons ajouter également 3 sphères. Lorsqu'un cube est sélectionné, nous cachons le cube et révélons la sphère correspondante.</p>
+<p>Donc, d'abord, nous allons créer une géométrie de sphère.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const boxWidth = 1;
+const boxHeight = 1;
+const boxDepth = 1;
+-const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
++const boxGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
++
++const sphereRadius = 0.5;
++const sphereGeometry = new THREE.SphereGeometry(sphereRadius);
+</pre>
+<p>Ensuite, créons 3 paires de maillages (meshes) boîte et sphère. Nous utiliserons une <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"><code class="notranslate" translate="no">Map</code></a> afin de pouvoir associer chaque <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> à son partenaire.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const cubes = [
+-  makeInstance(geometry, 0x44aa88,  0),
+-  makeInstance(geometry, 0x8844aa, -2),
+-  makeInstance(geometry, 0xaa8844,  2),
+-];
++const meshToMeshMap = new Map();
++[
++  { x:  0, boxColor: 0x44aa88, sphereColor: 0xFF4444, },
++  { x:  2, boxColor: 0x8844aa, sphereColor: 0x44FF44, },
++  { x: -2, boxColor: 0xaa8844, sphereColor: 0x4444FF, },
++].forEach((info) =&gt; {
++  const {x, boxColor, sphereColor} = info;
++  const sphere = makeInstance(sphereGeometry, sphereColor, x);
++  const box = makeInstance(boxGeometry, boxColor, x);
++  // cacher la sphère
++  sphere.visible = false;
++  // mapper la sphère à la boîte
++  meshToMeshMap.set(box, sphere);
++  // mapper la boîte à la sphère
++  meshToMeshMap.set(sphere, box);
++});
+</pre>
+<p>Dans <code class="notranslate" translate="no">render</code>, où nous faisons tourner les cubes, nous devons itérer sur <code class="notranslate" translate="no">meshToMeshMap</code> au lieu de <code class="notranslate" translate="no">cubes</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-cubes.forEach((cube, ndx) =&gt; {
++let ndx = 0;
++for (const mesh of meshToMeshMap.keys()) {
+  const speed = 1 + ndx * .1;
+  const rot = time * speed;
+-  cube.rotation.x = rot;
+-  cube.rotation.y = rot;
+-});
++  mesh.rotation.x = rot;
++  mesh.rotation.y = rot;
++  ++ndx;
++}
+</pre>
+<p>Et maintenant, nous pouvons utiliser notre nouvelle implémentation de <code class="notranslate" translate="no">PickHelper</code> pour sélectionner l'un des objets. Lorsqu'il est sélectionné, nous cachons cet objet et révélons son partenaire.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// 0, 0 est le centre de la vue en coordonnées normalisées.
+-pickHelper.pick({x: 0, y: 0}, scene, camera, time);
++const selectedObject = pickHelper.pick({x: 0, y: 0}, scene, camera, time);
++if (selectedObject) {
++  selectedObject.visible = false;
++  const partnerObject = meshToMeshMap.get(selectedObject);
++  partnerObject.visible = true;
++}
+</pre>
+<p>Et avec cela, nous devrions avoir une implémentation assez correcte de la <em>sélection par le regard</em>.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-look-to-select-w-cursor.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-look-to-select-w-cursor.html" target="_blank">cliquer ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>J'espère que cet exemple vous a donné quelques idées sur la façon d'implémenter une interface utilisateur de type "sélection par le regard" au niveau de Google Cardboard. Faire glisser des textures en utilisant les décalages des coordonnées de texture est également une technique couramment utile.</p>
+<p>Ensuite, <a href="webxr-point-to-select.html">permettons à l'utilisateur disposant d'un contrôleur VR de pointer et de déplacer des objets</a>.</p>
 
         </div>
       </div>

+ 310 - 5
manual/fr/webxr-point-to-select.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html><html lang="fr"><head>
     <meta charset="utf-8">
-    <title>VR - 3DOF Point to Select</title>
+    <title>VR - Sélection par Pointage 3DOF</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <meta name="twitter:card" content="summary_large_image">
     <meta name="twitter:site" content="@threejs">
-    <meta name="twitter:title" content="Three.js – VR - 3DOF Point to Select">
+    <meta name="twitter:title" content="Three.js – VR - Sélection par Pointage 3DOF">
     <meta property="og:image" content="https://threejs.org/files/share.png">
     <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
     <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
@@ -22,12 +22,317 @@
   <body>
     <div class="container">
       <div class="lesson-title">
-        <h1>VR - 3DOF Point to Select</h1>
+        <h1>VR - Sélection par Pointage 3DOF</h1>
       </div>
       <div class="lesson">
         <div class="lesson-main">
-          <p>Désolé, cet article n'a pas encore été traduit. <a href="https://github.com/mrdoob/three.js">Les traductions sont le bienvenue</a>! 😄</p>
-<p><a href="/manual/en/webxr-point-to-select.html">Voici l'article anglais originel pour le moment</a>.</p>
+          <p><strong>NOTE : Les exemples sur cette page nécessitent un appareil compatible VR avec un dispositif de pointage. Sans cela, ils ne fonctionneront pas. Voir <a href="webxr.html">cet article</a> pour comprendre pourquoi.</strong></p>
+<p>Dans l'<a href="webxr-look-to-select.html">article précédent</a>, nous avons examiné un exemple VR très simple où l'utilisateur pouvait choisir des éléments en pointant via le regard. Dans cet article, nous irons un peu plus loin et laisserons l'utilisateur choisir avec un dispositif de pointage. </p>
+<p>Three.js rend les choses relativement faciles en fournissant 2 objets contrôleurs en VR et essaie de gérer les deux cas : un seul contrôleur 3DOF et deux contrôleurs 6DOF. Chacun des contrôleurs est un objet <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> qui donne l'orientation et la position de ce contrôleur. Ils fournissent également les événements <code class="notranslate" translate="no">selectstart</code>, <code class="notranslate" translate="no">select</code> et <code class="notranslate" translate="no">selectend</code> lorsque l'utilisateur commence à appuyer, appuie, et cesse d'appuyer (termine) sur le bouton "principal" du contrôleur.</p>
+<p>En partant du dernier exemple de l'<a href="webxr-look-to-select.html">article précédent</a>, changeons le <code class="notranslate" translate="no">PickHelper</code> en un <code class="notranslate" translate="no">ControllerPickHelper</code>.</p>
+<p>Notre nouvelle implémentation émettra un événement <code class="notranslate" translate="no">select</code> qui nous donnera l'objet qui a été sélectionné, donc pour l'utiliser, nous aurons juste besoin de faire ceci.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickHelper = new ControllerPickHelper(scene);
+pickHelper.addEventListener('select', (event) =&gt; {
+  event.selectedObject.visible = false;
+  const partnerObject = meshToMeshMap.get(event.selectedObject);
+  partnerObject.visible = true;
+});
+</pre>
+<p>Rappelez-vous de notre code précédent : <code class="notranslate" translate="no">meshToMeshMap</code> mappe nos boîtes et sphères les unes aux autres, donc si nous en avons une, nous pouvons trouver son partenaire via <code class="notranslate" translate="no">meshToMeshMap</code>. Ici, nous cachons simplement l'objet sélectionné et rendons son partenaire visible.</p>
+<p>Quant à l'implémentation réelle de <code class="notranslate" translate="no">ControllerPickHelper</code>, nous devons d'abord ajouter les objets contrôleurs VR à la scène et y ajouter des lignes 3D que nous pouvons utiliser pour afficher où l'utilisateur pointe. Nous sauvegardons à la fois les contrôleurs et leurs lignes.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
+  constructor(scene) {
+    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
+      new THREE.Vector3(0, 0, 0),
+      new THREE.Vector3(0, 0, -1),
+    ]);
+
+    this.controllers = [];
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
+      scene.add(controller);
+
+      const line = new THREE.Line(pointerGeometry);
+      line.scale.z = 5;
+      controller.add(line);
+      this.controllers.push({controller, line});
+    }
+  }
+}
+</pre>
+<p>Sans rien faire d'autre, cela seul nous donnerait 1 ou 2 lignes dans la scène montrant où se trouvent les dispositifs de pointage de l'utilisateur et dans quelle direction ils pointent.</p>
+<p>Cependant, nous avons un problème : nous ne voulons pas que notre <code class="notranslate" translate="no">RayCaster</code> sélectionne la ligne elle-même. Une solution facile est de séparer les objets que nous voulions pouvoir sélectionner des objets que nous ne voulons pas en les plaçant sous un autre <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
++// objet pour placer les objets sélectionnables afin de pouvoir les
++// séparer facilement des objets non sélectionnables
++const pickRoot = new THREE.Object3D();
++scene.add(pickRoot);
+
+...
+
+function makeInstance(geometry, color, x) {
+  const material = new THREE.MeshPhongMaterial({color});
+
+  const cube = new THREE.Mesh(geometry, material);
+-  scene.add(cube);
++  pickRoot.add(cube);
+
+...
+</pre>
+<p>Ajoutons ensuite du code pour sélectionner à partir des contrôleurs. C'est la première fois que nous sélectionnons avec autre chose que la caméra. Dans notre <a href="picking.html">article sur la sélection</a>, l'utilisateur utilise la souris ou le doigt pour sélectionner, ce qui signifie que la sélection provient de la caméra vers l'écran. Dans <a href="webxr-look-to-select.html">l'article précédent</a>, nous sélectionnions en fonction de la direction dans laquelle l'utilisateur regardait, donc cela venait aussi de la caméra. Cette fois, cependant, nous sélectionnons à partir de la position des contrôleurs, donc nous n'utilisons pas la caméra.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
+  constructor(scene) {
++    this.raycaster = new THREE.Raycaster();
++    this.objectToColorMap = new Map();
++    this.controllerToObjectMap = new Map();
++    this.tempMatrix = new THREE.Matrix4();
+
+    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
+      new THREE.Vector3(0, 0, 0),
+      new THREE.Vector3(0, 0, -1),
+    ]);
+
+    this.controllers = [];
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
+      scene.add(controller);
+
+      const line = new THREE.Line(pointerGeometry);
+      line.scale.z = 5;
+      controller.add(line);
+      this.controllers.push({controller, line});
+    }
++  update(pickablesParent, time) {
++    this.reset();
++    for (const {controller, line} of this.controllers) {
++      // lancer un rayon depuis le contrôleur
++      this.tempMatrix.identity().extractRotation(controller.matrixWorld);
++      this.raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
++      this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(this.tempMatrix);
++      // obtenir la liste des objets intersectés par le rayon
++      const intersections = this.raycaster.intersectObjects(pickablesParent.children);
++      if (intersections.length) {
++        const intersection = intersections[0];
++        // faire en sorte que la ligne touche l'objet
++        line.scale.z = intersection.distance;
++        // sélectionner le premier objet. C'est le plus proche
++        const pickedObject = intersection.object;
++        // sauvegarder quel objet ce contrôleur a sélectionné
++        this.controllerToObjectMap.set(controller, pickedObject);
++        // mettre en évidence l'objet si ce n'est pas déjà fait
++        if (this.objectToColorMap.get(pickedObject) === undefined) {
++          // sauvegarder sa couleur
++          this.objectToColorMap.set(pickedObject, pickedObject.material.emissive.getHex());
++          // définir sa couleur émissive en rouge/jaune clignotant
++          pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFF2000 : 0xFF0000);
++        }
++      } else {
++        line.scale.z = 5;
++      }
++    }
++  }
+}
+</pre>
+<p>Comme précédemment, nous utilisons un <a href="/docs/#api/en/core/Raycaster"><code class="notranslate" translate="no">Raycaster</code></a>, mais cette fois, nous prenons le rayon depuis le contrôleur. Dans notre précédent <code class="notranslate" translate="no">PickHelper</code>, il n'y avait qu'une seule chose pour la sélection, mais ici nous avons jusqu'à 2 contrôleurs, un pour chaque main. Nous sauvegardons l'objet que chaque contrôleur regarde dans <code class="notranslate" translate="no">controllerToObjectMap</code>. Nous sauvegardons également la couleur émissive d'origine dans <code class="notranslate" translate="no">objectToColorMap</code> et nous faisons en sorte que la ligne soit assez longue pour toucher ce vers quoi elle pointe.</p>
+<p>Nous devons ajouter du code pour réinitialiser ces paramètres à chaque image.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
+
+  ...
+
++  _reset() {
++    // restaurer les couleurs
++    this.objectToColorMap.forEach((color, object) =&gt; {
++      object.material.emissive.setHex(color);
++    });
++    this.objectToColorMap.clear();
++    this.controllerToObjectMap.clear();
++  }
+  update(pickablesParent, time) {
++    this._reset();
+
+    ...
+
+}
+</pre>
+<p>Ensuite, nous voulons émettre un événement <code class="notranslate" translate="no">select</code> lorsque l'utilisateur clique sur le contrôleur. Pour ce faire, nous pouvons étendre l'<a href="/docs/#api/en/core/EventDispatcher"><code class="notranslate" translate="no">EventDispatcher</code></a> de three.js, puis nous vérifierons quand nous recevons un événement <code class="notranslate" translate="no">select</code> du contrôleur. Si ce contrôleur pointe vers quelque chose, nous émettrons ce vers quoi le contrôleur pointe comme notre propre événement <code class="notranslate" translate="no">select</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-class ControllerPickHelper {
++class ControllerPickHelper extends THREE.EventDispatcher {
+  constructor(scene) {
++    super();
+    this.raycaster = new THREE.Raycaster();
+-    this.objectToColorMap = new Map();  // object to save color and picked object
++    this.objectToColorMap = new Map();  // objet pour sauvegarder la couleur et l'objet sélectionné
+    this.controllerToObjectMap = new Map();
+    this.tempMatrix = new THREE.Matrix4();
+
+    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
+      new THREE.Vector3(0, 0, 0),
+      new THREE.Vector3(0, 0, -1),
+    ]);
+
+    this.controllers = [];
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
++      controller.addEventListener('select', (event) =&gt; {
++        const controller = event.target;
++        const selectedObject = this.controllerToObjectMap.get(controller);
++        if (selectedObject) {
++          this.dispatchEvent({type: 'select', controller, selectedObject});
++        }
++      });
+      scene.add(controller);
+
+      const line = new THREE.Line(pointerGeometry);
+      line.scale.z = 5;
+      controller.add(line);
+      this.controllers.push({controller, line});
+    }
+  }
+}
+</pre>
+<p>Il ne reste plus qu'à appeler <code class="notranslate" translate="no">update</code> dans notre boucle de rendu.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
+
+  ...
+
++  pickHelper.update(pickablesParent, time);
+
+  renderer.render(scene, camera);
+}
+</pre>
+<p>et en supposant que vous ayez un appareil VR avec un contrôleur, vous devriez pouvoir utiliser les contrôleurs pour sélectionner des éléments.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-point-to-select.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-point-to-select.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Et si nous voulions pouvoir déplacer les objets ?</p>
+<p>C'est relativement facile. Déplaçons notre code d'écouteur 'select' du contrôleur dans une fonction afin de pouvoir l'utiliser pour plus d'une chose.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
+  constructor(scene) {
+    super();
+
+    ...
+
+    this.controllers = [];
+
++    const selectListener = (event) =&gt; {
++      const controller = event.target;
++      const selectedObject = this.controllerToObjectMap.get(event.target);
++      if (selectedObject) {
++        this.dispatchEvent({type: 'select', controller, selectedObject});
++      }
++    };
+
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
+-      controller.addEventListener('select', (event) =&gt; {
+-        const controller = event.target;
+-        const selectedObject = this.controllerToObjectMap.get(event.target);
+-        if (selectedObject) {
+-          this.dispatchEvent({type: 'select', controller, selectedObject});
+-        }
+-      });
++      controller.addEventListener('select', selectListener);
+
+       ...
+</pre>
+<p>Utilisons-le ensuite pour <code class="notranslate" translate="no">selectstart</code> et <code class="notranslate" translate="no">select</code>.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
+  constructor(scene) {
+    super();
+
+    ...
+
+    this.controllers = [];
+
+    const selectListener = (event) =&gt; {
+      const controller = event.target;
+      const selectedObject = this.controllerToObjectMap.get(event.target);
+      if (selectedObject) {
+-        this.dispatchEvent({type: 'select', controller, selectedObject});
++        this.dispatchEvent({type: event.type, controller, selectedObject});
+      }
+    };
+
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
+      controller.addEventListener('select', selectListener);
+      controller.addEventListener('selectstart', selectListener);
+
+       ...
+</pre>
+<p>et transmettons également l'événement <code class="notranslate" translate="no">selectend</code> que three.js envoie lorsque l'utilisateur relâche le bouton du contrôleur.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
+  constructor(scene) {
+    super();
+
+    ...
+
+    this.controllers = [];
+
+    const selectListener = (event) =&gt; {
+      const controller = event.target;
+      const selectedObject = this.controllerToObjectMap.get(event.target);
+      if (selectedObject) {
+        this.dispatchEvent({type: event.type, controller, selectedObject});
+      }
+    };
+
++    const endListener = (event) =&gt; {
++      const controller = event.target;
++      this.dispatchEvent({type: event.type, controller});
++    };
+
+    for (let i = 0; i &lt; 2; ++i) {
+      const controller = renderer.xr.getController(i);
+      controller.addEventListener('select', selectListener);
+      controller.addEventListener('selectstart', selectListener);
++      controller.addEventListener('selectend', endListener);
+
+       ...
+</pre>
+<p>Maintenant, modifions le code de manière à ce que, lorsque nous recevons un événement <code class="notranslate" translate="no">selectstart</code>, nous retirions l'objet sélectionné de la scène et en fassions un enfant du contrôleur. Cela signifie qu'il se déplacera avec le contrôleur. Lorsque nous recevrons un événement <code class="notranslate" translate="no">selectend</code>, nous le remettrons dans la scène.</p>
+<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickHelper = new ControllerPickHelper(scene);
+-pickHelper.addEventListener('select', (event) =&gt; {
+-  event.selectedObject.visible = false;
+-  const partnerObject = meshToMeshMap.get(event.selectedObject);
+-  partnerObject.visible = true;
+-});
+
++const controllerToSelection = new Map();
++pickHelper.addEventListener('selectstart', (event) =&gt; {
++  const {controller, selectedObject} = event;
++  const existingSelection = controllerToSelection.get(controller);
++  if (!existingSelection) {
++    controllerToSelection.set(controller, {
++      object: selectedObject,
++      parent: selectedObject.parent,
++    });
++    controller.attach(selectedObject);
++  }
++});
++
++pickHelper.addEventListener('selectend', (event) =&gt; {
++  const {controller} = event;
++  const selection = controllerToSelection.get(controller);
++  if (selection) {
++    controllerToSelection.delete(controller);
++    selection.parent.attach(selection.object);
++  }
++});
+</pre>
+<p>Lorsqu'un objet est sélectionné, nous sauvegardons cet objet et son parent d'origine. Lorsque l'utilisateur a terminé, nous pouvons remettre l'objet en place.</p>
+<p>Nous utilisons <a href="/docs/#api/en/core/Object3D.attach"><code class="notranslate" translate="no">Object3D.attach</code></a> pour changer le parent des objets sélectionnés. Ces fonctions nous permettent de modifier le parent d'un objet sans modifier son orientation et sa position dans la scène. </p>
+<p>Et avec cela, nous devrions pouvoir déplacer les objets avec un contrôleur 6DOF ou au moins changer leur orientation avec un contrôleur 3DOF.</p>
+<p></p><div translate="no" class="threejs_example_container notranslate">
+  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-point-to-select-w-move.html"></iframe></div>
+  <a class="threejs_center" href="/manual/examples/webxr-point-to-select-w-move.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
+</div>
+
+<p></p>
+<p>Pour être honnête, je ne suis pas sûr à 100 % que ce <code class="notranslate" translate="no">ControllerPickHelper</code> soit la meilleure façon d'organiser le code, mais il est utile pour démontrer les différentes parties nécessaires pour faire fonctionner quelque chose de simple en VR avec three.js.</p>
 
         </div>
       </div>

粤ICP备19079148号