SeemsPyo 5 years ago
parent
commit
9259b42980

+ 1 - 1
threejs/lessons/kr/index.md

@@ -1,6 +1,6 @@
 Title: Three.js 기초
 
-Three.js 입문자를 위한 튜토리얼입니다.
+Three.js 입문자를 위한 튜토리얼 시리즈입니다.
 
 {{{include "threejs/lessons/toc.html"}}}
 

+ 270 - 0
threejs/lessons/kr/threejs-fog.md

@@ -0,0 +1,270 @@
+Title: Three.js 안개
+Description: Three.js의 안개에 대해 알아봅니다
+TOC: 안개(Fog)
+
+※ 이 글은 Three.js의 튜토리얼 시리즈로서,
+먼저 [Three.js의 기본 구조에 관한 글](threejs-fundamentals.html)을
+읽고 오길 권장합니다.
+
+※ 카메라에 대해 잘 모른다면, 먼저 [카메라에 관한 글](threejs-cameras.html)을
+먼저 읽기 바랍니다.
+
+
+3D 엔진에서 안개란, 일반적으로 카메라로부터의 거리에 따라 특정 색상으로
+점차 변화하는 것을 말합니다. Three.js에서는 `Fog`나 `FogExp2` 객체를
+생성한 뒤 장면(scene)의 [`fog`](Scene.fog) 속성에 지정해 안개를 사용합니다.
+
+`Fog`는 인자로 `near`와 `far`값을 받는데, 이는 카메라로부터의 거리값입니다.
+`near`값보다 가까운 공간은 안개의 영향이 전혀 없고, `far`값보다 먼 공간은
+완전히 안개에 뒤덮입니다. `near`와 `far` 사이의 공간에 있는 물체 또는 물체의
+일부는 점차 안개의 색으로 변화하죠.
+
+`FogExp2`는 카메라에서 멀어질수록 안개의 강도가 강해집니다.
+
+두 가지 안개 모두 마찬가지로, 안개를 사용하려면 장면의 속성에 지정해야 합니다.
+
+```js
+const scene = new THREE.Scene();
+{
+  const color = 0xFFFFFF;  // 하양
+  const near = 10;
+  const far = 100;
+  scene.fog = new THREE.Fog(color, near, far);
+}
+```
+
+`FogExp2`의 경우는 다음처럼 쓸 수 있죠.
+
+```js
+const scene = new THREE.Scene();
+{
+  const color = 0xFFFFFF;
+  const density = 0.1;
+  scene.fog = new THREE.FogExp2(color, density);
+}
+```
+
+`FogExp2`가 더 현실적이긴 하나, 보통 안개의 범위를 특정하기 쉬운
+`Fog`를 더 많이 사용합니다.
+
+<div class="spread">
+  <div>
+    <div data-diagram="fog"></div>
+    <div class="code">THREE.Fog</div>
+  </div>
+  <div>
+    <div data-diagram="fogExp2"></div>
+    <div class="code">THREE.FogExp2</div>
+  </div>
+</div>
+
+한 가지 알아둬야 하는 건 안개는 *렌더링되는 물체*라는 점입니다.
+안개는 물체의 픽셀을 렌더링할 때 같이 렌더링되는데, 이 말은 장면에
+특정 색상의 안개 효과를 주려면 안개와 배경색 **둘 다** 같은 색으로
+지정해야 한다는 겁니다. 배경색은 [`scene.background`](Scene.background)
+속성을 `THREE.Color` 인스턴스로 지정해 바꿀 수 있습니다.
+
+```js
+scene.background = new THREE.Color('#F00');  // 빨강
+```
+
+<div class="spread">
+  <div>
+    <div data-diagram="fogBlueBackgroundRed" class="border"></div>
+    <div class="code">파란 안개, 빨간 배경</div>
+  </div>
+  <div>
+    <div data-diagram="fogBlueBackgroundBlue" class="border"></div>
+    <div class="code">파란 안개, 파란 배경</div>
+  </div>
+</div>
+
+아래는 이전에 사용했던 예제에 안개를 추가한 것입니다. 장면을 생성한 뒤
+안개를 추가하고, 장면의 배경색을 바꾸기만 했죠.
+
+```js
+const scene = new THREE.Scene();
+
++{
++  const near = 1;
++  const far = 2;
++  const color = 'lightblue';
++  scene.fog = new THREE.Fog(color, near, far);
++  scene.background = new THREE.Color(color);
++}
+```
+
+아래 예제의 카메라는 `near`값이 0.1, `far`값이 5입니다. 카메라의 위치는
+`z = 2`이죠. 정육면체의 크기는 1칸이고, z축의 원점에 있습니다. 여기서
+안개를 `near = 1`, `far = 2`로 설정하면 정육면체가 중간부터 사라지기
+시작하겠죠.
+
+{{{example url="../threejs-fog.html" }}}
+
+Let's add an interface so we can adjust the fog. Again we'll use
+[dat.GUI](https://github.com/dataarts/dat.gui). dat.GUI takes
+an object and a property and automagically makes an interface
+for that type of property. We could just simply let it manipulate
+the fog's `near` and `far` properties but it's invalid to have
+`near` be greater than `far` so let's make a helper so dat.GUI
+can manipulate a `near` and `far` property but we'll make sure `near`
+is less than or equal to `far` and `far` is greater than or equal `near`.
+
+```js
+// We use this class to pass to dat.gui
+// so when it manipulates near or far
+// near is never > far and far is never < near
+class FogGUIHelper {
+  constructor(fog) {
+    this.fog = fog;
+  }
+  get near() {
+    return this.fog.near;
+  }
+  set near(v) {
+    this.fog.near = v;
+    this.fog.far = Math.max(this.fog.far, v);
+  }
+  get far() {
+    return this.fog.far;
+  }
+  set far(v) {
+    this.fog.far = v;
+    this.fog.near = Math.min(this.fog.near, v);
+  }
+}
+```
+
+We can then add it like this
+
+```js
+{
+  const near = 1;
+  const far = 2;
+  const color = 'lightblue';
+  scene.fog = new THREE.Fog(color, near, far);
+  scene.background = new THREE.Color(color);
++
++  const fogGUIHelper = new FogGUIHelper(scene.fog);
++  gui.add(fogGUIHelper, 'near', near, far).listen();
++  gui.add(fogGUIHelper, 'far', near, far).listen();
+}
+```
+
+The `near` and `far` parameters set the minimum and maximum values
+for adjusting the fog. They are set when we setup the camera.
+
+The `.listen()` at the end of the last 2 lines tells dat.GUI to *listen*
+for changes. That way when we change `near` because of an edit to `far`
+or we change `far` in response to an edit to `near` dat.GUI will update
+the other property's UI for us.
+
+It might also be nice to be able to change the fog color but like was
+mentioned above we need to keep both the fog color and the background
+color in sync. So, let's add another *virtual* property to our helper
+that will set both colors when dat.GUI manipulates it.
+
+dat.GUI can manipulate colors in 4 ways, as a CSS 6 digit hex string (eg: `#112233`). As an hue, saturation, value, object (eg: `{h: 60, s: 1, v: }`).
+As an RGB array (eg: `[255, 128, 64]`). Or, as an RGBA array (eg: `[127, 200, 75, 0.3]`).
+
+It's easiest for our purpose to use the hex string version since that way
+dat.GUI is only manipulating a single value. Fortunately `THREE.Color`
+as a [`getHexString`](Color.getHexString) method
+we get use to easily get such a string, we just have to prepend a '#' to the front.
+
+```js
+// We use this class to pass to dat.gui
+// so when it manipulates near or far
+// near is never > far and far is never < near
++// Also when dat.gui manipulates color we'll
++// update both the fog and background colors.
+class FogGUIHelper {
+*  constructor(fog, backgroundColor) {
+    this.fog = fog;
++    this.backgroundColor = backgroundColor;
+  }
+  get near() {
+    return this.fog.near;
+  }
+  set near(v) {
+    this.fog.near = v;
+    this.fog.far = Math.max(this.fog.far, v);
+  }
+  get far() {
+    return this.fog.far;
+  }
+  set far(v) {
+    this.fog.far = v;
+    this.fog.near = Math.min(this.fog.near, v);
+  }
++  get color() {
++    return `#${this.fog.color.getHexString()}`;
++  }
++  set color(hexString) {
++    this.fog.color.set(hexString);
++    this.backgroundColor.set(hexString);
++  }
+}
+```
+
+We then call `gui.addColor` to add a color UI for our helper's virtual property.
+
+```js
+{
+  const near = 1;
+  const far = 2;
+  const color = 'lightblue';
+  scene.fog = new THREE.Fog(color, near, far);
+  scene.background = new THREE.Color(color);
+
+*  const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);
+  gui.add(fogGUIHelper, 'near', near, far).listen();
+  gui.add(fogGUIHelper, 'far', near, far).listen();
++  gui.addColor(fogGUIHelper, 'color');
+}
+```
+
+{{{example url="../threejs-fog-gui.html" }}}
+
+You can see setting `near` to like 1.9 and `far` to 2.0 gives
+a very sharp transition between un-fogged and completely fogged.
+where as `near` = 1.1 and `far` = 2.9 should just about be
+the smoothest given our cubes are spinning 2 units away from the camera.
+
+One last thing, there is a boolean [`fog`](Material.fog)
+property on a material for whether or not objects rendered
+with that material are affected by fog. It defaults to `true`
+for most materials. As an example of why you might want
+to turn the fog off, imagine you're making a 3D vehicle
+simulator with a view from the driver's seat or cockpit.
+You probably want the fog off for everything inside the vehicle when
+viewing from inside the vehicle.
+
+A better example might be a house
+and thick fog outside house. Let's say the fog is set to start
+2 meters away (near = 2) and completely fogged out at 4 meters (far = 4).
+Rooms are longer than 2 meters and the house is probably longer
+than 4 meters so you need to set the materials for the inside
+of the house to not apply fog otherwise when standing inside the
+house looking outside the wall at the far end of the room will look
+like it's in the fog.
+
+<div class="spread">
+  <div>
+    <div data-diagram="fogHouseAll" style="height: 300px;" class="border"></div>
+    <div class="code">fog: true, all</div>
+  </div>
+</div>
+
+Notice the walls and ceiling at the far end of the room are getting fog applied.
+By turning fog off on the materials for the house we can fix that issue.
+
+<div class="spread">
+  <div>
+    <div data-diagram="fogHouseInsideNoFog" style="height: 300px;" class="border"></div>
+    <div class="code">fog: true, only outside materials</div>
+  </div>
+</div>
+
+<canvas id="c"></canvas>
+<script type="module" src="resources/threejs-fog.js"></script>

+ 362 - 0
threejs/lessons/kr/threejs-prerequisites.md

@@ -0,0 +1,362 @@
+Title: 먼저 알아야 할 것들
+Description: 튜토리얼을 배우기 전에 알아야 할 기본 지식에 대해 알아봅니다
+TOC: 먼저 알아야 할 것들
+
+이 시리즈는 Three.js에 입문하는 초심자를 위한 튜토리얼입니다. 이 시리즈는
+독자가 최소한 자바스크립트 기본 프로그래밍에 익숙하며, DOM이 무엇인지 설명할
+수 있고, HTML과 자바스크립트로 DOM 요소를 조작할 수 있다는 전제 하에 작성하였습니다.
+또한 `<script type="module">` 태그로 [ES2015 모듈](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import)을
+불러올 수 있으며, [CSS 셀렉터가 무엇인지](https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors)도
+알고, ES5, ES2015, 약간의 ES2016도 알며, 브라우저가 자바스크립트를 이벤트와 콜백으로만
+실행한다는 것도 알고, 클로저가 무엇인지까지 안다고 가정했죠.
+
+이번 글에서는 시리즈를 읽는 데 필요한 이런 기본 전제에 대해 간단히 환기하고
+넘어가겠습니다.
+
+## ES2015 모듈
+
+ES2015 모듈은 스크립트 안에서 `import` 키워드나, 인라인 `<script type="module">`
+태그로 불러올 수 있습니다. 두 가지 예시를 동시에 써보죠.
+
+```html
+<script type="module">
+import * as THREE from './resources/threejs/r115/build/three.module.js';
+
+...
+
+</script>
+```
+
+모듈의 경로는 반드시 상대 경로나 절대 경로여야 합니다. `<img>`, `<a>` 태그, CSS 경로와
+달리 여기서 상대 경로란 `./`이나 `../`로 시작하는 경로를 말합니다.
+
+더 자세한 것은 [이 글](threejs-fundamentals.html)의 마지막 부분을 참고하세요.
+
+## `document.querySelector`와 `document.querySelectorAll`
+
+요소를 선택할 때는 `document.querySelector`나 `document.querySelectorAll`을
+사용하면 됩니다. 첫 번째는 CSS 셀렉터와 일치하는 첫 번째 요소를 반환하고, 두 번째는
+CSS 셀렉터와 일치하는 모든 요소를 반환하죠.
+
+## `onbody`를 쓰지 마세요
+
+옛날에 개발된 많은 페이지가 body 태그의 onload 속성을 사용합니다.
+
+    <body onload="somefunction()">
+
+이런 스타일은 더 이상 권장하지 않습니다. script 태그를 페이지의 끝에 삽입하세요.
+
+```html
+<html>
+  <head>
+    ...
+  </head>
+  <body>
+     ...
+  </body>
+  <script>
+    // inline javascript
+  </script>
+</html>
+```
+
+또는 [`defer` 속성을 사용](https://developer.mozilla.org/ko/docs/Web/HTML/Element/script)하는
+것이 좋습니다.
+
+## 클로저(closure)란?
+
+```js
+function a(v) {
+  const foo = v;
+  return function() {
+     return foo;
+  };
+}
+
+const f = a(123);
+const g = a(456);
+console.log(f());  // 123 출력
+console.log(g());  // 456 출력
+```
+
+위 예제에서 함수 `a`는 매번 호출할 때마다 새로운 함수를 반환합니다. 이는 상수 `foo`에
+대한 *클로저*이죠. 자세한 건 [여기](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures)를
+참고하기 바랍니다.
+
+## `this` 이해하기
+
+`this`는 마법이 아닙니다. `this`는 다른 인자들처럼 자동으로 함수에 넘겨지는 일종의 변수이죠.
+간단히 함수를 직접 호출하는 경우를 예로 들어보겠습니다.
+
+    somefunction(a, b, c);
+
+여기서 `this`는 `null`입니다(엄격(strict) 모드나 모듈 안에서). 반면 `.` 연산자를 붙여 메서드로
+호출하는 경우,
+
+    someobject.somefunction(a, b, c);
+
+`this`는 `someobject`로 지정될 겁니다.
+
+많은 사람들이 헷갈리는 부분은 메서드를 콜백 처리할 때이죠.
+
+     const callback = someobject.somefunction;
+     loader.load(callback);
+
+자바스크립트에 능숙하지 않은 사람이 보기에는 문제가 없을지 모르나, `loader.load`는 콜백 함수를
+`.` 연산자를 써서 호출하지 않으므로-loader가 별도로 `this`를 지정하지 않는 한-`this`는 null이
+됩니다. 콜백 함수를 호출할 때 `this`를 `someobject`로 지정하려면 명시적으로 `this`를 종속시켜야(binding)
+합니다.
+
+     const callback = someobject.somefunction.bind(someobject);
+     loader.load(callback);
+
+`this`를 이해하기 어렵다면, [*this*에 관한 문서](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this)를
+한 번 읽어보길 권합니다.
+
+## ES5/ES2015/ES2016
+
+### `var` 대신 `const`와 `let` 사용하기
+
+`const`와 `let`을 쓸 수 있는 환경에서 `var`를 써야할 이유는 *전혀* 없고, 지금 `var`를 사용하는
+것은 실력 향상에 전혀 도움이 되지 않습니다. 변수를 재할당할 일이 없을 경우 `const`를 사용하세요.
+변수를 재할당하는 것은 흔치 않은 일이니, 주로 `const`를 더 많이 쓰게 될 겁니다. 변수의 값을 바꿔야
+한다면 `let`을 사용하세요. 이런 습관을 들이면 버그를 훨씬 더 많이 줄일 수 있습니다.
+
+### `for(elem in collection)` 대신 `for(elem of collection)` 사용하기
+
+`for of`는 `for in`의 문제를 해결하기 위해 새로 추가된 문법입니다.
+
+예를 들어 객체의 키/값 쌍을 반복문으로 돌릴 경우 다음과 같이 쓸 수 있죠.
+
+```js
+for (const [key, value] of Object.entries(someObject)) {
+  console.log(key, value);
+}
+```
+
+### `forEach`, `map`, `filter` 등을 적절히 활용하기
+
+ES5에서 배열에 [`forEach`](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach),
+[`map`](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
+메서드 등이 추가되었고, ES2015에서는 [`filter`](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
+메서드 등 여러 유용한 메서드가 추가되었습니다.
+
+### 구조분해할당(destructuring) 사용하기
+
+`const dims = { width: 300, height: 150 }`. 이런 객체가 있다고 해보죠. 객체의
+각 속성을 별도의 변수에 할당하고자 합니다.
+
+기존 방법
+
+     const width = dims.width;
+     const height = dims.height;
+
+새로운 방법
+
+     const { width, height } = dims;
+
+### 객체 선언 시 축약 문법 사용
+
+기존 방법
+
+```js
+ const width = 300;
+ const height = 150;
+ const obj = {
+   width: width,
+   height: height,
+   area: function() {
+     return this.width * this.height
+   },
+ };
+```
+
+새로운 방법
+
+```js
+ const width = 300;
+ const height = 150;
+ const obj = {
+   width,
+   height,
+   area() {
+     return this.width * this.height;
+   },
+ };
+```
+
+### 전개 연산자 `...` 사용하기
+
+전개 연산자는 매우 유용합니다. 예를 들어보죠.
+
+```js
+ function log(className, ...args) {
+   const elem = document.createElement('div');
+   elem.className = className;
+   elem.textContent = [...args].join(' ');
+   document.body.appendChild(elem);
+ }
+```
+
+또 배열을 인자로 넘겨줄 때도 유용합니다.
+
+```js
+const position = [1, 2, 3];
+somemesh.position.set(...position);
+```
+
+### `class` 사용하기
+
+ES5 이하의 문법으로 클래스 스타일의 객체를 만드는 방법은 다른 개발자들에게 낯선 요소
+중 하나였습니다. ES2015부터는 C++/C#/Java 등 다른 객체지향 언어처럼 [`class` 키워드를 사용](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes)해
+클래스를 생성할 수 있습니다.
+
+### getter와 setter
+
+모던 프로그래밍 언어에는 대부분
+[getter](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/get)와
+[setter](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/set)가
+있습니다. ES2015의 `class` 문법을 사용하면 훨씬 쉽게 getter와 setter를 설정할 수 있죠.
+
+### 화살표 함수(arrow function) 활용하기
+
+화살표 함수는 특히 콜백과 프로미스를 처리할 때 유용합니다.
+
+```js
+loader.load((texture) => {
+  // 불러온 텍스처를 사용
+});
+```
+
+화살표 함수는 `this` 값을 지정하지 않습니다.
+
+(※ 원문에서는 "Arrow functions bind `this`.(화살표 함수는 `this`를 바인딩한다.)"라고 적었지만,
+표준에 기재된 바 화살표 함수는 익명 함수로 `this`, `arguments`, `super` 또는 `new.target`을 지정하지
+않습니다. 혼돈을 막기 위해 달리 번역하였으니 참고 바랍니다. 역주. 출처: [MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/%EC%95%A0%EB%A1%9C%EC%9A%B0_%ED%8E%91%EC%85%98))
+
+```js
+const foo = (args) => {/* code */};
+```
+
+위 예제는 다음과 과 같죠.
+
+```js
+const foo = (function(args) {/* code */}).bind(this));
+```
+
+### 프로미스(Promise)와 async/await
+
+프로미스는 비동기 처리를 도와줍니다. async/await는 프로미스를 좀 더 쉽게 쓰도록 도와주죠.
+
+이 둘은 짧게 다루기 어려우므로 다른 글([프로미스](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Using_promises),
+[async/await](https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Async_await))을
+참고하기 바랍니다.
+
+### 템플릿 리터럴(Template Literals)
+
+템플릿 리터럴은 따옴표 대신 백틱(backticks, 억음 부호)을 사용한 문자열입니다.
+
+    const foo = `템플릿 리터럴 문자열입니다`;
+
+템플릿 리터럴은 문자열에 2가지 기능을 더한 것인데요. 하나는 여러 줄에 걸쳐 쓸 수
+있는 기능입니다.
+
+```js
+const foo = `템플릿
+리터럴
+문자열입니다`;
+const bar = "템플릿\n리터럴\n문자열입니다";
+```
+
+예제의 `foo`와 `bar`는 같은 문자열이죠.
+
+다른 하나는 문자열 중간에 자바스크립트 표현식을 끼워넣을 수 있는 기능으로,
+`${ 자바스크립트 표현식 }`처럼 사용합니다.
+
+```js
+const r = 192;
+const g = 255;
+const b = 64;
+const rgbCSSColor = `rgb(${r},${g},${b})`;
+```
+
+함수를 실행하거나
+
+```js
+const color = [ 192, 255, 64 ];
+const rgbCSSColor = `rgb(${ color.join(',') })`;
+```
+
+계산식을 넣을 수도 있습니다.
+
+```js
+const aWidth = 10;
+const bWidth = 20;
+someElement.style.width = `${ aWidth + bWidth }px`;
+```
+
+# 자바스크립트 네이밍 컨벤션(coding convection, 코딩 스타일 규약) 지키기
+
+코드를 작성하는 것은 여러분의 자유지만, 웬만하면 한 가지 규약은 지키는 것이 좋습니다.
+자바스크립트의 변수, 메서드, 함수 이름은 전부 lowerCamelCase이죠. 생성자나, 클래스의
+이름은 UpperCamelCase입니다. 이 규약만 따른다면 자바스크립트의 세계에서 크게 문제될
+것은 없죠. 대부분의 [린터(linter)](https://eslint.org)나 린팅 프로그램이 위 네이밍
+컨벤션을 지키지 않는 코드에 대해 에러나 경고를 던집니다.
+
+(※ 문서에 따라 lower camel case는 camelCase, upper camel case는 PascalCase로 부르기도
+합니다. 역주.)
+
+```js
+const v = new vector(); // 모든 클래스가 대문자로 시작할 경우 에러
+const v = Vector();     // 모든 함수가 소문자로 시작할 경우 에러
+```
+
+# Visual Studio Code 사용해보기
+
+이 세상에는 훌륭한 에디터가 많습니다. 자신이 좋아하는 에디터를 쓰는 것이 가장
+좋죠. 하지만 아직 마음에 드는 자바스크립트 에디터가 없다면 [Visual Studio Code](https://code.visualstudio.com/)를
+써보세요! 에디터를 설치하고 [eslint를 설정](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
+하면-환경에 따라 시간이 좀 걸릴지 모르나-무수한 자바스크립트 코드의 버그를
+잡는 데 도움이 될 겁니다.
+
+예를 들어 [`no-undef` 규칙](https://eslint.org/docs/rules/no-undef)을 활성화하면
+VSCode는 ESLint를 통해 변수의 값이 지정되지 않은 경우 경고를 출력할 겁니다.
+
+<div class="threejs_center"><img style="width: 615px;" src="resources/images/vscode-eslint-not-defined.png"></div>
+
+위 예시에서 작성자는 `doTheThing` 함수를 `doThing`로 잘못 적었습니다. 그러자 `doThing`
+아래에 밑줄이 생겼죠. 그리고 그 위에 커서를 올리면 해당 함수를 선언하지 않았다고
+알려줍니다. 미리 에러를 하나 줄인 셈이죠.
+
+`THREE`를 쓸 때도 경고가 나타날 것이므로 코드 상단에 `/* global THREE */`를 선언해
+`THREE`가 미리 선언되었음을 알려줄 수 있습니다.
+
+<div class="threejs_center"><img style="width: 615px;" src="resources/images/vscode-eslint-not-a-constructor.png"></div>
+
+위 이미지에서 ESLint는 `대문자로 시작하는 이름`은 생성자이니 `new` 키워드를 사용하라고
+알려줍니다. [`new-cap` 규칙](https://eslint.org/docs/rules/new-cap) 덕에 에러를 하나
+더 줄였네요.
+
+이 외에도 [설정할 수 있는 규칙](https://eslint.org/docs/rules/)이 거의 100개 정도 더
+있습니다. 아까 `var` 대신 `const`와 `let`을 쓰라고 했었죠.
+
+아래 이미지에서 작성자는 `var`를 썼는데, `let`이나 `const`를 쓰라는 경고를 받았습니다.
+
+<div class="threejs_center"><img style="width: 615px;" src="resources/images/vscode-eslint-var.png"></div>
+
+또 `let`을 쓰고 재할당을 하지 않자 `const`를 쓰라고 제안합니다.
+
+<div class="threejs_center"><img style="width: 615px;" src="resources/images/vscode-eslint-let.png"></div>
+
+물론 `var`가 더 좋다고 생각하면 이 규칙을 꺼버리면 됩니다. 이 규칙을 쓰는 건 제가 성능과
+버그 예방 면에서 `var`보다 `let`과 `const`를 쓰는 걸 더 좋아하기 때문이죠.
+
+일부 파일이나 코드의 일부분에서 규칙을 덮어 씌워야 할 경우, [주석을 추가해 규칙을 끌 수
+있습니다](https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments).
+
+# 구형 브라우저를 지원해야 한다면 트랜스파일러를 쓰세요
+
+대부분의 모던 브라우저가 자동 업데이트 기능을 사용하기에 최신 문법을 사용해 생산성과
+버그 예방 두 마리의 토끼를 동시에 잡을 수 있습니다. 만약 꼭 구형 브라우저를 지원해야
+한다면 [ES5/ES2015 등의 최신 문법을 구형 문법으로 변환시켜주는 트랜스파일러](https://babeljs.io)를
+사용하길 권장합니다.

+ 1 - 1
threejs/lessons/kr/threejs-responsive.md

@@ -103,7 +103,7 @@ canvas 요소에는 두 종류의 크기값이 있습니다. 하나는 아까 CS
 크기라고 부르겠습니다.
 
 ```html
