lights.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <!DOCTYPE html><html lang="en"><head>
  2. <meta charset="utf-8">
  3. <title>Lights</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 – Lights">
  8. <meta property="og:image" content="https://threejs.org/files/share.png">
  9. <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
  10. <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
  11. <link rel="stylesheet" href="../resources/lesson.css">
  12. <link rel="stylesheet" href="../resources/lang.css">
  13. <!-- 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>Lights</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 and also the article on <a href="setup.html">setting up your environment</a>. The
  35. <a href="textures.html">previous article was about textures</a>.</p>
  36. <p>Let's go over how to use the various kinds of lights in three.</p>
  37. <p>Starting with one of our previous samples let's update the camera.
  38. We'll set the field of view to 45 degrees, the far plane to 100 units,
  39. and we'll move the camera 10 units up and 20 units back from the origin</p>
  40. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">*const fov = 45;
  41. const aspect = 2; // the canvas default
  42. const near = 0.1;
  43. *const far = 100;
  44. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  45. +camera.position.set(0, 10, 20);
  46. </pre>
  47. <p>Next let's add <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>. <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> let the user spin
  48. or <em>orbit</em> the camera around some point. The <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> are
  49. an optional feature of three.js so first we need to include them
  50. in our page</p>
  51. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
  52. +import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
  53. </pre>
  54. <p>Then we can use them. We pass the <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> a camera to
  55. control and the DOM element to use to get input events</p>
  56. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const controls = new OrbitControls(camera, canvas);
  57. controls.target.set(0, 5, 0);
  58. controls.update();
  59. </pre>
  60. <p>We also set the target to orbit around to 5 units above the origin
  61. and then call <code class="notranslate" translate="no">controls.update</code> so the controls will use the new
  62. target.</p>
  63. <p>Next up let's make some things to light up. First we'll make ground
  64. plane. We'll apply a tiny 2x2 pixel checkerboard texture that looks
  65. like this</p>
  66. <div class="threejs_center">
  67. <img src="../examples/resources/images/checker.png" class="border" style="
  68. image-rendering: pixelated;
  69. width: 128px;
  70. ">
  71. </div>
  72. <p>First we load the texture, set it to repeating, set the filtering to
  73. nearest, and set how many times we want it to repeat. Since the
  74. texture is a 2x2 pixel checkerboard, by repeating and setting the
  75. repeat to half the size of the plane each check on the checkerboard
  76. will be exactly 1 unit large;</p>
  77. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeSize = 40;
  78. const loader = new THREE.TextureLoader();
  79. const texture = loader.load('resources/images/checker.png');
  80. texture.wrapS = THREE.RepeatWrapping;
  81. texture.wrapT = THREE.RepeatWrapping;
  82. texture.magFilter = THREE.NearestFilter;
  83. texture.colorSpace = THREE.SRGBColorSpace;
  84. const repeats = planeSize / 2;
  85. texture.repeat.set(repeats, repeats);
  86. </pre>
  87. <p>We then make a plane geometry, a material for the plane, and a mesh
  88. to insert it in the scene. Planes default to being in the XY plane
  89. but the ground is in the XZ plane so we rotate it.</p>
  90. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  91. const planeMat = new THREE.MeshPhongMaterial({
  92. map: texture,
  93. side: THREE.DoubleSide,
  94. });
  95. const mesh = new THREE.Mesh(planeGeo, planeMat);
  96. mesh.rotation.x = Math.PI * -.5;
  97. scene.add(mesh);
  98. </pre>
  99. <p>Let's add a cube and a sphere so we have 3 things to light including the plane</p>
  100. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  101. const cubeSize = 4;
  102. const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
  103. const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
  104. const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  105. mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
  106. scene.add(mesh);
  107. }
  108. {
  109. const sphereRadius = 3;
  110. const sphereWidthDivisions = 32;
  111. const sphereHeightDivisions = 16;
  112. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  113. const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
  114. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  115. mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
  116. scene.add(mesh);
  117. }
  118. </pre>
  119. <p>Now that we have a scene to light up let's add lights!</p>
  120. <h2 id="-ambientlight-"><a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a></h2>
  121. <p>First let's make an <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a></p>
  122. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
  123. const intensity = 1;
  124. const light = new THREE.AmbientLight(color, intensity);
  125. scene.add(light);
  126. </pre>
  127. <p>Let's also make it so we can adjust the light's parameters.
  128. We'll use <a href="https://github.com/georgealways/lil-gui">lil-gui</a> again.
  129. To be able to adjust the color via lil-gui we need a small helper
  130. that presents a property to lil-gui that looks like a CSS hex color string
  131. (eg: <code class="notranslate" translate="no">#FF8844</code>). Our helper will get the color from a named property,
  132. convert it to a hex string to offer to lil-gui. When lil-gui tries
  133. to set the helper's property we'll assign the result back to the light's
  134. color.</p>
  135. <p>Here's the helper:</p>
  136. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ColorGUIHelper {
  137. constructor(object, prop) {
  138. this.object = object;
  139. this.prop = prop;
  140. }
  141. get value() {
  142. return `#${this.object[this.prop].getHexString()}`;
  143. }
  144. set value(hexString) {
  145. this.object[this.prop].set(hexString);
  146. }
  147. }
  148. </pre>
  149. <p>And here's our code setting up lil-gui</p>
  150. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  151. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  152. gui.add(light, 'intensity', 0, 2, 0.01);
  153. </pre>
  154. <p>And here's the result</p>
  155. <p></p><div translate="no" class="threejs_example_container notranslate">
  156. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-ambient.html"></iframe></div>
  157. <a class="threejs_center" href="/manual/examples/lights-ambient.html" target="_blank">click here to open in a separate window</a>
  158. </div>
  159. <p></p>
  160. <p>Click and drag in the scene to <em>orbit</em> the camera.</p>
  161. <p>Notice there is no definition. The shapes are flat. The <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a> effectively
  162. just multiplies the material's color by the light's color times the
  163. intensity.</p>
  164. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">color = materialColor * light.color * light.intensity;
  165. </pre><p>That's it. It has no direction.
  166. This style of ambient lighting is actually not all that
  167. useful as lighting as it's 100% even so other than changing the color
  168. of everything in the scene it doesn't look much like <em>lighting</em>.
  169. What it does help with is making the darks not too dark.</p>
  170. <h2 id="-hemispherelight-"><a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a></h2>
  171. <p>Let's switch the code to a <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a>. A <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a>
  172. takes a sky color and a ground color and just multiplies the
  173. material's color between those 2 colors—the sky color if the
  174. surface of the object is pointing up and the ground color if
  175. the surface of the object is pointing down.</p>
  176. <p>Here's the new code</p>
  177. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const color = 0xFFFFFF;
  178. +const skyColor = 0xB1E1FF; // light blue
  179. +const groundColor = 0xB97A20; // brownish orange
  180. const intensity = 1;
  181. -const light = new THREE.AmbientLight(color, intensity);
  182. +const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
  183. scene.add(light);
  184. </pre>
  185. <p>Let's also update the lil-gui code to edit both colors</p>
  186. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  187. -gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  188. +gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('skyColor');
  189. +gui.addColor(new ColorGUIHelper(light, 'groundColor'), 'value').name('groundColor');
  190. gui.add(light, 'intensity', 0, 2, 0.01);
  191. </pre>
  192. <p>The result:</p>
  193. <p></p><div translate="no" class="threejs_example_container notranslate">
  194. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-hemisphere.html"></iframe></div>
  195. <a class="threejs_center" href="/manual/examples/lights-hemisphere.html" target="_blank">click here to open in a separate window</a>
  196. </div>
  197. <p></p>
  198. <p>Notice again there is almost no definition, everything looks kind
  199. of flat. The <a href="/docs/#api/en/lights/HemisphereLight"><code class="notranslate" translate="no">HemisphereLight</code></a> used in combination with another light
  200. can help give a nice kind of influence of the color of the sky
  201. and ground. In that way it's best used in combination with some
  202. other light or a substitute for an <a href="/docs/#api/en/lights/AmbientLight"><code class="notranslate" translate="no">AmbientLight</code></a>.</p>
  203. <h2 id="-directionallight-"><a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a></h2>
  204. <p>Let's switch the code to a <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a>.
  205. A <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> is often used to represent the sun.</p>
  206. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
  207. const intensity = 1;
  208. const light = new THREE.DirectionalLight(color, intensity);
  209. light.position.set(0, 10, 0);
  210. light.target.position.set(-5, 0, 0);
  211. scene.add(light);
  212. scene.add(light.target);
  213. </pre>
  214. <p>Notice that we had to add the <code class="notranslate" translate="no">light</code> and the <code class="notranslate" translate="no">light.target</code>
  215. to the scene. A three.js <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> will shine
  216. in the direction of its target.</p>
  217. <p>Let's make it so we can move the target by adding it to
  218. our GUI.</p>
  219. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  220. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  221. gui.add(light, 'intensity', 0, 2, 0.01);
  222. gui.add(light.target.position, 'x', -10, 10);
  223. gui.add(light.target.position, 'z', -10, 10);
  224. gui.add(light.target.position, 'y', 0, 10);
  225. </pre>
  226. <p></p><div translate="no" class="threejs_example_container notranslate">
  227. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-directional.html"></iframe></div>
  228. <a class="threejs_center" href="/manual/examples/lights-directional.html" target="_blank">click here to open in a separate window</a>
  229. </div>
  230. <p></p>
  231. <p>It's kind of hard to see what's going on. Three.js has a bunch
  232. of helper objects we can add to our scene to help visualize
  233. invisible parts of a scene. In this case we'll use the
  234. <a href="/docs/#api/en/helpers/DirectionalLightHelper"><code class="notranslate" translate="no">DirectionalLightHelper</code></a> which will draw a plane, to represent
  235. the light, and a line from the light to the target. We just
  236. pass it the light and add it to the scene.</p>
  237. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const helper = new THREE.DirectionalLightHelper(light);
  238. scene.add(helper);
  239. </pre>
  240. <p>While we're at it let's make it so we can set both the position
  241. of the light and the target. To do this we'll make a function
  242. that given a <a href="/docs/#api/en/math/Vector3"><code class="notranslate" translate="no">Vector3</code></a> will adjust its <code class="notranslate" translate="no">x</code>, <code class="notranslate" translate="no">y</code>, and <code class="notranslate" translate="no">z</code> properties
  243. using <code class="notranslate" translate="no">lil-gui</code>.</p>
  244. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeXYZGUI(gui, vector3, name, onChangeFn) {
  245. const folder = gui.addFolder(name);
  246. folder.add(vector3, 'x', -10, 10).onChange(onChangeFn);
  247. folder.add(vector3, 'y', 0, 10).onChange(onChangeFn);
  248. folder.add(vector3, 'z', -10, 10).onChange(onChangeFn);
  249. folder.open();
  250. }
  251. </pre>
  252. <p>Note that we need to call the helper's <code class="notranslate" translate="no">update</code> function
  253. anytime we change something so the helper knows to update
  254. itself. As such we pass in an <code class="notranslate" translate="no">onChangeFn</code> function to
  255. get called anytime lil-gui updates a value.</p>
  256. <p>Then we can use that for both the light's position
  257. and the target's position like this</p>
  258. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+function updateLight() {
  259. + light.target.updateMatrixWorld();
  260. + helper.update();
  261. +}
  262. +updateLight();
  263. const gui = new GUI();
  264. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  265. gui.add(light, 'intensity', 0, 2, 0.01);
  266. +makeXYZGUI(gui, light.position, 'position', updateLight);
  267. +makeXYZGUI(gui, light.target.position, 'target', updateLight);
  268. </pre>
  269. <p>Now we can move the light, and its target</p>
  270. <p></p><div translate="no" class="threejs_example_container notranslate">
  271. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-directional-w-helper.html"></iframe></div>
  272. <a class="threejs_center" href="/manual/examples/lights-directional-w-helper.html" target="_blank">click here to open in a separate window</a>
  273. </div>
  274. <p></p>
  275. <p>Orbit the camera and it gets easier to see. The plane
  276. represents a <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> because a directional
  277. light computes light coming in one direction. There is no
  278. <em>point</em> the light comes from, it's an infinite plane of light
  279. shooting out parallel rays of light.</p>
  280. <h2 id="-pointlight-"><a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a></h2>
  281. <p>A <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> is a light that sits at a point and shoots light
  282. in all directions from that point. Let's change the code.</p>
  283. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
  284. -const intensity = 1;
  285. +const intensity = 150;
  286. -const light = new THREE.DirectionalLight(color, intensity);
  287. +const light = new THREE.PointLight(color, intensity);
  288. light.position.set(0, 10, 0);
  289. -light.target.position.set(-5, 0, 0);
  290. scene.add(light);
  291. -scene.add(light.target);
  292. </pre>
  293. <p>Let's also switch to a <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">PointLightHelper</code></a></p>
  294. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const helper = new THREE.DirectionalLightHelper(light);
  295. +const helper = new THREE.PointLightHelper(light);
  296. scene.add(helper);
  297. </pre>
  298. <p>and as there is no target the <code class="notranslate" translate="no">onChange</code> function can be simpler.</p>
  299. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateLight() {
  300. - light.target.updateMatrixWorld();
  301. helper.update();
  302. }
  303. -updateLight();
  304. </pre>
  305. <p>Note that at some level a <a href="/docs/#api/en/helpers/PointLightHelper"><code class="notranslate" translate="no">PointLightHelper</code></a> has no um, point.
  306. It just draws a small wireframe diamond. It could just as easily
  307. be any shape you want, just add a mesh to the light itself.</p>
  308. <p>A <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> has the added property of <a href="/docs/#api/en/lights/PointLight#distance"><code class="notranslate" translate="no">distance</code></a>.
  309. If the <code class="notranslate" translate="no">distance</code> is 0 then the <a href="/docs/#api/en/lights/PointLight"><code class="notranslate" translate="no">PointLight</code></a> shines to
  310. infinity. If the <code class="notranslate" translate="no">distance</code> is greater than 0 then the light shines
  311. its full intensity at the light and fades to no influence at <code class="notranslate" translate="no">distance</code>
  312. units away from the light.</p>
  313. <p>Let's setup the GUI so we can adjust the distance.</p>
  314. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  315. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  316. gui.add(light, 'intensity', 0, 2, 0.01);
  317. +gui.add(light, 'distance', 0, 40).onChange(updateLight);
  318. makeXYZGUI(gui, light.position, 'position', updateLight);
  319. -makeXYZGUI(gui, light.target.position, 'target', updateLight);
  320. </pre>
  321. <p>And now try it out.</p>
  322. <p></p><div translate="no" class="threejs_example_container notranslate">
  323. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-point.html"></iframe></div>
  324. <a class="threejs_center" href="/manual/examples/lights-point.html" target="_blank">click here to open in a separate window</a>
  325. </div>
  326. <p></p>
  327. <p>Notice when <code class="notranslate" translate="no">distance</code> is &gt; 0 how the light fades out.</p>
  328. <h2 id="-spotlight-"><a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a></h2>
  329. <p>Spotlights are effectively a point light with a cone
  330. attached where the light only shines inside the cone.
  331. There's actually 2 cones. An outer cone and an inner
  332. cone. Between the inner cone and the outer cone the
  333. light fades from full intensity to zero.</p>
  334. <p>To use a <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a> we need a target just like
  335. the directional light. The light's cone will
  336. open toward the target.</p>
  337. <p>Modifying our <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> with helper from above</p>
  338. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
  339. -const intensity = 1;
  340. +const intensity = 150;
  341. -const light = new THREE.DirectionalLight(color, intensity);
  342. +const light = new THREE.SpotLight(color, intensity);
  343. scene.add(light);
  344. scene.add(light.target);
  345. -const helper = new THREE.DirectionalLightHelper(light);
  346. +const helper = new THREE.SpotLightHelper(light);
  347. scene.add(helper);
  348. </pre>
  349. <p>The spotlight's cone's angle is set with the <a href="/docs/#api/en/lights/SpotLight#angle"><code class="notranslate" translate="no">angle</code></a>
  350. property in radians. We'll use our <code class="notranslate" translate="no">DegRadHelper</code> from the
  351. <a href="textures.html">texture article</a> to present a UI in
  352. degrees.</p>
  353. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">gui.add(new DegRadHelper(light, 'angle'), 'value', 0, 90).name('angle').onChange(updateLight);
  354. </pre>
  355. <p>The inner cone is defined by setting the <a href="/docs/#api/en/lights/SpotLight#penumbra"><code class="notranslate" translate="no">penumbra</code></a> property
  356. as a percentage from the outer cone. In other words when <code class="notranslate" translate="no">penumbra</code> is 0 then the
  357. inner cone is the same size (0 = no difference) from the outer cone. When the
  358. <code class="notranslate" translate="no">penumbra</code> is 1 then the light fades starting in the center of the cone to the
  359. outer cone. When <code class="notranslate" translate="no">penumbra</code> is .5 then the light fades starting from 50% between
  360. the center of the outer cone.</p>
  361. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">gui.add(light, 'penumbra', 0, 1, 0.01);
  362. </pre>
  363. <p></p><div translate="no" class="threejs_example_container notranslate">
  364. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-spot-w-helper.html"></iframe></div>
  365. <a class="threejs_center" href="/manual/examples/lights-spot-w-helper.html" target="_blank">click here to open in a separate window</a>
  366. </div>
  367. <p></p>
  368. <p>Notice with the default <code class="notranslate" translate="no">penumbra</code> of 0 the spotlight has a very sharp edge
  369. whereas as you adjust the <code class="notranslate" translate="no">penumbra</code> toward 1 the edge blurs.</p>
  370. <p>It might be hard to see the <em>cone</em> of the spotlight. The reason is it's
  371. below the ground. Shorten the distance to around 5 and you'll see the open
  372. end of the cone.</p>
  373. <h2 id="-rectarealight-"><a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a></h2>
  374. <p>There's one more type of light, the <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a>, which represents
  375. exactly what it sounds like, a rectangular area of light like a long
  376. fluorescent light or maybe a frosted sky light in a ceiling.</p>
  377. <p>The <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> only works with the <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> and the
  378. <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a> so let's change all our materials to <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a></p>
  379. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> ...
  380. const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  381. - const planeMat = new THREE.MeshPhongMaterial({
  382. + const planeMat = new THREE.MeshStandardMaterial({
  383. map: texture,
  384. side: THREE.DoubleSide,
  385. });
  386. const mesh = new THREE.Mesh(planeGeo, planeMat);
  387. mesh.rotation.x = Math.PI * -.5;
  388. scene.add(mesh);
  389. }
  390. {
  391. const cubeSize = 4;
  392. const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
  393. - const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
  394. + const cubeMat = new THREE.MeshStandardMaterial({color: '#8AC'});
  395. const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  396. mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
  397. scene.add(mesh);
  398. }
  399. {
  400. const sphereRadius = 3;
  401. const sphereWidthDivisions = 32;
  402. const sphereHeightDivisions = 16;
  403. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  404. - const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
  405. + const sphereMat = new THREE.MeshStandardMaterial({color: '#CA8'});
  406. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  407. mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
  408. scene.add(mesh);
  409. }
  410. </pre>
  411. <p>To use the <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> we need to include some extra three.js optional data and we'll
  412. include the <a href="/docs/#api/en/helpers/RectAreaLightHelper"><code class="notranslate" translate="no">RectAreaLightHelper</code></a> to help us visualize the light</p>
  413. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from 'three';
  414. +import {RectAreaLightUniformsLib} from 'three/addons/lights/RectAreaLightUniformsLib.js';
  415. +import {RectAreaLightHelper} from 'three/addons/helpers/RectAreaLightHelper.js';
  416. </pre>
  417. <p>and we need to call <code class="notranslate" translate="no">RectAreaLightUniformsLib.init</code></p>
  418. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function main() {
  419. const canvas = document.querySelector('#c');
  420. const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  421. + RectAreaLightUniformsLib.init();
  422. </pre>
  423. <p>If you forget the data the light will still work but it will look funny so
  424. be sure to remember to include the extra data.</p>
  425. <p>Now we can create the light</p>
  426. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = 0xFFFFFF;
  427. *const intensity = 5;
  428. +const width = 12;
  429. +const height = 4;
  430. *const light = new THREE.RectAreaLight(color, intensity, width, height);
  431. light.position.set(0, 10, 0);
  432. +light.rotation.x = THREE.MathUtils.degToRad(-90);
  433. scene.add(light);
  434. *const helper = new RectAreaLightHelper(light);
  435. *light.add(helper);
  436. </pre>
  437. <p>One thing to notice is that unlike the <a href="/docs/#api/en/lights/DirectionalLight"><code class="notranslate" translate="no">DirectionalLight</code></a> and the <a href="/docs/#api/en/lights/SpotLight"><code class="notranslate" translate="no">SpotLight</code></a>, the
  438. <a href="/docs/#api/en/lights/RectAreaLight"><code class="notranslate" translate="no">RectAreaLight</code></a> does not use a target. It just uses its rotation. Another thing
  439. to notice is the helper needs to be a child of the light. It is not a child of the
  440. scene like other helpers.</p>
  441. <p>Let's also adjust the GUI. We'll make it so we can rotate the light and adjust
  442. its <code class="notranslate" translate="no">width</code> and <code class="notranslate" translate="no">height</code></p>
  443. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  444. gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
  445. gui.add(light, 'intensity', 0, 10, 0.01);
  446. gui.add(light, 'width', 0, 20);
  447. gui.add(light, 'height', 0, 20);
  448. gui.add(new DegRadHelper(light.rotation, 'x'), 'value', -180, 180).name('x rotation');
  449. gui.add(new DegRadHelper(light.rotation, 'y'), 'value', -180, 180).name('y rotation');
  450. gui.add(new DegRadHelper(light.rotation, 'z'), 'value', -180, 180).name('z rotation');
  451. makeXYZGUI(gui, light.position, 'position');
  452. </pre>
  453. <p>And here is that.</p>
  454. <p></p><div translate="no" class="threejs_example_container notranslate">
  455. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lights-rectarea.html"></iframe></div>
  456. <a class="threejs_center" href="/manual/examples/lights-rectarea.html" target="_blank">click here to open in a separate window</a>
  457. </div>
  458. <p></p>
  459. <p>It's important to note each light you add to the scene slows down how fast
  460. three.js renders the scene so you should always try to use as few as
  461. possible to achieve your goals.</p>
  462. <p>Next up let's go over <a href="cameras.html">dealing with cameras</a>.</p>
  463. <p><canvas id="c"></canvas></p>
  464. <script type="module" src="../resources/threejs-lights.js"></script>
  465. </div>
  466. </div>
  467. </div>
  468. <script src="../resources/prettify.js"></script>
  469. <script src="../resources/lesson.js"></script>
  470. </body></html>