SeemsPyo 5 years ago
parent
commit
9259b42980

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

@@ -1,6 +1,6 @@
 Title: Three.js 기초
 Title: Three.js 기초
 
 
-Three.js 입문자를 위한 튜토리얼입니다.
+Three.js 입문자를 위한 튜토리얼 시리즈입니다.
 
 
 {{{include "threejs/lessons/toc.html"}}}
 {{{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
 ```html
-<img src="128x64이미지.jpg" style="width:400px; height:200px">
+<img src="some128x64image.jpg" style="width:400px; height:200px">
 ```
 ```
 
 
 canvas의 원본 크기, 해상도는 드로잉버퍼(drawingbuffer)라고 불립니다.
 canvas의 원본 크기, 해상도는 드로잉버퍼(drawingbuffer)라고 불립니다.

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

@@ -8,16 +8,16 @@ TOC: 그림자(Shadows)
 
 
 ※ 이전 글인 [카메라에 관한 글](threejs-cameras.html)과
 ※ 이전 글인 [카메라에 관한 글](threejs-cameras.html)과
 [조명에 관한 글](threejs-lights.html)에서 이 장을 읽는 꼭 필요한 내용을
 [조명에 관한 글](threejs-lights.html)에서 이 장을 읽는 꼭 필요한 내용을
-다루었으니 꼭 먼저 읽고 오기 바랍니다.
+다루었으니 꼭 먼저 읽고 오기 바랍니다.
 
 
 
 
-컴퓨터에서 그림자란 그리 간단한 주제가 아닙니다. 그림자를 구현하는 방법은
+3D 그래픽에서 그림자란 그리 간단한 주제가 아닙니다. 그림자를 구현하는 방법은
 아주 많지만 모두 단점이 있기에 어떤 것이 가장 효율적이라고 말하기 어렵습니다.
 아주 많지만 모두 단점이 있기에 어떤 것이 가장 효율적이라고 말하기 어렵습니다.
 이는 Three.js에서 제공하는 방법도 마찬가지이죠.
 이는 Three.js에서 제공하는 방법도 마찬가지이죠.
 
 
-Three.js는 기본적으로 *그림자 맵(shadow maps)*을 사용합니다. 그림자 맵
+Three.js는 기본적으로 *그림자 맵(shadow maps)*을 사용합니다. 그림자 맵이란
 *그림자를 만드는 빛의 영향을 받는, 그림자를 드리우는 모든 물체를 빛의 시점에서
 *그림자를 만드는 빛의 영향을 받는, 그림자를 드리우는 모든 물체를 빛의 시점에서
-렌더링*하는 방식을 말합니다. 중요하니 **다시 한 번 읽어보세요!**
+렌더링*하는 기법을 말합니다. 중요하니 **한 번 더 읽어보세요!**
 
 
 다시 말해, 공간 안에 20개의 물체와 5개의 조명이 있고, 20개의 물체 모두
 다시 말해, 공간 안에 20개의 물체와 5개의 조명이 있고, 20개의 물체 모두
 그림자를 드리우며 5개의 조명 모두 그림자를 지게 한다면, 한 장면을 만들기
 그림자를 드리우며 5개의 조명 모두 그림자를 지게 한다면, 한 장면을 만들기
@@ -114,27 +114,27 @@ const planeSize = 1;
 const shadowGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
 const shadowGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
 ```
 ```
 
 
-이제 구체를 아주 많이 만들겠습니다. 각각 구체마다 `기초` 역할을 할
+이제 구체를 아주 많이 만들겠습니다. 각각 구체마다 `컨테이너` 역할을 할
 `THREE.Object3D`를 만들고, 그림자 평면 mesh, 구체 mesh를 이
 `THREE.Object3D`를 만들고, 그림자 평면 mesh, 구체 mesh를 이
-기초의 자식으로 만듭니다. 이러면 구체와 그림자를 동시에 움직일 수
+컨테이너의 자식으로 만듭니다. 이러면 구체와 그림자를 동시에 움직일 수
 있죠. z-파이팅 현상을 막기 위해 그림자는 땅보다 약간 위에 둡니다.
 있죠. z-파이팅 현상을 막기 위해 그림자는 땅보다 약간 위에 둡니다.
 또 `depthWrite` 속성을 false로 설정해 그림자끼리 충돌하는 현상을
 또 `depthWrite` 속성을 false로 설정해 그림자끼리 충돌하는 현상을
 막습니다. 이 충돌 현상은 [다른 글](threejs-transparency.html)에서
 막습니다. 이 충돌 현상은 [다른 글](threejs-transparency.html)에서
 더 자세히 이야기할 거예요. 그림자는 빛을 반사하지 않으니 `MeshBasicMaterial`을
 더 자세히 이야기할 거예요. 그림자는 빛을 반사하지 않으니 `MeshBasicMaterial`을
 사용합니다.
 사용합니다.
 
 
-구체의 색상을 각각 다르게 지정하고, 기초, 구체 mesh, 그림자 mesh와
+구체의 색상을 각각 다르게 지정하고, 컨테이너, 구체 mesh, 그림자 mesh와
 구체의 처음 y축 좌표를 배열에 기록합니다.
 구체의 처음 y축 좌표를 배열에 기록합니다.
 
 
 ```js
 ```js
 const numSpheres = 15;
 const numSpheres = 15;
 for (let i = 0; i < numSpheres; ++i) {
 for (let i = 0; i < numSpheres; ++i) {
-  // 구체와 그림자가 같이 움직이도록 기초(base)를 만듭니다
+  // 구체와 그림자가 같이 움직이도록 컨테이너(base)를 만듭니다
   const base = new THREE.Object3D();
   const base = new THREE.Object3D();
   scene.add(base);
   scene.add(base);
 
 
   /**
   /**
-   * 그림자를 기초에 추가합니다
+   * 그림자를 컨테이너에 추가합니다
    * 주의: 여기서는 각 구체의 투명도를 따로 설정할 수 있도록
    * 주의: 여기서는 각 구체의 투명도를 따로 설정할 수 있도록
    * 재질을 각각 따로 만듬
    * 재질을 각각 따로 만듬
    */
    */
@@ -150,7 +150,7 @@ for (let i = 0; i < numSpheres; ++i) {
   shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
   shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
   base.add(shadowMesh);
   base.add(shadowMesh);
 
 
-  // 구체를 기초에 추가
+  // 구체를 컨테이너에 추가
   const u = i / numSpheres;   // 반복문이 진행됨에 따라 0에서 1사이 값을 지정
   const u = i / numSpheres;   // 반복문이 진행됨에 따라 0에서 1사이 값을 지정
   const sphereMat = new THREE.MeshPhongMaterial();
   const sphereMat = new THREE.MeshPhongMaterial();
   sphereMat.color.setHSL(u, 1, .75);
   sphereMat.color.setHSL(u, 1, .75);
@@ -163,8 +163,8 @@ for (let i = 0; i < numSpheres; ++i) {
 }
 }
 ```
 ```
 
 
-조명은 2개를 만들겠습니다. 하나는 `HemisphereLight`, 강도를 2로 설정해 굉장히 밝은
-화면을 만들 겁니다.
+조명은 2개를 만들겠습니다. 하나는 `HemisphereLight`, 강도를 2로 설정해 화면을 아주
+밝게 설정할 겁니다.
 
 
 ```js
 ```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 u = ndx / sphereShadowBases.length;
 
 
     /**
     /**
-     * 기초의 위치를 계산합니다. 구체와 그림자가
-     * 기초에 종속적이므로 위치가 같이 변합니다
+     * 컨테이너의 위치를 계산합니다. 구체와 그림자가
+     * 컨테이너에 종속적이므로 위치가 같이 변합니다
      */
      */
     const speed = time * .2;
     const speed = time * .2;
     const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
     const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
@@ -232,45 +232,42 @@ function render(time) {
 
 
 {{{example url="../threejs-shadows-fake.html" }}}
 {{{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
 ```js
 const renderer = new THREE.WebGLRenderer({canvas});
 const renderer = new THREE.WebGLRenderer({canvas});
 +renderer.shadowMap.enabled = true;
 +renderer.shadowMap.enabled = true;
 ```
 ```
 
 
-Then we also need to tell the light to cast a shadow
+조명도 그림자를 드리우도록 옵션을 활성화합니다.
 
 
 ```js
 ```js
 const light = new THREE.DirectionalLight(color, intensity);
 const light = new THREE.DirectionalLight(color, intensity);
 +light.castShadow = true;
 +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
 ```js
 const mesh = new THREE.Mesh(planeGeo, planeMat);
 const mesh = new THREE.Mesh(planeGeo, planeMat);
 mesh.receiveShadow = true;
 mesh.receiveShadow = true;
 ```
 ```
 
 
-For the cube and the sphere let's have them both receive and cast shadows
+정육면체와 구체는 그림자도 드리우고, 영향도 받도록 설정합니다.
 
 
 ```js
 ```js
 const mesh = new THREE.Mesh(cubeGeo, cubeMat);
 const mesh = new THREE.Mesh(cubeGeo, cubeMat);
@@ -284,48 +281,47 @@ mesh.castShadow = true;
 mesh.receiveShadow = true;
 mesh.receiveShadow = true;
 ```
 ```
 
 
-And then we run it.
+이제 실행해보죠.
 
 
 {{{example url="../threejs-shadows-directional-light.html" }}}
 {{{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
 ```js
 const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
 const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
 scene.add(cameraHelper);
 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" }}}
 {{{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
 ```js
 class DimensionGUIHelper {
 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
 ```js
 const gui = new GUI();
 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
 ```js
 function updateCamera() {
 function updateCamera() {
-  // update the light target's matrixWorld because it's needed by the helper
+  // 헬퍼가 가이드라인을 그릴 때 필요한 조명 목표(target)의 matrixWorld를 업데이트 합니다
   light.target.updateMatrixWorld();
   light.target.updateMatrixWorld();
   helper.update();
   helper.update();
-  // update the light's shadow camera's projection matrix
+  // 그림자용 카메라의 투영 행렬(projection matrix)를 업데이트합니다
   light.shadow.camera.updateProjectionMatrix();
   light.shadow.camera.updateProjectionMatrix();
-  // and now update the camera helper we're using to show the light's shadow camera
+  // 그림자용 카메라를 보기 위해 설치한 카메라의 헬퍼를 업데이트합니다
   cameraHelper.update();
   cameraHelper.update();
 }
 }
 updateCamera();
 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" }}}
 {{{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>
 <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
 ```js
 -const light = new THREE.DirectionalLight(color, intensity);
 -const light = new THREE.DirectionalLight(color, intensity);
 +const light = new THREE.SpotLight(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" }}}
 {{{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
 ```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
 ```js
 -const light = new THREE.SpotLight(color, intensity);
 -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);
 +const helper = new THREE.PointLightHelper(light);
 +scene.add(helper);
 +scene.add(helper);
 ```
 ```
 
 
 {{{example url="../threejs-shadows-point-light.html" }}}
 {{{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` 값보다 먼 곳은 항상 그림자가 지죠.