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

381 lines
30 KiB

2 years ago
<!DOCTYPE html><html lang="fr"><head>
<meta charset="utf-8">
<title>Les ombres dans </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 dans ">
<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="/manual/resources/lesson.css">
<link rel="stylesheet" href="/manual/resources/lang.css">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
</head>
<body>
<div class="container">
<div class="lesson-title">
<h1>Les ombres dans </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 caméras</a> est à lire ainsi que <a href="lights.html">celui à propos des lumières</a> avant d'entamer cet article-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>
<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>
<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>
<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 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>
<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>
<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>
<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 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 les combats en Z. Nous définissons également <code class="notranslate" translate="no">depthWrite</code> sur false pour que les ombres ne se gâchent pas. 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>
<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.
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.
const shadowMat = new THREE.MeshBasicMaterial({
map: shadowTexture,
transparent: true, // pour que nous puissions voir le sol
depthWrite: false, // donc nous n'avons pas à trier
});
const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
shadowMesh.position.y = 0.001; // donc nous sommes 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 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
sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
}
</pre>
<p>Nous avons installé 2 lumières. L'un est un <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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
const skyColor = 0xB1E1FF; // bleu
const groundColor = 0xB97A20; // orange brun
const intensity = 2;
const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
scene.add(light);
}
</pre>
<p>L'autre est un <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> donc les sphères ont une 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>Il rendrait tel quel mais animons les sphères. Pour chaque sphère, ombre, jeu de 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="https://threejs.org/docs/#api/en/math/Math.abs(Math.sin(time"><code class="notranslate" translate="no">Math.abs(Math.sin(time))</code></a>)) qui nous donne une animation rebondissante. Et, nous avons également défini l'opacité du matériau d'ombre de sorte qu'à 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 l'on itère les sphères
const u = ndx / sphereShadowBases.length;
// calculer une position pour la base. Cela va bouger
// à 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
const yOff = Math.abs(Math.sin(time * 2 + ndx));
// déplace 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
shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
});
...
</pre>
<p>Et voici 15 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>
</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>Donc, en passant aux cartes d'ombre, il y a 3 lumières qui peuvent projeter des ombres. Le <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>, le <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> et le <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> avec l'aide 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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const renderer = new THREE.WebGLRenderer({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 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) ne reçoive que des 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>
<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 puis 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>
<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>
<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></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>
<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>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>
<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>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>
<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');
+ folder.open();
+ folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)
+ .name('width')
+ .onChange(updateCamera);
+ folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)
+ .name('height')
+ .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(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>
<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
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
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></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>
</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>
<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 en blocs.</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, vous voulez donc 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>
<!--
Ok but what about `near` and `far` I hear you thinking. Can we set `near` to 0.00001 and far to `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-controlé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>
<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>Rajoutons 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></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>
</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.
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
<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>
-->
<p>Et enfin il y a les ombres avec un <a href="https://threejs.org/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>. Étant donné qu'un <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>Mettons un cadre autour de notre scène 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 le sol, nous ne le paramétrons 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>
<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, il faut passer 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
+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>
<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
-->
</div>
</div>
</div>
<script src="/manual/resources/prettify.js"></script>
<script src="/manual/resources/lesson.js"></script>
</body></html>