-<img src="128x64이미지.jpg" style="width:400px; height:200px">
+<img src="some128x64image.jpg" style="width:400px; height:200px">
 ```
 
 canvas의 원본 크기, 해상도는 드로잉버퍼(drawingbuffer)라고 불립니다.

+ 95 - 133
threejs/lessons/kr/threejs-shadows.md

@@ -8,16 +8,16 @@ TOC: 그림자(Shadows)
 
 ※ 이전 글인 [카메라에 관한 글](threejs-cameras.html)과
 [조명에 관한 글](threejs-lights.html)에서 이 장을 읽는 꼭 필요한 내용을
-다루었으니 꼭 먼저 읽고 오기 바랍니다.
+다루었으니 꼭 먼저 읽고 오기 바랍니다.
 
 
-컴퓨터에서 그림자란 그리 간단한 주제가 아닙니다. 그림자를 구현하는 방법은
+3D 그래픽에서 그림자란 그리 간단한 주제가 아닙니다. 그림자를 구현하는 방법은
 아주 많지만 모두 단점이 있기에 어떤 것이 가장 효율적이라고 말하기 어렵습니다.
 이는 Three.js에서 제공하는 방법도 마찬가지이죠.
 
-Three.js는 기본적으로 *그림자 맵(shadow maps)*을 사용합니다. 그림자 맵
+Three.js는 기본적으로 *그림자 맵(shadow maps)*을 사용합니다. 그림자 맵이란
 *그림자를 만드는 빛의 영향을 받는, 그림자를 드리우는 모든 물체를 빛의 시점에서
-렌더링*하는 방식을 말합니다. 중요하니 **다시 한 번 읽어보세요!**
+렌더링*하는 기법을 말합니다. 중요하니 **한 번 더 읽어보세요!**
 
 다시 말해, 공간 안에 20개의 물체와 5개의 조명이 있고, 20개의 물체 모두
 그림자를 드리우며 5개의 조명 모두 그림자를 지게 한다면, 한 장면을 만들기
@@ -114,27 +114,27 @@ const planeSize = 1;
 const shadowGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
 ```
 
