shadows.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <!DOCTYPE html><html lang="en"><head>
  2. <meta charset="utf-8">
  3. <title>Shadows</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 – Shadows">
  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="/manual/resources/lesson.css">
  12. <link rel="stylesheet" href="/manual/resources/lang.css">
  13. <!-- Import maps polyfill -->
  14. <!-- Remove this when import maps will be widely supported -->
  15. <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
  16. <script type="importmap">
  17. {
  18. "imports": {
  19. "three": "../../build/three.module.js"
  20. }
  21. }
  22. </script>
  23. </head>
  24. <body>
  25. <div class="container">
  26. <div class="lesson-title">
  27. <h1>Shadows</h1>
  28. </div>
  29. <div class="lesson">
  30. <div class="lesson-main">
  31. <p>This article is part of a series of articles about three.js. The
  32. first article is <a href="fundamentals.html">three.js fundamentals</a>. If
  33. you haven't read that yet and you're new to three.js you might want to
  34. consider starting there. The
  35. <a href="cameras.html">previous article was about cameras</a> which is
  36. important to have read before you read this article as well as
  37. the <a href="lights.html">article before that one about lights</a>.</p>
  38. <p>Shadows on computers can be a complicated topic. There are various
  39. solutions and all of them have tradeoffs including the solutions
  40. available in three.js.</p>
  41. <p>Three.js by default uses <em>shadow maps</em>. The way a shadow map works
  42. is, <em>for every light that casts shadows all objects marked to cast
  43. shadows are rendered from the point of view of the light</em>. <strong>READ THAT
  44. AGAIN!</strong> and let it sink in.</p>
  45. <p>In other words, if you have 20 objects, and 5 lights, and
  46. all 20 objects are casting shadows and all 5 lights are casting
  47. shadows then your entire scene will be drawn 6 times. All 20 objects
  48. will be drawn for light #1, then all 20 objects will be drawn for
  49. light #2, then #3, etc and finally the actual scene will be drawn
  50. using data from the first 5 renders.</p>
  51. <p>It gets worse, if you have a point light casting shadows the scene
  52. has to be drawn 6 times just for that light!</p>
  53. <p>For these reasons it's common to find other solutions than to have
  54. a bunch of lights all generating shadows. One common solution
  55. is to have multiple lights but only one directional light generating
  56. shadows.</p>
  57. <p>Yet another solution is to use lightmaps and or ambient occlusion maps
  58. to pre-compute the effects of lighting offline. This results in static
  59. lighting or static lighting hints but at least it's fast. We'll
  60. cover both of those in another article.</p>
  61. <p>Another solution is to use fake shadows. Make a plane, put a grayscale
  62. texture in the plane that approximates a shadow,
  63. draw it above the ground below your object.</p>
  64. <p>For example let's use this texture as a fake shadow</p>
  65. <div class="threejs_center"><img src="../examples/resources/images/roundshadow.png"></div>
  66. <p>We'll use some of the code from <a href="cameras.html">the previous article</a>.</p>
  67. <p>Let's set the background color to white.</p>
  68. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
  69. +scene.background = new THREE.Color('white');
  70. </pre>
  71. <p>Then we'll setup the same checkerboard ground but this time it's using
  72. a <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> as we don't need lighting for the ground.</p>
  73. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
  74. {
  75. const planeSize = 40;
  76. - const loader = new THREE.TextureLoader();
  77. const texture = loader.load('resources/images/checker.png');
  78. texture.wrapS = THREE.RepeatWrapping;
  79. texture.wrapT = THREE.RepeatWrapping;
  80. texture.magFilter = THREE.NearestFilter;
  81. const repeats = planeSize / 2;
  82. texture.repeat.set(repeats, repeats);
  83. const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  84. const planeMat = new THREE.MeshBasicMaterial({
  85. map: texture,
  86. side: THREE.DoubleSide,
  87. });
  88. + planeMat.color.setRGB(1.5, 1.5, 1.5);
  89. const mesh = new THREE.Mesh(planeGeo, planeMat);
  90. mesh.rotation.x = Math.PI * -.5;
  91. scene.add(mesh);
  92. }
  93. </pre>
  94. <p>Note we're setting the color to <code class="notranslate" translate="no">1.5, 1.5, 1.5</code>. This will multiply the checkerboard
  95. texture's colors by 1.5, 1.5, 1.5. Since the texture's colors are 0x808080 and 0xC0C0C0
  96. which is medium gray and light gray, multiplying them by 1.5 will give us a white and
  97. light grey checkerboard.</p>
  98. <p>Let's load the shadow texture</p>
  99. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const shadowTexture = loader.load('resources/images/roundshadow.png');
  100. </pre>
  101. <p>and make an array to remember each sphere and associated objects.</p>
  102. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereShadowBases = [];
  103. </pre>
  104. <p>Then we'll make a sphere geometry</p>
  105. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const sphereRadius = 1;
  106. const sphereWidthDivisions = 32;
  107. const sphereHeightDivisions = 16;
  108. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  109. </pre>
  110. <p>And a plane geometry for the fake shadow</p>
  111. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeSize = 1;
  112. const shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  113. </pre>
  114. <p>Now we'll make a bunch of spheres. For each sphere we'll create a <code class="notranslate" translate="no">base</code>
  115. <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> and we'll make both the shadow plane mesh and the sphere mesh
  116. children of the base. That way if we move the base both the sphere and the shadow
  117. will move. We need to put the shadow slightly above the ground to prevent z-fighting.
  118. We also set <code class="notranslate" translate="no">depthWrite</code> to false so that the shadows don't mess each other up.
  119. We'll go over both of these issues in <a href="transparency.html">another article</a>.
  120. The shadow is a <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> because it doesn't need lighting.</p>
  121. <p>We make each sphere a different hue and then save off the base, the sphere mesh,
  122. the shadow mesh and the initial y position of each sphere.</p>
  123. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const numSpheres = 15;
  124. for (let i = 0; i &lt; numSpheres; ++i) {
  125. // make a base for the shadow and the sphere
  126. // so they move together.
  127. const base = new THREE.Object3D();
  128. scene.add(base);
  129. // add the shadow to the base
  130. // note: we make a new material for each sphere
  131. // so we can set that sphere's material transparency
  132. // separately.
  133. const shadowMat = new THREE.MeshBasicMaterial({
  134. map: shadowTexture,
  135. transparent: true, // so we can see the ground
  136. depthWrite: false, // so we don't have to sort
  137. });
  138. const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
  139. shadowMesh.position.y = 0.001; // so we're above the ground slightly
  140. shadowMesh.rotation.x = Math.PI * -.5;
  141. const shadowSize = sphereRadius * 4;
  142. shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
  143. base.add(shadowMesh);
  144. // add the sphere to the base
  145. const u = i / numSpheres; // goes from 0 to 1 as we iterate the spheres.
  146. const sphereMat = new THREE.MeshPhongMaterial();
  147. sphereMat.color.setHSL(u, 1, .75);
  148. const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
  149. sphereMesh.position.set(0, sphereRadius + 2, 0);
  150. base.add(sphereMesh);
  151. // remember all 3 plus the y position
  152. sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
  153. }
  154. </pre>
  155. <p>We setup 2 lights. One is a <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> with the intensity set to 2 to really
  156. brighten things up.</p>
  157. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  158. const skyColor = 0xB1E1FF; // light blue
  159. const groundColor = 0xB97A20; // brownish orange
  160. const intensity = 2;
  161. const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
  162. scene.add(light);
  163. }
  164. </pre>
  165. <p>The other is a <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> so the spheres get some definition</p>
  166. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  167. const color = 0xFFFFFF;
  168. const intensity = 1;
  169. const light = new THREE.DirectionalLight(color, intensity);
  170. light.position.set(0, 10, 5);
  171. light.target.position.set(-5, 0, 0);
  172. scene.add(light);
  173. scene.add(light.target);
  174. }
  175. </pre>
  176. <p>It would render as is but let's animate there spheres.
  177. For each sphere, shadow, base set we move the base in the xz plane, we
  178. move the sphere up and down using <a href="/docs/#api/en/math/Math.abs(Math.sin(time))"><code class="notranslate" translate="no">Math.abs(Math.sin(time))</code></a>
  179. which gives us a bouncy animation. And, we also set the shadow material's
  180. opacity so that as each sphere goes higher its shadow fades out.</p>
  181. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  182. time *= 0.001; // convert to seconds
  183. ...
  184. sphereShadowBases.forEach((sphereShadowBase, ndx) =&gt; {
  185. const {base, sphereMesh, shadowMesh, y} = sphereShadowBase;
  186. // u is a value that goes from 0 to 1 as we iterate the spheres
  187. const u = ndx / sphereShadowBases.length;
  188. // compute a position for the base. This will move
  189. // both the sphere and its shadow
  190. const speed = time * .2;
  191. const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
  192. const radius = Math.sin(speed - ndx) * 10;
  193. base.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
  194. // yOff is a value that goes from 0 to 1
  195. const yOff = Math.abs(Math.sin(time * 2 + ndx));
  196. // move the sphere up and down
  197. sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);
  198. // fade the shadow as the sphere goes up
  199. shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
  200. });
  201. ...
  202. </pre>
  203. <p>And here's 15 kind of bouncing balls.</p>
  204. <p></p><div translate="no" class="threejs_example_container notranslate">
  205. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-fake.html"></iframe></div>
  206. <a class="threejs_center" href="/manual/examples/shadows-fake.html" target="_blank">click here to open in a separate window</a>
  207. </div>
  208. <p></p>
  209. <p>In some apps it's common to use a round or oval shadow for everything but
  210. of course you could also use different shaped shadow textures. You might also
  211. give the shadow a harder edge. A good example of using this type
  212. of shadow is <a href="https://www.google.com/search?tbm=isch&amp;q=animal+crossing+pocket+camp+screenshots">Animal Crossing Pocket Camp</a>
  213. where you can see each character has a simple round shadow. It's effective and cheap.
  214. <a href="https://www.google.com/search?q=monument+valley+screenshots&amp;tbm=isch">Monument Valley</a>
  215. appears to also use this kind of shadow for the main character.</p>
  216. <p>So, moving on to shadow maps, there are 3 lights which can cast shadows. The <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>,
  217. the <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>, and the <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>.</p>
  218. <p>Let's start with the <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> with the helper example from <a href="lights.html">the lights article</a>.</p>
  219. <p>The first thing we need to do is turn on shadows in the renderer.</p>
  220. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const renderer = new THREE.WebGLRenderer({canvas});
  221. +renderer.shadowMap.enabled = true;
  222. </pre>
  223. <p>Then we also need to tell the light to cast a shadow</p>
  224. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const light = new THREE.DirectionalLight(color, intensity);
  225. +light.castShadow = true;
  226. </pre>
  227. <p>We also need to go to each mesh in the scene and decide if it should
  228. both cast shadows and/or receive shadows.</p>
  229. <p>Let's make the plane (the ground) only receive shadows since we don't
  230. really care what happens underneath.</p>
  231. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(planeGeo, planeMat);
  232. mesh.receiveShadow = true;
  233. </pre>
  234. <p>For the cube and the sphere let's have them both receive and cast shadows</p>
  235. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  236. mesh.castShadow = true;
  237. mesh.receiveShadow = true;
  238. ...
  239. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  240. mesh.castShadow = true;
  241. mesh.receiveShadow = true;
  242. </pre>
  243. <p>And then we run it.</p>
  244. <p></p><div translate="no" class="threejs_example_container notranslate">
  245. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-directional-light.html"></iframe></div>
  246. <a class="threejs_center" href="/manual/examples/shadows-directional-light.html" target="_blank">click here to open in a separate window</a>
  247. </div>
  248. <p></p>
  249. <p>What happened? Why are parts of the shadows missing?</p>
  250. <p>The reason is shadow maps are created by rendering the scene from the point
  251. of view of the light. In this case there is a camera at the <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>
  252. that is looking at its target. Just like <a href="cameras.html">the camera's we previously covered</a>
  253. the light's shadow camera defines an area inside of which
  254. the shadows get rendered. In the example above that area is too small.</p>
  255. <p>In order to visualize that area we can get the light's shadow camera and add
  256. a <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> to the scene.</p>
  257. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
  258. scene.add(cameraHelper);
  259. </pre>
  260. <p>And now you can see the area for which shadows are cast and received.</p>
  261. <p></p><div translate="no" class="threejs_example_container notranslate">
  262. <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>
  263. <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-helper.html" target="_blank">click here to open in a separate window</a>
  264. </div>
  265. <p></p>
  266. <p>Adjust the target x value back and forth and it should be pretty clear that only
  267. what's inside the light's shadow camera box is where shadows are drawn.</p>
  268. <p>We can adjust the size of that box by adjusting the light's shadow camera.</p>
  269. <p>Let's add some GUI setting to adjust the light's shadow camera box. Since a
  270. <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> represents light all going in a parallel direction, the
  271. <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> uses an <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> for its shadow camera.
  272. We went over how an <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> works in <a href="cameras.html">the previous article about cameras</a>.</p>
  273. <p>Recall an <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> defines
  274. its box or <em>view frustum</em> by its <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>,
  275. and <code class="notranslate" translate="no">zoom</code> properties.</p>
  276. <p>Again let's make a helper class for the lil-gui. We'll make a <code class="notranslate" translate="no">DimensionGUIHelper</code>
  277. that we'll pass an object and 2 properties. It will present one property that lil-gui
  278. can adjust and in response will set the two properties one positive and one negative.
  279. We can use this to set <code class="notranslate" translate="no">left</code> and <code class="notranslate" translate="no">right</code> as <code class="notranslate" translate="no">width</code> and <code class="notranslate" translate="no">up</code> and <code class="notranslate" translate="no">down</code> as <code class="notranslate" translate="no">height</code>.</p>
  280. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DimensionGUIHelper {
  281. constructor(obj, minProp, maxProp) {
  282. this.obj = obj;
  283. this.minProp = minProp;
  284. this.maxProp = maxProp;
  285. }
  286. get value() {
  287. return this.obj[this.maxProp] * 2;
  288. }
  289. set value(v) {
  290. this.obj[this.maxProp] = v / 2;
  291. this.obj[this.minProp] = v / -2;
  292. }
  293. }
  294. </pre>
  295. <p>We'll also use the <code class="notranslate" translate="no">MinMaxGUIHelper</code> we created in the <a href="cameras.html">camera article</a>
  296. to adjust <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code>.</p>
  297. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  298. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  299. gui.add(light, 'intensity', 0, 2, 0.01);
  300. +{
  301. + const folder = gui.addFolder('Shadow Camera');
  302. + folder.open();
  303. + folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)
  304. + .name('width')
  305. + .onChange(updateCamera);
  306. + folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)
  307. + .name('height')
  308. + .onChange(updateCamera);
  309. + const minMaxGUIHelper = new MinMaxGUIHelper(light.shadow.camera, 'near', 'far', 0.1);
  310. + folder.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  311. + folder.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
  312. + folder.add(light.shadow.camera, 'zoom', 0.01, 1.5, 0.01).onChange(updateCamera);
  313. +}
  314. </pre>
  315. <p>We tell the GUI to call our <code class="notranslate" translate="no">updateCamera</code> function anytime anything changes.
  316. Let's write that function to update the light, the helper for the light, the
  317. light's shadow camera, and the helper showing the light's shadow camera.</p>
  318. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateCamera() {
  319. // update the light target's matrixWorld because it's needed by the helper
  320. light.target.updateMatrixWorld();
  321. helper.update();
  322. // update the light's shadow camera's projection matrix
  323. light.shadow.camera.updateProjectionMatrix();
  324. // and now update the camera helper we're using to show the light's shadow camera
  325. cameraHelper.update();
  326. }
  327. updateCamera();
  328. </pre>
  329. <p>And now that we've given the light's shadow camera a GUI we can play with the values.</p>
  330. <p></p><div translate="no" class="threejs_example_container notranslate">
  331. <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>
  332. <a class="threejs_center" href="/manual/examples/shadows-directional-light-with-camera-gui.html" target="_blank">click here to open in a separate window</a>
  333. </div>
  334. <p></p>
  335. <p>Set the <code class="notranslate" translate="no">width</code> and <code class="notranslate" translate="no">height</code> to about 30 and you can see the shadows are correct
  336. and the areas that need to be in shadow for this scene are entirely covered.</p>
  337. <p>But this brings up the question, why not just set <code class="notranslate" translate="no">width</code> and <code class="notranslate" translate="no">height</code> to some
  338. giant numbers to just cover everything? Set the <code class="notranslate" translate="no">width</code> and <code class="notranslate" translate="no">height</code> to 100
  339. and you might see something like this</p>
  340. <div class="threejs_center"><img src="../resources/images/low-res-shadow-map.png" style="width: 369px"></div>
  341. <p>What's going on with these low-res shadows?!</p>
  342. <p>This issue is yet another shadow related setting to be aware of.
  343. Shadow maps are textures the shadows get drawn into.
  344. Those textures have a size. The shadow camera's area we set above is stretched
  345. across that size. That means the larger area you set, the more blocky your shadows will
  346. be.</p>
  347. <p>You can set the resolution of the shadow map's texture by setting <code class="notranslate" translate="no">light.shadow.mapSize.width</code>
  348. and <code class="notranslate" translate="no">light.shadow.mapSize.height</code>. They default to 512x512.
  349. The larger you make them the more memory they take and the slower they are to compute so you want
  350. to set them as small as you can and still make your scene work. The same is true with the
  351. light's shadow camera area. Smaller means better looking shadows so make the area as small as you
  352. can and still cover your scene. Be aware that each user's machine has a maximum texture size
  353. allowed which is available on the renderer as <a href="/docs/#api/en/renderers/WebGLRenderer#capabilities"><code class="notranslate" translate="no">renderer.capabilities.maxTextureSize</code></a>.</p>
  354. <!--
  355. Ok but what about `near` and `far` I hear you thinking. Can we set `near` to 0.00001 and far to `100000000`
  356. -->
  357. <p>Switching to the <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> the light's shadow camera becomes a <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Unlike the <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>'s shadow camera
  358. where we could manually set most its settings, <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>'s shadow camera is controlled by the <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> itself. The <code class="notranslate" translate="no">fov</code> for the shadow
  359. camera is directly connected to the <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>'s <code class="notranslate" translate="no">angle</code> setting.
  360. The <code class="notranslate" translate="no">aspect</code> is set automatically based on the size of the shadow map.</p>
  361. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.DirectionalLight(color, intensity);
  362. +const light = new THREE.SpotLight(color, intensity);
  363. </pre>
  364. <p>and we added back in the <code class="notranslate" translate="no">penumbra</code> and <code class="notranslate" translate="no">angle</code> settings
  365. from our <a href="lights.html">article about lights</a>.</p>
  366. <p></p><div translate="no" class="threejs_example_container notranslate">
  367. <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>
  368. <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-camera-gui.html" target="_blank">click here to open in a separate window</a>
  369. </div>
  370. <p></p>
  371. <!--
  372. You can notice, just like the last example if we set the angle high
  373. then the shadow map, the texture is spread over a very large area and
  374. the resolution of our shadows gets really low.
  375. div class="threejs_center"><img src="../resources/images/low-res-shadow-map-spotlight.png" style="width: 344px"></div>
  376. You can increase the size of the shadow map as mentioned above. You can
  377. also blur the result
  378. <div translate="no" class="threejs_example_container notranslate">
  379. <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>
  380. <a class="threejs_center" href="/manual/examples/shadows-spot-light-with-shadow-radius" target="_blank">click here to open in a separate window</a>
  381. </div>
  382. -->
  383. <p>And finally there's shadows with a <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>. Since a <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>
  384. shines in all directions the only relevant settings are <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code>.
  385. Otherwise the <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> shadow is effectively 6 <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> shadows
  386. each one pointing to the face of a cube around the light. This means
  387. <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> shadows are much slower since the entire scene must be
  388. drawn 6 times, one for each direction.</p>
  389. <p>Let's put a box around our scene so we can see shadows on the walls
  390. and ceiling. We'll set the material's <code class="notranslate" translate="no">side</code> property to <code class="notranslate" translate="no">THREE.BackSide</code>
  391. so we render the inside of the box instead of the outside. Like the floor
  392. we'll set it only to receive shadows. Also we'll set the position of the
  393. box so its bottom is slightly below the floor so the floor and the bottom
  394. of the box don't z-fight.</p>
  395. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  396. const cubeSize = 30;
  397. const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
  398. const cubeMat = new THREE.MeshPhongMaterial({
  399. color: '#CCC',
  400. side: THREE.BackSide,
  401. });
  402. const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  403. mesh.receiveShadow = true;
  404. mesh.position.set(0, cubeSize / 2 - 0.1, 0);
  405. scene.add(mesh);
  406. }
  407. </pre>
  408. <p>And of course we need to switch the light to a <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a>.</p>
  409. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const light = new THREE.SpotLight(color, intensity);
  410. +const light = new THREE.PointLight(color, intensity);
  411. ....
  412. // so we can easily see where the point light is
  413. +const helper = new THREE.PointLightHelper(light);
  414. +scene.add(helper);
  415. </pre>
  416. <p></p><div translate="no" class="threejs_example_container notranslate">
  417. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/shadows-point-light.html"></iframe></div>
  418. <a class="threejs_center" href="/manual/examples/shadows-point-light.html" target="_blank">click here to open in a separate window</a>
  419. </div>
  420. <p></p>
  421. <p>Use the <code class="notranslate" translate="no">position</code> GUI settings to move the light around
  422. and you'll see the shadows fall on all the walls. You can
  423. also adjust <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> settings and see just like
  424. the other shadows when things are closer than <code class="notranslate" translate="no">near</code> they
  425. no longer receive a shadow and they are further than <code class="notranslate" translate="no">far</code>
  426. they are always in shadow.</p>
  427. <!--
  428. self shadow, shadow acne
  429. -->
  430. </div>
  431. </div>
  432. </div>
  433. <script src="/manual/resources/prettify.js"></script>
  434. <script src="/manual/resources/lesson.js"></script>
  435. </body></html>