You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

464 lines
31 KiB
HTML

<!DOCTYPE html><html lang="fr"><head>
<meta charset="utf-8">
<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 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)">
<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>Ombres</h1>
</div>
<div class="lesson">
<div class="lesson-main">
<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>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-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();
{
const planeSize = 40;
- 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;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
});
+ planeMat.color.setRGB(1.5, 1.5, 1.5);
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
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 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, 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 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 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
// afin qu'elles se déplacent ensemble.
const base = new THREE.Object3D();
scene.add(base);
// 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, // pour ne pas avoir à trier
});
const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
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; // 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);
// mémoriser les 3 plus la position y
sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
}
</pre>
<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 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 obtiennent une certaine définition</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(0, 10, 5);
light.target.position.set(-5, 0, 0);
scene.add(light);
scene.add(light.target);
}
</pre>
<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
...
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 nous parcourons les sphères
const u = ndx / sphereShadowBases.length;
// 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 qui va de 0 à 1
const yOff = Math.abs(Math.sin(time * 2 + ndx));
// déplacer la sphère de haut en bas
sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);
// estomper l'ombre à mesure que la sphère monte
shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
});
...
</pre>
<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">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 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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const light = new THREE.DirectionalLight(color, intensity);
+light.castShadow = true;
</pre>
<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 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;
...
const mesh = new THREE.Mesh(sphereGeo, sphereMat);
mesh.castShadow = true;
mesh.receiveShadow = true;
</pre>
<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/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 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>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/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 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 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;
this.minProp = minProp;
this.maxProp = maxProp;
}
get value() {
return this.obj[this.maxProp] * 2;
}
set value(v) {
this.obj[this.maxProp] = v / 2;
this.obj[this.minProp] = v / -2;
}
}
</pre>
<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('Caméra d\'ombre');
+ folder.open();
+ folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)
+ .name('largeur')
+ .onChange(updateCamera);
+ folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)
+ .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('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>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 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'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 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">cliquez ici pour ouvrir dans une fenêtre séparée</a>
</div>
<p></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>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, 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 à 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>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">cliquez ici pour ouvrir dans une fenêtre séparée</a>
</div>
<p></p>
<!--
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>
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/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="/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);
const cubeMat = new THREE.MeshPhongMaterial({
color: '#CCC',
side: THREE.BackSide,
});
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.receiveShadow = true;
mesh.position.set(0, cubeSize / 2 - 0.1, 0);
scene.add(mesh);
}
</pre>
<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);
....
// 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/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 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>
</div>
<script src="../resources/prettify.js"></script>
<script src="../resources/lesson.js"></script>
</body></html>