-이제 구체를 아주 많이 만들겠습니다. 각각 구체마다 `기초` 역할을 할
+이제 구체를 아주 많이 만들겠습니다. 각각 구체마다 `컨테이너` 역할을 할
 `THREE.Object3D`를 만들고, 그림자 평면 mesh, 구체 mesh를 이
-기초의 자식으로 만듭니다. 이러면 구체와 그림자를 동시에 움직일 수
+컨테이너의 자식으로 만듭니다. 이러면 구체와 그림자를 동시에 움직일 수
 있죠. z-파이팅 현상을 막기 위해 그림자는 땅보다 약간 위에 둡니다.
 또 `depthWrite` 속성을 false로 설정해 그림자끼리 충돌하는 현상을
 막습니다. 이 충돌 현상은 [다른 글](threejs-transparency.html)에서
 더 자세히 이야기할 거예요. 그림자는 빛을 반사하지 않으니 `MeshBasicMaterial`을
 사용합니다.
 
-구체의 색상을 각각 다르게 지정하고, 기초, 구체 mesh, 그림자 mesh와
+구체의 색상을 각각 다르게 지정하고, 컨테이너, 구체 mesh, 그림자 mesh와
 구체의 처음 y축 좌표를 배열에 기록합니다.
 
 ```js
 const numSpheres = 15;
 for (let i = 0; i < numSpheres; ++i) {
-  // 구체와 그림자가 같이 움직이도록 기초(base)를 만듭니다
+  // 구체와 그림자가 같이 움직이도록 컨테이너(base)를 만듭니다
   const base = new THREE.Object3D();
   scene.add(base);
 
   /**
-   * 그림자를 기초에 추가합니다
+   * 그림자를 컨테이너에 추가합니다
    * 주의: 여기서는 각 구체의 투명도를 따로 설정할 수 있도록
    * 재질을 각각 따로 만듬
    */
@@ -150,7 +150,7 @@ for (let i = 0; i < numSpheres; ++i) {
   shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
   base.add(shadowMesh);
 
-  // 구체를 기초에 추가
+  // 구체를 컨테이너에 추가
   const u = i / numSpheres;   // 반복문이 진행됨에 따라 0에서 1사이 값을 지정
   const sphereMat = new THREE.MeshPhongMaterial();
   sphereMat.color.setHSL(u, 1, .75);
@@ -163,8 +163,8 @@ for (let i = 0; i < numSpheres; ++i) {
 }
 ```
 
