shadows.html 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <!DOCTYPE html><html lang="fr"><head>
  2. <meta charset="utf-8">
  3. <title>Ombres</title>
  4. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  5. <meta name="twitter:card" content="summary_large_image">
  6. <meta name="twitter:site" content="@threejs">
  7. <meta name="twitter:title" content="Three.js – Ombres">
  8. <meta property="og:image" content="https://threejs.org/files/share.png">
  9. <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
  10. <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
  11. <link rel="stylesheet" href="../resources/lesson.css">
  12. <link rel="stylesheet" href="../resources/lang.css">
  13. <script type="importmap">
  14. {
  15. "imports": {
  16. "three": "../../build/three.module.js"
  17. }
  18. }
  19. </script>
  20. </head>
  21. <body>
  22. <div class="container">
  23. <div class="lesson-title">
  24. <h1>Ombres</h1>
  25. </div>
  26. <div class="lesson">
  27. <div class="lesson-main">
  28. <p>Cet article fait partie d'une série d'articles sur three.js. Le
  29. premier article est <a href="fundamentals.html">les fondamentaux de three.js</a>. Si
  30. vous ne l'avez pas encore lu et que vous débutez avec three.js, vous pourriez envisager de
  31. commencer par là. L'<a href="cameras.html">article précédent portait sur les caméras</a>, ce qui est
  32. important à lire avant de lire cet article, tout comme
  33. l'<a href="lights.html">article précédent sur les lumières</a>.</p>
  34. <p>Les ombres sur ordinateur peuvent être un sujet complexe. Il existe diverses
  35. solutions et toutes impliquent des compromis, y compris les solutions
  36. disponibles dans three.js.</p>
  37. <p>Three.js utilise par défaut des <em>cartes d'ombres</em>. Le fonctionnement d'une carte d'ombres
  38. est le suivant : <em>pour chaque lumière qui projette des ombres, tous les objets marqués pour projeter
  39. des ombres sont rendus du point de vue de la lumière</em>. **LISEZ CELA
  40. À NOUVEAU !** et laissez-le s'imprégner.</p>
  41. <p>En d'autres termes, si vous avez 20 objets et 5 lumières, et que
  42. les 20 objets projettent des ombres et les 5 lumières projettent
  43. des ombres, alors toute votre scène sera dessinée 6 fois. Les 20 objets
  44. seront dessinés pour la lumière n°1, puis les 20 objets seront dessinés pour
  45. la lumière n°2, puis n°3, etc., et enfin la scène réelle sera dessinée
  46. en utilisant les données des 5 premiers rendus.</p>
  47. <p>Pire encore, si vous avez une lumière ponctuelle (point light) qui projette des ombres, la scène
  48. doit être dessinée 6 fois juste pour cette lumière !</p>
  49. <p>Pour ces raisons, il est courant de trouver d'autres solutions plutôt que d'avoir
  50. un tas de lumières générant toutes des ombres. Une solution courante
  51. consiste à avoir plusieurs lumières mais seulement une lumière directionnelle (directional light) générant
  52. des ombres.</p>
  53. <p>Une autre solution consiste à utiliser des lightmaps (cartes d'éclairage) et/ou des ambient occlusion maps
  54. (cartes d'occlusion ambiante) pour pré-calculer les effets d'éclairage hors ligne. Cela se traduit par un éclairage statique
  55. ou des indices d'éclairage statique, mais au moins c'est rapide. Nous
  56. aborderons ces deux points dans un autre article.</p>
  57. <p>Une autre solution consiste à utiliser de fausses ombres. Créez un plan, placez une texture en niveaux de gris
  58. sur le plan qui approxime une ombre,
  59. dessinez-le au-dessus du sol, sous votre objet.</p>
  60. <p>Par exemple, utilisons cette texture comme fausse ombre</p>
  61. <div class="threejs_center"><img src="../examples/resources/images/roundshadow.png"></div>
  62. <p>Nous utiliserons une partie du code de l'<a href="cameras.html">article précédent</a>.</p>
  63. <p>Définissons la couleur de fond sur blanc.</p>
  64. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
  65. +scene.background = new THREE.Color('white');
  66. </pre>
  67. <p>Ensuite, nous allons configurer le même sol en damier, mais cette fois-ci en utilisant
  68. 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>
  69. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
  70. {
  71. const planeSize = 40;
  72. - const loader = new THREE.TextureLoader();
  73. const texture = loader.load('resources/images/checker.png');
  74. texture.wrapS = THREE.RepeatWrapping;
  75. texture.wrapT = THREE.RepeatWrapping;
  76. texture.magFilter = THREE.NearestFilter;
  77. const repeats = planeSize / 2;
  78. texture.repeat.set(repeats, repeats);
  79. const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  80. const planeMat = new THREE.MeshBasicMaterial({
  81. map: texture,
  82. side: THREE.DoubleSide,
  83. });
  84. + planeMat.color.setRGB(1.5, 1.5, 1.5);
  85. const mesh = new THREE.Mesh(planeGeo, planeMat);
  86. mesh.rotation.x = Math.PI * -.5;
  87. scene.add(mesh);
  88. }
  89. </pre>
  90. <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
  91. de la texture en damier par 1.5, 1.5, 1.5. Étant donné que les couleurs de la texture sont 0x808080 et 0xC0C0C0,
  92. ce qui correspond à un gris moyen et un gris clair, les multiplier par 1.5 nous donnera un damier
  93. blanc et gris clair.</p>
  94. <p>Chargeons la texture d'ombre</p>
  95. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const shadowTexture = loader.load('resources/images/roundshadow.png');
  96. </pre>
  97. <p>et créons un tableau pour mémoriser chaque sphère et les objets associés.</p>
  98. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereShadowBases = [];
  99. </pre>
  100. <p>Ensuite, nous allons créer une géométrie de sphère</p>
  101. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereRadius = 1;
  102. const sphereWidthDivisions = 32;
  103. const sphereHeightDivisions = 16;
  104. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  105. </pre>
  106. <p>Et une géométrie de plan pour la fausse ombre</p>
  107. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeSize = 1;
  108. const shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  109. </pre>
  110. <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>
  111. <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.
  112. 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.
  113. Nous définissons également <code class="notranslate" translate="no">depthWrite</code> à false afin que les ombres ne s'entremêlent pas.
  114. Nous aborderons ces deux problèmes dans <a href="transparency.html">un autre article</a>.
  115. 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>
  116. <p>Nous donnons à chaque sphère une teinte différente, puis nous enregistrons la base, le maillage de la sphère,
  117. le maillage de l'ombre et la position y initiale de chaque sphère.</p>
  118. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const numSpheres = 15;
  119. for (let i = 0; i &lt; numSpheres; ++i) {
  120. // créer une base pour l'ombre et la sphère
  121. // afin qu'elles se déplacent ensemble.
  122. const base = new THREE.Object3D();
  123. scene.add(base);
  124. // ajouter l'ombre à la base
  125. // note : nous créons un nouveau matériau pour chaque sphère
  126. // afin de pouvoir définir la transparence du matériau de cette sphère
  127. // séparément.
  128. const shadowMat = new THREE.MeshBasicMaterial({
  129. map: shadowTexture,
  130. transparent: true, // pour que nous puissions voir le sol
  131. depthWrite: false, // pour ne pas avoir à trier
  132. });
  133. const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
  134. shadowMesh.position.y = 0.001; // pour être légèrement au-dessus du sol
  135. shadowMesh.rotation.x = Math.PI * -.5;
  136. const shadowSize = sphereRadius * 4;
  137. shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
  138. base.add(shadowMesh);
  139. // ajouter la sphère à la base
  140. const u = i / numSpheres; // va de 0 à 1 au fur et à mesure que nous parcourons les sphères.
  141. const sphereMat = new THREE.MeshPhongMaterial();
  142. sphereMat.color.setHSL(u, 1, .75);
  143. const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
  144. sphereMesh.position.set(0, sphereRadius + 2, 0);
  145. base.add(sphereMesh);
  146. // mémoriser les 3 plus la position y
  147. sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
  148. }
  149. </pre>
  150. <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
  151. éclaircir les choses.</p>
  152. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  153. const skyColor = 0xB1E1FF; // bleu clair
  154. const groundColor = 0xB97A20; // orange brunâtre
  155. const intensity = 2;
  156. const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
  157. scene.add(light);
  158. }
  159. </pre>
  160. <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>
  161. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  162. const color = 0xFFFFFF;
  163. const intensity = 1;
  164. const light = new THREE.DirectionalLight(color, intensity);
  165. light.position.set(0, 10, 5);
  166. light.target.position.set(-5, 0, 0);
  167. scene.add(light);
  168. scene.add(light.target);
  169. }
  170. </pre>
  171. <p>Cela rendrait tel quel, mais animons ces sphères.
  172. Pour chaque ensemble sphère, ombre, base, nous déplaçons la base dans le plan xz, nous
  173. 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>
  174. ce qui nous donne une animation rebondissante. Et nous définissons également l'opacité du matériau de l'ombre
  175. afin que, à mesure que chaque sphère monte, son ombre s'estompe.</p>
  176. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  177. time *= 0.001; // convertir en secondes
  178. ...
  179. sphereShadowBases.forEach((sphereShadowBase, ndx) =&gt; {
  180. const {base, sphereMesh, shadowMesh, y} = sphereShadowBase;
  181. // u est une valeur qui va de 0 à 1 au fur et à mesure que nous parcourons les sphères
  182. const u = ndx / sphereShadowBases.length;
  183. // calculer une position pour la base. Cela déplacera
  184. // à la fois la sphère et son ombre
  185. const speed = time * .2;
  186. const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
  187. const radius = Math.sin(speed - ndx) * 10;
  188. base.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
  189. // yOff est une valeur qui va de 0 à 1
  190. const yOff = Math.abs(Math.sin(time * 2 + ndx));
  191. // déplacer la sphère de haut en bas
  192. sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);
  193. // estomper l'ombre à mesure que la sphère monte
  194. shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
  195. });
  196. ...
  197. </pre>
  198. <p>Et voici 15 sortes de balles rebondissantes.</p>
  199. <p></p><div translate="no" class="threejs_example_container notranslate">
  200. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-fake.html"></iframe></div>
  201. <a class="threejs_center" href="/manual/examples/shadows-fake.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  202. </div>
  203. <p></p>
  204. <p>Dans certaines applications, il est courant d'utiliser une ombre ronde ou ovale pour tout, mais
  205. bien sûr, vous pourriez aussi utiliser des textures d'ombre de formes différentes. Vous pourriez également
  206. donner à l'ombre un bord plus net. Un bon exemple de l'utilisation de ce type
  207. d'ombre est <a href="https://www.google.com/search?tbm=isch&amp;q=animal+crossing+pocket+camp+screenshots">Animal Crossing Pocket Camp</a>
  208. où vous pouvez voir que chaque personnage a une simple ombre ronde. C'est efficace et peu coûteux.
  209. <a href="https://www.google.com/search?q=monument+valley+screenshots&amp;tbm=isch">Monument Valley</a>
  210. semble également utiliser ce type d'ombre pour le personnage principal.</p>
  211. <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>,
  212. 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>
  213. <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>
  214. <p>La première chose à faire est d'activer les ombres dans le rendu (renderer).</p>
  215. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  216. +renderer.shadowMap.enabled = true;
  217. </pre>
  218. <p>Ensuite, nous devons également dire à la lumière de projeter une ombre</p>
  219. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const light = new THREE.DirectionalLight(color, intensity);
  220. +light.castShadow = true;
  221. </pre>
  222. <p>Nous devons également parcourir chaque maillage dans la scène et décider s'il doit
  223. à la fois projeter des ombres et/ou recevoir des ombres.</p>
  224. <p>Faisons en sorte que le plan (le sol) reçoive uniquement les ombres, car nous ne
  225. nous soucions pas vraiment de ce qui se passe en dessous.</p>
  226. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(planeGeo, planeMat);
  227. mesh.receiveShadow = true;
  228. </pre>
  229. <p>Pour le cube et la sphère, faisons en sorte qu'ils reçoivent et projettent tous deux des ombres</p>
  230. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  231. mesh.castShadow = true;
  232. mesh.receiveShadow = true;
  233. ...
  234. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  235. mesh.castShadow = true;
  236. mesh.receiveShadow = true;
  237. </pre>
  238. <p>Et ensuite, nous l'exécutons.</p>
  239. <p></p><div translate="no" class="threejs_example_container notranslate">
  240. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-directional-light.html"></iframe></div>
  241. <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>
  242. </div>
  243. <p></p>
  244. <p>Que s'est-il passé ? Pourquoi des parties des ombres sont-elles manquantes ?</p>
  245. <p>La raison est que les cartes d'ombres sont créées en rendant la scène du point
  246. 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>
  247. qui regarde sa cible. Tout comme les <a href="cameras.html">caméras que nous avons précédemment couvertes</a>,
  248. la caméra d'ombre de la lumière définit une zone à l'intérieur de laquelle
  249. les ombres sont rendues. Dans l'exemple ci-dessus, cette zone est trop petite.</p>
  250. <p>Afin de visualiser cette zone, nous pouvons obtenir la caméra d'ombre de la lumière et ajouter
  251. un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> à la scène.</p>
  252. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
  253. scene.add(cameraHelper);
  254. </pre>
  255. <p>Et maintenant, vous pouvez voir la zone pour laquelle les ombres sont projetées et reçues.</p>
  256. <p></p><div translate="no" class="threejs_example_container notranslate">
  257. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-directional-light-with-camera-helper.html"></iframe></div>
  258. <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>
  259. </div>
  260. <p></p>
  261. <p>Ajustez la valeur x de la cible d'avant en arrière et il devrait être assez clair que seules
  262. les ombres sont dessinées dans la zone de la caméra d'ombre de la lumière.</p>
  263. <p>Nous pouvons ajuster la taille de cette boîte en ajustant la caméra d'ombre de la lumière.</p>
  264. <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
  265. <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
  266. <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.
  267. 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>
  268. <p>Rappelons qu'une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> définit
  269. 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>,
  270. et <code class="notranslate" translate="no">zoom</code>.</p>
  271. <p>À nouveau, créons une classe d'aide pour lil-gui. Nous créerons une <code class="notranslate" translate="no">DimensionGUIHelper</code>
  272. à laquelle nous passerons un objet et 2 propriétés. Elle présentera une propriété que lil-gui
  273. peut ajuster et, en réponse, définira les deux propriétés, l'une positive et l'autre négative.
  274. 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>
  275. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DimensionGUIHelper {
  276. constructor(obj, minProp, maxProp) {
  277. this.obj = obj;
  278. this.minProp = minProp;
  279. this.maxProp = maxProp;
  280. }
  281. get value() {
  282. return this.obj[this.maxProp] * 2;
  283. }
  284. set value(v) {
  285. this.obj[this.maxProp] = v / 2;
  286. this.obj[this.minProp] = v / -2;
  287. }
  288. }
  289. </pre>
  290. <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>
  291. pour ajuster <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>.</p>
  292. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  293. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  294. gui.add(light, 'intensity', 0, 2, 0.01);
  295. +{
  296. + const folder = gui.addFolder('Caméra d\'ombre');
  297. + folder.open();
  298. + folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)
  299. + .name('largeur')
  300. + .onChange(updateCamera);
  301. + folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)
  302. + .name('hauteur')
  303. + .onChange(updateCamera);
  304. + const minMaxGUIHelper = new MinMaxGUIHelper(light.shadow.camera, 'near', 'far', 0.1);
  305. + folder.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('proche').onChange(updateCamera);
  306. + folder.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('loin').onChange(updateCamera);
  307. + folder.add(light.shadow.camera, 'zoom', 0.01, 1.5, 0.01).onChange(updateCamera);
  308. +}
  309. </pre>
  310. <p>Nous demandons à l'interface graphique d'appeler notre fonction <code class="notranslate" translate="no">updateCamera</code> chaque fois que quelque chose change.
  311. Écrivons cette fonction pour mettre à jour la lumière, l'helper pour la lumière, la
  312. caméra d'ombre de la lumière et l'helper affichant la caméra d'ombre de la lumière.</p>
  313. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateCamera() {
  314. // mettre à jour le matrixWorld de la cible de la lumière car il est nécessaire pour l'helper
  315. light.target.updateMatrixWorld();
  316. helper.update();
  317. // mettre à jour la matrice de projection de la caméra d'ombre de la lumière
  318. light.shadow.camera.updateProjectionMatrix();
  319. // et maintenant mettre à jour l'helper caméra que nous utilisons pour afficher la caméra d'ombre de la lumière
  320. cameraHelper.update();
  321. }
  322. updateCamera();
  323. </pre>
  324. <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>
  325. <p></p><div translate="no" class="threejs_example_container notranslate">
  326. <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>
  327. <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>
  328. </div>
  329. <p></p>
  330. <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
  331. et que les zones qui doivent être dans l'ombre pour cette scène sont entièrement couvertes.</p>
  332. <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
  333. 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
  334. et vous pourriez voir quelque chose comme ceci</p>
  335. <div class="threejs_center"><img src="../resources/images/low-res-shadow-map.png" style="width: 369px"></div>
  336. <p>Qu'est-ce qui se passe avec ces ombres basse résolution ?!</p>
  337. <p>Ce problème est un autre paramètre lié aux ombres dont il faut être conscient.
  338. Les cartes d'ombres sont des textures dans lesquelles les ombres sont dessinées.
  339. Ces textures ont une taille. La zone de la caméra d'ombre que nous avons définie ci-dessus est étirée
  340. sur cette taille. Cela signifie que plus la zone que vous définissez est grande, plus vos ombres seront
  341. pixelisées.</p>
  342. <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>
  343. et <code class="notranslate" translate="no">light.shadow.mapSize.height</code>. Par défaut, elles sont de 512x512.
  344. Plus vous les augmentez, plus elles consomment de mémoire et plus elles sont lentes à calculer, vous voulez donc
  345. les définir aussi petites que possible tout en faisant fonctionner votre scène. Il en va de même pour la
  346. 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
  347. continuez à couvrir votre scène. Sachez que la machine de chaque utilisateur a une taille de texture maximale
  348. 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>
  349. <!--
  350. 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
  351. -->
  352. <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
  353. 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
  354. 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
  355. 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>.
  356. L'<code class="notranslate" translate="no">aspect</code> est défini automatiquement en fonction de la taille de la carte d'ombre.</p>
  357. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.DirectionalLight(color, intensity);
  358. +const light = new THREE.SpotLight(color, intensity);
  359. </pre>
  360. <p>et nous avons rajouté les paramètres <code class="notranslate" translate="no">penumbra</code> et <code class="notranslate" translate="no">angle</code>
  361. de notre <a href="lights.html">article sur les lumières</a>.</p>
  362. <p></p><div translate="no" class="threejs_example_container notranslate">
  363. <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>
  364. <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>
  365. </div>
  366. <p></p>
  367. <!--
  368. Vous pouvez remarquer, tout comme dans le dernier exemple, si nous définissons l'angle élevé
  369. alors la carte d'ombre, la texture est étendue sur une très grande zone et
  370. la résolution de nos ombres devient très faible.
  371. div class="threejs_center"><img src="../resources/images/low-res-shadow-map-spotlight.png" style="width: 344px"></div>
  372. Vous pouvez augmenter la taille de la carte d'ombre comme mentionné ci-dessus. Vous pouvez
  373. également flouter le résultat
  374. <div translate="no" class="threejs_example_container notranslate">
  375. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-spot-light-with-shadow-radius"></iframe></div>
  376. <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>
  377. </div>
  378. -->
  379. <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>
  380. 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>.
  381. 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>
  382. , chacune pointant vers une face d'un cube autour de la lumière. Cela signifie
  383. 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
  384. dessinée 6 fois, une pour chaque direction.</p>
  385. <p>Mettons une boîte autour de notre scène pour que nous puissions voir les ombres sur les murs
  386. 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>
  387. afin de rendre l'intérieur de la boîte au lieu de l'extérieur. Comme le sol,
  388. nous la définirons pour qu'elle ne reçoive que les ombres. De plus, nous définirons la position de la
  389. boîte de manière à ce que son bas soit légèrement en dessous du sol afin que le sol et le bas
  390. de la boîte ne causent pas de z-fighting.</p>
  391. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  392. const cubeSize = 30;
  393. const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
  394. const cubeMat = new THREE.MeshPhongMaterial({
  395. color: '#CCC',
  396. side: THREE.BackSide,
  397. });
  398. const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  399. mesh.receiveShadow = true;
  400. mesh.position.set(0, cubeSize / 2 - 0.1, 0);
  401. scene.add(mesh);
  402. }
  403. </pre>
  404. <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>
  405. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.SpotLight(color, intensity);
  406. +const light = new THREE.PointLight(color, intensity);
  407. ....
  408. // pour pouvoir facilement voir où se trouve la lumière ponctuelle
  409. +const helper = new THREE.PointLightHelper(light);
  410. +scene.add(helper);
  411. </pre>
  412. <p></p><div translate="no" class="threejs_example_container notranslate">
  413. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/shadows-point-light.html"></iframe></div>
  414. <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>
  415. </div>
  416. <p></p>
  417. <p>Utilisez les paramètres d'interface graphique <code class="notranslate" translate="no">position</code> pour déplacer la lumière
  418. et vous verrez les ombres tomber sur tous les murs. Vous pouvez
  419. également ajuster les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> et voir, tout comme
  420. pour les autres ombres, que lorsque les objets sont plus proches que <code class="notranslate" translate="no">near</code>, ils
  421. 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>
  422. <!-- auto-ombre, acné d'ombre -->
  423. </div>
  424. </div>
  425. </div>
  426. <script src="../resources/prettify.js"></script>
  427. <script src="../resources/lesson.js"></script>
  428. </body></html>
粤ICP备19079148号