-조명은 2개를 만들겠습니다. 하나는 `HemisphereLight`, 강도를 2로 설정해 굉장히 밝은
-화면을 만들 겁니다.
+조명은 2개를 만들겠습니다. 하나는 `HemisphereLight`, 강도를 2로 설정해 화면을 아주
+밝게 설정할 겁니다.
 
 ```js
 {
@@ -191,8 +191,8 @@ for (let i = 0; i < numSpheres; ++i) {
 ```
 
 이대로도 렌더링해도 좋지만, 구체들에 애니메이션을 한 번 줘봅시다.
-기초를 움직여 구체, 그림자가 xz축 평면을 따라 움직이게 하고,
-`Math.abs(Math.sin(time))`를 사용해 구체에 위아래로 통통 튀는
+컨테이너를 움직여 구체, 그림자가 xz축 평면을 따라 움직이게 하고,
+`Math.abs(Math.sin(time))`를 사용해 구체에 공처럼 통통 튀는
 애니메이션을 넣어줍니다. 또 그림자 재질의 투명도를 조절해 구체가
 높을수록 그림자가 옅어지도록 합니다.
 
@@ -209,8 +209,8 @@ function render(time) {
     const u = ndx / sphereShadowBases.length;
 
     /**
-     * 기초의 위치를 계산합니다. 구체와 그림자가
-     * 기초에 종속적이므로 위치가 같이 변합니다
+     * 컨테이너의 위치를 계산합니다. 구체와 그림자가
+     * 컨테이너에 종속적이므로 위치가 같이 변합니다
      */
     const speed = time * .2;
     const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
@@ -232,45 +232,42 @@ function render(time) {
 
 {{{example url="../threejs-shadows-fake.html" }}}
 
-In some apps it's common to use a round or oval shadow for everything but
-of course you could also use different shaped shadow textures. You might also
-give the shadow a harder edge. A good example of using this type
-of shadow is [Animal Crossing Pocket Camp](https://www.google.com/search?tbm=isch&q=animal+crossing+pocket+camp+screenshots)
-where you can see each character has a simple round shadow. It's effective and cheap.
-[Monument Valley](https://www.google.com/search?q=monument+valley+screenshots&tbm=isch)
-appears to also use this kind of shadow for the main character.
+물론 다른 모양의 그림자를 사용해야 하는 경우도 있습니다. 그림자의 경계를 분명하게
+하고 싶을 수도 있죠. 하지만 모든 물체의 그림자를 둥글게 표현하는 것이 좋은 경우도
+분명 있습니다. 모든 그림자를 둥글게 표현한 예 중 하나는 [동물의 숲 포켓 캠프](https://www.google.com/search?tbm=isch&q=animal+crossing+pocket+camp+screenshots)입니다.
+자연스럽고 성능면에서도 이득이죠. [Monument Valley](https://www.google.com/search?q=monument+valley+screenshots&tbm=isch)도
+메인 캐릭터에 이런 그림자를 사용한 것으로 보입니다.
 
-So, moving on to shadow maps, there are 3 lights which can cast shadows. The `DirectionalLight`,
-the `PointLight`, and the `SpotLight`.
+이제 그림자 맵을 살펴보겠습니다. 그림자를 드리울 수 있는 조명은 3가지, `DirectionalLight`,
+`PointLight`, `SpotLight`입니다.
 
-Let's start with the `DirectionalLight` with the helper example from [the lights article](threejs-lights.html).
+[조명에 관한 글](threejs-lights.html)에서 썼던 예제로 먼저 `DirectionalLight`부터
+살펴보죠.
 
-The first thing we need to do is turn on shadows in the renderer.
+먼저 renderer의 그림자 맵 옵션을 켜야 합니다.
 
 ```js
 const renderer = new THREE.WebGLRenderer({canvas});
 +renderer.shadowMap.enabled = true;
 ```
 
-Then we also need to tell the light to cast a shadow
+조명도 그림자를 드리우도록 옵션을 활성화합니다.
 
 ```js
 const light = new THREE.DirectionalLight(color, intensity);
 +light.castShadow = true;
 ```
 
-We also need to go to each mesh in the scene and decide if it should
-both cast shadows and/or receive shadows.
+또한 장면(scene) 안 각 mesh에 그림자를 드리울지, 그림자의 영향을 받을지 설정해줘야 합니다.
 
-Let's make the plane (the ground) only receive shadows since we don't
-really care what happens underneath.
+바닥 아래는 굳이 신경 쓸 필요가 없으니 평면(바닥)은 그림자의 영향만 받게 하겠습니다.
 
 ```js
 const mesh = new THREE.Mesh(planeGeo, planeMat);
 mesh.receiveShadow = true;
 ```
 
-For the cube and the sphere let's have them both receive and cast shadows
+정육면체와 구체는 그림자도 드리우고, 영향도 받도록 설정합니다.
 
 ```js
 const mesh = new THREE.Mesh(cubeGeo, cubeMat);
@@ -284,48 +281,47 @@ mesh.castShadow = true;
 mesh.receiveShadow = true;
 ```
 
-And then we run it.
+이제 실행해보죠.
 
 {{{example url="../threejs-shadows-directional-light.html" }}}
 
-What happened? Why are parts of the shadows missing?
+이런, 그림자 일부가 잘려나간 것이 보이나요?
 
-The reason is shadow maps are created by rendering the scene from the point
-of view of the light. In this case there is a camera at the `DirectionalLight`
-that is looking at its target. Just like [the camera's we previously covered](threejs-cameras.html)
-the light's shadow camera defines an area inside of which
-the shadows get rendered. In the example above that area is too small.
+이는 빛의 시점에서 장면을 렌더링해 그림자 맵을 만들기 때문입니다. 위 예제를 예로 들면
+`DirectionalLight`의 위치에 카메라가 있고, 해당 조명의 목표를 바라보는 것이죠. 조명의
+그림자에는 별도의 카메라가 있고, 이전에 [카메라에 관한 글](threejs-cameras.html)에서
+설명한 것처럼 일정 공간 안의 그림자만 렌더링합니다. 위 예제에서는 그 공간이 너무 좁은
+것이죠.
 
-In order to visualize that area we can get the light's shadow camera and add
-a `CameraHelper` to the scene.
+그림자용 카메라를 시각화하기 위해 조명의 그림자 속성에서 카메라를 가져와 `CameraHelper`를
+생성한 뒤, 장면에 추가하겠습니다.
 
 ```js
 const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
 scene.add(cameraHelper);
 ```
 
-And now you can see the area for which shadows are cast and received.
+이제 그림자가 렌더링되는 공간을 확인할 수 있을 겁니다.
 
 {{{example url="../threejs-shadows-directional-light-with-camera-helper.html" }}}
 
-Adjust the target x value back and forth and it should be pretty clear that only
-what's inside the light's shadow camera box is where shadows are drawn.
+target의 x 값을 조정해보면 그림자용 카메라 범위 안에 있는 곳에만 그림자가 보이는
+것을 확인할 수 있을 겁니다.
 
-We can adjust the size of that box by adjusting the light's shadow camera.
+이 공간의 크기는 이 카메라의 속성을 수정해 바꿀 수 있습니다.
 
-Let's add some GUI setting to adjust the light's shadow camera box. Since a
-`DirectionalLight` represents light all going in a parallel direction, the
-`DirectionalLight` uses an `OrthographicCamera` for its shadow camera.
-We went over how an `OrthographicCamera` works in [the previous article about cameras](threejs-cameras.html).
+그림자용 카메라의 속성을 수정하는 GUI를 추가해보죠. `DirectionalLight`는 빛이 평행으로
+나아가므로, `DirectionalLight`는  그림자용 카메라로 `OrthographicCamera`(정사영 카메라)를
+사용합니다. `OrthographicCamera`가 뭔지 잘 기억나지 않는다면, [카메라에 관한 이전 글](threejs-cameras.html)을
+참고하세요.
 
-Recall an `OrthographicCamera` defines
-its box or *view frustum* by its `left`, `right`, `top`, `bottom`, `near`, `far`,
-and `zoom` properties.
+`OrthographicCamera`의 시야는 육면체나 *절두체(frustum)*로 정의한다고 했었죠. `left`,
+`right`, `top`, `bottom`, `near`, `far`, `zoom` 속성을 지정해서요.
 
-Again let's make a helper class for the dat.GUI. We'll make a `DimensionGUIHelper`
-that we'll pass an object and 2 properties. It will present one property that dat.GUI
-can adjust and in response will set the two properties one positive and one negative.
-We can use this to set `left` and `right` as `width` and `up` and `down` as `height`.
+dat.GUI가 쓸 간단한 헬퍼 클래스를 하나 더 만들겠습니다. 이 `DimensionGUIHelper`는
+객체와 속성 이름 2개를 인자로 받아, GUI가 하나의 값을 조정할 때 하나의 값은 양수로,
+다른 값은 음수로 지정합니다. 이렇게 하면 `left`와 `right`값을 `width`로, `up`과
+`down`값을 `height`로 바꾸어 조작할 수 있죠.
 
 ```js
 class DimensionGUIHelper {
@@ -344,8 +340,8 @@ class DimensionGUIHelper {
 }
 ```
 
-We'll also use the `MinMaxGUIHelper` we created in the [camera article](threejs-cameras.html)
-to adjust `near` and `far`.
+또한 [이전 글](threejs-cameras.html)에서 썼던 `MinMaxGUIHelper`를 가져와 `near`와
+`far` 속성을 조작하는 데 사용하겠습니다.
 
 ```js
 const gui = new GUI();
@@ -367,98 +363,72 @@ gui.add(light, 'intensity', 0, 2, 0.01);
 +}
 ```
 
-We tell the GUI to call our `updateCamera` function anytime anything changes.
-Let's write that function to update the light, the helper for the light, the
-light's shadow camera, and the helper showing the light's shadow camera.
+그리고 값이 바뀔 때마다 `updateCamera` 함수를 호출하도록 합니다. 이 함수 안에서는
+조명, 조명 헬퍼, 조명의 그림자용 카메라, 그림자용 카메라의 헬퍼를 업데이트할 거예요.
 
 ```js
 function updateCamera() {
-  // update the light target's matrixWorld because it's needed by the helper
+  // 헬퍼가 가이드라인을 그릴 때 필요한 조명 목표(target)의 matrixWorld를 업데이트 합니다
   light.target.updateMatrixWorld();
   helper.update();
-  // update the light's shadow camera's projection matrix
+  // 그림자용 카메라의 투영 행렬(projection matrix)를 업데이트합니다
   light.shadow.camera.updateProjectionMatrix();
-  // and now update the camera helper we're using to show the light's shadow camera
+  // 그림자용 카메라를 보기 위해 설치한 카메라의 헬퍼를 업데이트합니다
   cameraHelper.update();
 }
 updateCamera();
 ```
 
-And now that we've given the light's shadow camera a GUI we can play with the values.
+이제 그림자용 카메라에 GUI가 생겼으니, 값들을 조정하며 놀아봅시다.
 
 {{{example url="../threejs-shadows-directional-light-with-camera-gui.html" }}}
 
-Set the `width` and `height` to about 30 and you can see the shadows are correct
-and the areas that need to be in shadow for this scene are entirely covered.
+`width`와 `height` 속성을 30 정도로 조정하면 그림자가 있어야 할만한 공간은
+대부분 그림자용 카메라 안에 속할 겁니다.
 
-But this brings up the question, why not just set `width` and `height` to some
-giant numbers to just cover everything? Set the `width` and `height` to 100
-and you might see something like this
+하지만 여기서 의문이 하나 생깁니다. 어째서 `width`와 `height`를 완전 큰 값으로
+설정해 모든 요소를 다 포함하도록 하지 않는 걸까요? `width`와 `height`를 100 정도로
+설정해보세요. 아래와 같은 현상이 나타날 겁니다.
 
 <div class="threejs_center"><img src="resources/images/low-res-shadow-map.png" style="width: 369px"></div>
 
-What's going on with these low-res shadows?!
+왜 그림자의 해상도가 낮아졌을까요?
 
-This issue is yet another shadow related setting to be aware of.
-Shadow maps are textures the shadows get drawn into.
-Those textures have a size. The shadow camera's area we set above is stretched
-across that size. That means the larger area you set, the more blocky your shadows will
-be.
+이는 그림자 관련 설정을 할 때 항상 주의해야하는 부분입니다. 사실 그림자 맵은 그림자가
+포함된 하나의 텍스처입니다. 이 텍스처는 크기가 정해져 있죠. 위 예제에서 카메라의 공간을
+늘리면, 이 텍스처 또한 늘어납니다. 다시 말해 공간을 크게 설정할수록 그림자가 더 각져
+보일 거라는 얘기죠.
 
-You can set the resolution of the shadow map's texture by setting `light.shadow.mapSize.width`
-and `light.shadow.mapSize.height`. They default to 512x512.
-The larger you make them the more memory they take and the slower they are to compute so you want
-to set them as small as you can and still make your scene work. The same is true with the
-light's shadow camera area. Smaller means better looking shadows so make the area as small as you
-can and still cover your scene. Be aware that each user's machine has a maximum texture size
-allowed which is available on the renderer as [`renderer.capabilities.maxTextureSize`](WebGLRenderer.capabilities).
+그림자 맵의 해상도는 `light.shadow.mapSize` 속성의 `width`와 `height` 속성으로 설정합니다(기본값은
+512x512). 그림자 맵은 크게 설정할수록 메모리를 많이 차지하고, 연산이 더 복잡해지므로
+가능한 작게 설정하는 것이 좋습니다. 이는 그림자용 카메라의 공간도 마찬가지죠. 작을 수록
+그림자의 퀄리티가 좋아질 테니 가능한 공간을 작게 설정하는 것이 좋습니다. 또한 기기마다
+렌더링할 수 있는 텍스처의 용량이 정해져 있으니 주의해야 합니다. Three.js에서 이 용량은
+[`renderer.capabilities.maxTextureSize`](WebGLRenderer.capabilities)로 확인할 수 있습니다.
 
-<!--
-Ok but what about `near` and `far` I hear you thinking. Can we set `near` to 0.00001 and far to `100000000`
--->
-
-Switching to the `SpotLight` the light's shadow camera becomes a `PerspectiveCamera`. Unlike the `DirectionalLight`'s shadow camera
-where we could manually set most its settings, `SpotLight`'s shadow camera is controlled by the `SpotLight` itself. The `fov` for the shadow
-camera is directly connected to the `SpotLight`'s `angle` setting.
-The `aspect` is set automatically based on the size of the shadow map.
+`SpotLight`는 그림자용 카메라로 `PerspectiveCamera`(원근 카메라)를 사용합니다. `DirectionalLight`의
+그림자용 카메라는 거의 모든 속성을 직접 변경할 수 있었지만, `SpotLight`의 그림자용 카메라는
+조명 속성의 영향을 받습니다. 카메라의 `fov` 속성은 `SpotLight`의 `angle` 속성과 직접 연결되어
+있죠. `aspect`는 그림자 맵의 크기에 따라 자동으로 정해집니다.
 
 ```js
 -const light = new THREE.DirectionalLight(color, intensity);
 +const light = new THREE.SpotLight(color, intensity);
 ```
 
-and we added back in the `penumbra` and `angle` settings
-from our [article about lights](threejs-lights.html).
+추가로 [이전 글](threejs-lights.html)에서 썼던 `penumbra(반음영)`, `angle` 설정을 가져오겠습니다.
 
 {{{example url="../threejs-shadows-spot-light-with-camera-gui.html" }}}
 
-<!--
-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
-
-{{{example url="../threejs-shadows-spot-light-with-shadow-radius" }}}
--->
+마지막으로 `PointLight`를 살펴보죠. `PointLight`는 모든 방향으로 빛을 발산하기에
+관련 설정은 `near`와 `far` 정도입니다. 그리고 사실 `PointLight`의 그림자는 정육면체의
+각 면에 `SpotLight`를 하나씩, 총 6개의 그림자를 놓은 것과 같습니다. 한 방향에 한
+번씩, 총 6번을 렌더링해야 하니 렌더링 속도가 훨씬 느리겠죠.
 
-
-And finally there's shadows with a `PointLight`. Since a `PointLight`
-shines in all directions the only relevant settings are `near` and `far`.
-Otherwise the `PointLight` shadow is effectively 6 `SpotLight` shadows
-each one pointing to the face of a cube around the light. This means
-`PointLight` shadows are much slower since the entire scene must be
-drawn 6 times, one for each direction.
-
-Let's put a box around our scene so we can see shadows on the walls
-and ceiling. We'll set the material's `side` property to `THREE.BackSide`
-so we render the inside of the box instead of the outside. Like the floor
-we'll set it only to receive shadows. Also we'll set the position of the
-box so its bottom is slightly below the floor so the floor and the bottom
-of the box don't z-fight.
+이번에는 장면 주위에 상자를 두어 벽과 천장에도 그림자가 생길 수 있도록 해보겠습니다.
+먼저 재질의 `side` 속성을 `THREE.BackSide`로 설정해 외부 상자의 밖이 아닌 안을 렌더링
+하도록 합니다. 바닥과 마찬가지로 그림자를 드리우지 않도록 설정하고, z-파이팅 현상을
+피하기 위해 외부 상자를 바닥보다 살짝 아래에 둡니다.
 
 ```js
 {
@@ -475,7 +445,7 @@ of the box don't z-fight.
 }
 ```
 
-And of course we need to switch the light to a `PointLight`.
+물론 조명도 `PointLight`로 바꿔야죠.
 
 ```js
 -const light = new THREE.SpotLight(color, intensity);
@@ -483,21 +453,13 @@ And of course we need to switch the light to a `PointLight`.
 
 ....
 
-// so we can easily see where the point light is
+// 조명이 위치를 확인하기 쉽도록 헬퍼 추가
 +const helper = new THREE.PointLightHelper(light);
 +scene.add(helper);
 ```
 
 {{{example url="../threejs-shadows-point-light.html" }}}
 
-Use the `position` GUI settings to move the light around
-and you'll see the shadows fall on all the walls. You can
-also adjust `near` and `far` settings and see just like
-the other shadows when things are closer than `near` they
-no longer receive a shadow and they are further than `far`
-they are always in shadow.
-
-<!--
-self shadow, shadow acne
--->
-
+GUI의 `position` 속성을 조정해 조명을 움직이면 벽에 그림자가 지는 걸
+확인할 수 있을 겁니다. 다른 그림자와 마찬가지로 `near` 값보다 가까운
+곳은 그림자가 지지 않고, `far` 값보다 먼 곳은 항상 그림자가 지죠.