SeemsPyo 5 years ago
parent
commit
56bfd78316
2 changed files with 657 additions and 80 deletions
  1. 581 0
      threejs/lessons/kr/threejs-lights.md
  2. 76 80
      threejs/lessons/kr/threejs-textures.md

+ 581 - 0
threejs/lessons/kr/threejs-lights.md

@@ -0,0 +1,581 @@
+Title: Three.js의 조명
+Description: 조명에 대해 알아봅니다
+TOC: 조명(Lights)
+
+※ 이 글은 Three.js의 튜토리얼 시리즈로서,
+먼저 [Three.js의 기본 구조에 관한 글](threejs-fundamentals.html)과
+[개발 환경 설정하는 법](threejs-setup.html)을 읽고 오길 권장합니다.
+
+이전 글은 [텍스처에 관한 글](threejs-textures.html)이었죠. 이번에는
+Three.js의 다양한 조명을 어떻게 쓰는지 알아보겠습니다.
+
+먼저 이전 예제에서 카메라를 수정하겠습니다. 시야각(fov, field of view)은
+45도, `far`면은 100칸, 카메라의 위치는 중점에서 위로 10칸, 뒤로 20칸 옮깁니다.
+
+```js
+*const fov = 45;
+const aspect = 2;  // canvas 요소의 기본 비율
+const near = 0.1;
+*const far = 100;
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
++camera.position.set(0, 10, 20);
+```
+
+다음으로 `OrbitControls`를 추가합니다. `OrbitControls`는 특정 좌표를
+중심으로 카메라를 자전 또는 *공전(orbit)*하도록 해줍니다. `OrbitControls`는
+별도 모듈이므로, 먼저 페이지에 로드해야 합니다.
+
+```js
+import * as THREE from './resources/three/r115/build/three.module.js';
++import { OrbitControls } from './resources/threejs/r115/examples/jsm/controls/OrbitControls.js';
+```
+
+이제 `OrbitControls`에 카메라와, DOM 이벤트를 감지할 수 있도록
+canvas 요소를 넘겨줍니다.
+
+```js
+const controls = new OrbitControls(camera, canvas);
+controls.target.set(0, 5, 0);
+controls.update();
+```
+
+또한 시점을 중점에서 위로 5칸 올린 후 `controls.update` 메서드를
+호출해 `OrbitControls`가 새로운 시점을 바라보게 합니다.
+
+다음으로 빛을 받을 무언가를 만들어보겠습니다. 먼저 땅의 역할을 할
+평면을 만들고, 평면에 2x2 픽셀의 체크판 텍스처를 씌우겠습니다.
+
+<div class="threejs_center">
+  <img src="../resources/images/checker.png" class="border" style="
+    image-rendering: pixelated;
+    width: 128px;
+  ">
+</div>
+
+일단 텍스처를 불러온 뒤, 반복하도록 래핑(wrapping)을 설정해줍니다. 필터는
+`NearestFilter`, 텍스처가 2x2 픽셀의 체크판이니 `repeat` 속성을 평면의
+반으로 설정하면 체크판의 각 칸은 정확히 (장면의) 1칸이 될 겁니다.
+
+```js
+const planeSize = 40;
+
+const loader = new THREE.TextureLoader();
+const texture = loader.load('resources/images/checker.png');
+texture.wrapS = THREE.RepeatWrapping;
+texture.wrapT = THREE.RepeatWrapping;
+texture.magFilter = THREE.NearestFilter;
+const repeats = planeSize / 2;
+texture.repeat.set(repeats, repeats);
+```
+
+그리고 평면 `geometry`, 평면에 쓸 재질(material), 장면(scene)에 추가할
+`mesh`를 만듭니다. 평면은 기본적으로 XY축을 기준으로 하니, XZ축을 기준으로
+하려면 평면을 회전시켜야 합니다.
+
+```js
+const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
+const planeMat = new THREE.MeshPhongMaterial({
+  map: texture,
+  side: THREE.DoubleSide,
+});
+const mesh = new THREE.Mesh(planeGeo, planeMat);
+mesh.rotation.x = Math.PI * -.5;
+scene.add(mesh);
+```
+
+정육면체와 구체도 추가해서 평면까지 총 3개의 물체를 추가하도록 하죠.
+
+```js
+{
+  const cubeSize = 4;
+  const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
+  const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
+  const mesh = new THREE.Mesh(cubeGeo, cubeMat);
+  mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
+  scene.add(mesh);
+}
+{
+  const sphereRadius = 3;
+  const sphereWidthDivisions = 32;
+  const sphereHeightDivisions = 16;
+  const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
+  const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
+  const mesh = new THREE.Mesh(sphereGeo, sphereMat);
+  mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
+  scene.add(mesh);
+}
+```
+
+빛을 받을 물체를 만들었으니 이제 조명을 추가해보죠!
+
+## `AmbientLight`
+
+먼저 `AmbientLight`를 써보겠습니다.
+
+```js
+const color = 0xFFFFFF;
+const intensity = 1;
+const light = new THREE.AmbientLight(color, intensity);
+scene.add(light);
+```
+
+Let's also make it so we can adjust the light's parameters.
+We'll use [dat.GUI](https://github.com/dataarts/dat.gui) again.
+To be able to adjust the color via dat.GUI we need a small helper
+that presents a property to dat.GUI that looks like a CSS hex color string
+(eg: `#FF8844`). Our helper will get the color from a named property,
+convert it to a hex string to offer to dat.GUI. When dat.GUI tries
+to set the helper's property we'll assign the result back to the light's
+color.
+
+Here's the helper:
+
+```js
+class ColorGUIHelper {
+  constructor(object, prop) {
+    this.object = object;
+    this.prop = prop;
+  }
+  get value() {
+    return `#${this.object[this.prop].getHexString()}`;
+  }
+  set value(hexString) {
+    this.object[this.prop].set(hexString);
+  }
+}
+```
+
+And here's our code setting up dat.GUI
+
+```js
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'intensity', 0, 2, 0.01);
+```
+
+And here's the result
+
+{{{example url="../threejs-lights-ambient.html" }}}
+
+Click and drag in the scene to *orbit* the camera.
+
+Notice there is no definition. The shapes are flat. The `AmbientLight` effectively
+just multiplies the material's color by the light's color times the
+intensity.
+
+    color = materialColor * light.color * light.intensity;
+
+That's it. It has no direction.
+This style of ambient lighting is actually not all that
+useful as lighting as it's 100% even so other than changing the color
+of everything in the scene it doesn't look much like *lighting*.
+What it does help with is making the darks not too dark.
+
+## `HemisphereLight`
+
+Let's switch the code to a `HemisphereLight`. A `HemisphereLight`
+takes a sky color and a ground color and just multiplies the
+material's color between those 2 colors—the sky color if the
+surface of the object is pointing up and the ground color if
+the surface of the object is pointing down.
+
+Here's the new code
+
+```js
+-const color = 0xFFFFFF;
++const skyColor = 0xB1E1FF;  // light blue
++const groundColor = 0xB97A20;  // brownish orange
+const intensity = 1;
+-const light = new THREE.AmbientLight(color, intensity);
++const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
+scene.add(light);
+```
+
+Let's also update the dat.GUI code to edit both colors
+
+```js
+const gui = new GUI();
+-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
++gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('skyColor');
++gui.addColor(new ColorGUIHelper(light, 'groundColor'), 'value').name('groundColor');
+gui.add(light, 'intensity', 0, 2, 0.01);
+```
+
+The result:
+
+{{{example url="../threejs-lights-hemisphere.html" }}}
+
+Notice again there is almost no definition, everything looks kind
+of flat. The `HemisphereLight` used in combination with another light
+can help give a nice kind of influence of the color of the sky
+and ground. In that way it's best used in combination with some
+other light or a substitute for an `AmbientLight`.
+
+## `DirectionalLight`
+
+Let's switch the code to a `DirectionalLight`.
+A `DirectionalLight` is often used to represent the sun.
+
+```js
+const color = 0xFFFFFF;
+const intensity = 1;
+const light = new THREE.DirectionalLight(color, intensity);
+light.position.set(0, 10, 0);
+light.target.position.set(-5, 0, 0);
+scene.add(light);
+scene.add(light.target);
+```
+
+Notice that we had to add the `light` and the `light.target`
+to the scene. A three.js `DirectionalLight` will shine
+in the direction of its target.
+
+Let's make it so we can move the target by adding it to
+our GUI.
+
+```js
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'intensity', 0, 2, 0.01);
+gui.add(light.target.position, 'x', -10, 10);
+gui.add(light.target.position, 'z', -10, 10);
+gui.add(light.target.position, 'y', 0, 10);
+```
+
+{{{example url="../threejs-lights-directional.html" }}}
+
+It's kind of hard to see what's going on. Three.js has a bunch
+of helper objects we can add to our scene to help visualize
+invisible parts of a scene. In this case we'll use the
+`DirectionalLightHelper` which will draw a plane, to represent
+the light, and a line from the light to the target. We just
+pass it the light and add it to the scene.
+
+```js
+const helper = new THREE.DirectionalLightHelper(light);
+scene.add(helper);
+```
+
+While we're at it let's make it so we can set both the position
+of the light and the target. To do this we'll make a function
+that given a `Vector3` will adjust its `x`, `y`, and `z` properties
+using `dat.GUI`.
+
+```js
+function makeXYZGUI(gui, vector3, name, onChangeFn) {
+  const folder = gui.addFolder(name);
+  folder.add(vector3, 'x', -10, 10).onChange(onChangeFn);
+  folder.add(vector3, 'y', 0, 10).onChange(onChangeFn);
+  folder.add(vector3, 'z', -10, 10).onChange(onChangeFn);
+  folder.open();
+}
+```
+
+Note that we need to call the helper's `update` function
+anytime we change something so the helper knows to update
+itself. As such we pass in an `onChangeFn` function to
+get called anytime dat.GUI updates a value.
+
+Then we can use that for both the light's position
+and the target's position like this
+
+```js
++function updateLight{
++  light.target.updateMatrixWorld();
++  helper.update();
++}
++updateLight();
+
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'intensity', 0, 2, 0.01);
+
++makeXYZGUI(gui, light.position, 'position', updateLight);
++makeXYZGUI(gui, light.target.position, 'target', updateLight);
+```
+
+Now we can move the light, and its target
+
+{{{example url="../threejs-lights-directional-w-helper.html" }}}
+
+Orbit the camera and it gets easier to see. The plane
+represents a `DirectionalLight` because a directional
+light computes light coming in one direction. There is no
+*point* the light comes from, it's an infinite plane of light
+shooting out parallel rays of light.
+
+## `PointLight`
+
+A `PointLight` is a light that sits at a point and shoots light
+in all directions from that point. Let's change the code.
+
+```js
+const color = 0xFFFFFF;
+const intensity = 1;
+-const light = new THREE.DirectionalLight(color, intensity);
++const light = new THREE.PointLight(color, intensity);
+light.position.set(0, 10, 0);
+-light.target.position.set(-5, 0, 0);
+scene.add(light);
+-scene.add(light.target);
+```
+
+Let's also switch to a `PointLightHelper`
+
+```js
+-const helper = new THREE.DirectionalLightHelper(light);
++const helper = new THREE.PointLightHelper(light);
+scene.add(helper);
+```
+
+and as there is no target the `onChange` function can be simpler.
+
+```js
+function updateLight{
+-  light.target.updateMatrixWorld();
+  helper.update();
+}
+-updateLight();
+```
+
+Note that at some level a `PointLightHelper` has no um, point.
+It just draws a small wireframe diamond. It could just as easily
+be any shape you want, just add a mesh to the light itself.
+
+A `PointLight` has the added property of [`distance`](PointLight.distance).
+If the `distance` is 0 then the `PointLight` shines to
+infinity. If the `distance` is greater than 0 then the light shines
+its full intensity at the light and fades to no influence at `distance`
+units away from the light.
+
+Let's setup the GUI so we can adjust the distance.
+
+```js
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'intensity', 0, 2, 0.01);
++gui.add(light, 'distance', 0, 40).onChange(updateLight);
+
+makeXYZGUI(gui, light.position, 'position', updateLight);
+-makeXYZGUI(gui, light.target.position, 'target', updateLight);
+```
+
+And now try it out.
+
+{{{example url="../threejs-lights-point.html" }}}
+
+Notice when `distance` is > 0 how the light fades out.
+
+## `SpotLight`
+
+Spotlights are effectively a point light with a cone
+attached where the light only shines inside the cone.
+There's actually 2 cones. An outer cone and an inner
+cone. Between the inner cone and the outer cone the
+light fades from full intensity to zero.
+
+To use a `SpotLight` we need a target just like
+the directional light. The light's cone will
+open toward the target.
+
+Modifying our `DirectionalLight` with helper from above
+
+```js
+const color = 0xFFFFFF;
+const intensity = 1;
+-const light = new THREE.DirectionalLight(color, intensity);
++const light = new THREE.SpotLight(color, intensity);
+scene.add(light);
+scene.add(light.target);
+
+-const helper = new THREE.DirectionalLightHelper(light);
++const helper = new THREE.SpotLightHelper(light);
+scene.add(helper);
+```
+
+The spotlight's cone's angle is set with the [`angle`](SpotLight.angle)
+property in radians. We'll use our `DegRadHelper` from the
+[texture article](threejs-textures.html) to present a UI in
+degrees.
+
+```js
+gui.add(new DegRadHelper(light, 'angle'), 'value', 0, 90).name('angle').onChange(updateLight);
+```
+
+The inner cone is defined by setting the [`penumbra`](SpotLight.penumbra) property
+as a percentage from the outer cone. In other words when `penumbra` is 0 then the
+inner code is the same size (0 = no difference) from the outer cone. When the
+`penumbra` is 1 then the light fades starting in the center of the cone to the
+outer cone. When `penumbra` is .5 then the light fades starting from 50% between
+the center of the outer cone.
+
+```js
+gui.add(light, 'penumbra', 0, 1, 0.01);
+```
+
+{{{example url="../threejs-lights-spot-w-helper.html" }}}
+
+Notice with the default `penumbra` of 0 the spotlight has a very sharp edge
+whereas as you adjust the `penumbra` toward 1 the edge blurs.
+
+It might be hard to see the *cone* of the spotlight. The reason is it's
+below the ground. Shorten the distance to around 5 and you'll see the open
+end of the cone.
+
+## `RectAreaLight`
+
+There's one more type of light, the `RectAreaLight`, which represents
+exactly what it sounds like, a rectangular area of light like a long
+fluorescent light or maybe a frosted sky light in a ceiling.
+
+The `RectAreaLight` only works with the `MeshStandardMaterial` and the
+`MeshPhysicalMaterial` so let's change all our materials to `MeshStandardMaterial`
+
+```js
+  ...
+
+  const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
+-  const planeMat = new THREE.MeshPhongMaterial({
++  const planeMat = new THREE.MeshStandardMaterial({
+    map: texture,
+    side: THREE.DoubleSide,
+  });
+  const mesh = new THREE.Mesh(planeGeo, planeMat);
+  mesh.rotation.x = Math.PI * -.5;
+  scene.add(mesh);
+}
+{
+  const cubeSize = 4;
+  const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
+- const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
++ const cubeMat = new THREE.MeshStandardMaterial({color: '#8AC'});
+  const mesh = new THREE.Mesh(cubeGeo, cubeMat);
+  mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
+  scene.add(mesh);
+}
+{
+  const sphereRadius = 3;
+  const sphereWidthDivisions = 32;
+  const sphereHeightDivisions = 16;
+  const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
+-  const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
++ const sphereMat = new THREE.MeshStandardMaterial({color: '#CA8'});
+  const mesh = new THREE.Mesh(sphereGeo, sphereMat);
+  mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
+  scene.add(mesh);
+}
+```
+
+To use the `RectAreaLight` we need to include some extra three.js optional data and we'll
+include the `RectAreaLightHelper` to help us visualize the light
+
+```js
+import * as THREE from './resources/three/r115/build/three.module.js';
++import {RectAreaLightUniformsLib} from './resources/threejs/r115/examples/jsm/lights/RectAreaLightUniformsLib.js';
++import {RectAreaLightHelper} from './resources/threejs/r115/examples/jsm/helpers/RectAreaLightHelper.js';
+```
+
+and we need to call `RectAreaLightUniformsLib.init`
+
+```js
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas});
++  RectAreaLightUniformsLib.init();
+```
+
+If you forget the data the light will still work but it will look funny so
+be sure to remember to include the extra data.
+
+Now we can create the light
+
+```js
+const color = 0xFFFFFF;
+*const intensity = 5;
++const width = 12;
++const height = 4;
+*const light = new THREE.RectAreaLight(color, intensity, width, height);
+light.position.set(0, 10, 0);
++light.rotation.x = THREE.MathUtils.degToRad(-90);
+scene.add(light);
+
+*const helper = new RectAreaLightHelper(light);
+*light.add(helper);
+```
+
+One thing to notice is that unlike the `DirectionalLight` and the `SpotLight`, the
+`RectAreaLight` does not use a target. It just uses its rotation. Another thing
+to notice is the helper needs to be a child of the light. It is not a child of the
+scene like other helpers.
+
+Let's also adjust the GUI. We'll make it so we can rotate the light and adjust
+its `width` and `height`
+
+```js
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'intensity', 0, 10, 0.01);
+gui.add(light, 'width', 0, 20).onChange(updateLight);
+gui.add(light, 'height', 0, 20).onChange(updateLight);
+gui.add(new DegRadHelper(light.rotation, 'x'), 'value', -180, 180).name('x rotation').onChange(updateLight);
+gui.add(new DegRadHelper(light.rotation, 'y'), 'value', -180, 180).name('y rotation').onChange(updateLight);
+gui.add(new DegRadHelper(light.rotation, 'z'), 'value', -180, 180).name('z rotation').onChange(updateLight);
+
+makeXYZGUI(gui, light.position, 'position', updateLight);
+```
+
+And here is that.
+
+{{{example url="../threejs-lights-rectarea.html" }}}
+
+One thing we didn't cover is that there is a setting on the `WebGLRenderer`
+called `physicallyCorrectLights`. It effects how light falls off as distance from light.
+It only affects `PointLight` and `SpotLight`. `RectAreaLight` does this automatically.
+
+For lights though the basic idea is you don't set a distance for them to fade out,
+and you don't set `intensity`. Instead you set the [`power`](PointLight.power) of
+the light in lumens and then three.js will use physics calculations like real lights.
+The units of three.js in this case are meters and a 60w light bulb would have
+around 800 lumens. There's also a [`decay`](PointLight.decay) property. It should
+be set to `2` for realistic decay.
+
+Let's test that.
+
+First we'll turn on physically correct lights
+
+```js
+const renderer = new THREE.WebGLRenderer({canvas});
++renderer.physicallyCorrectLights = true;
+```
+
+Then we'll set the `power` to 800 lumens, the `decay` to 2, and
+the `distance` to `Infinity`.
+
+```js
+const color = 0xFFFFFF;
+const intensity = 1;
+const light = new THREE.PointLight(color, intensity);
+light.power = 800;
+light.decay = 2;
+light.distance = Infinity;
+```
+
+and we'll add gui so we can change the `power` and `decay`
+
+```js
+const gui = new GUI();
+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');
+gui.add(light, 'decay', 0, 4, 0.01);
+gui.add(light, 'power', 0, 2000);
+```
+
+{{{example url="../threejs-lights-point-physically-correct.html" }}}
+
+It's important to note each light you add to the scene slows down how fast
+three.js renders the scene so you should always try to use as few as
+possible to achieve your goals.
+
+Next up let's go over [dealing with cameras](threejs-cameras.html).
+
+<canvas id="c"></canvas>
+<script type="module" src="resources/threejs-lights.js"></script>

+ 76 - 80
threejs/lessons/kr/threejs-textures.md

@@ -6,14 +6,14 @@ TOC: 텍스처(Textures)
 먼저 [Three.js의 기본 구조에 관한 글](threejs-fundamentals.html)과
 [개발 환경 설정하는 법](threejs-setup.html)을 읽고 오길 권장합니다.
 
-※ 텍스처, Texture는 질감으로 번역할 수 있으나, 그대로 표기하는 쪽이
-직관적이라 판단하여 **텍스처**로 번역하였습니다.
+※ 텍스처, Texture는 "질감"으로 번역할 수 있으나, 그대로 표기하는 쪽이
+직관적이라 판단하여 **텍스처**로 번역하였습니다.
 
 
 Three.js에서 텍스처를 이야기하기란 쉽지 않습니다. 텍스처는 워낙 방대한
 주제이고, 각 주제끼리도 서로 연결되어 있어 한 번에 설명하는 것이 거의
-불가능기 때문이죠. 어떻게 설명해야 잘 설명했다고 할 수 있을지 확신은
-없지만, 일단 해보기로 합시다. 다음은 이 글의 간략한 목차입니다.
+불가능에 가깝기 때문이죠. 어떻게 설명해야 잘 설명했다고 할 수 있을지
+확신은 없지만, 일단 해보기로 합시다. 다음은 이 글의 간략한 목차입니다.
 
 <ul>
 <li><a href="#hello">하이, 텍스처</a></li>
@@ -28,7 +28,7 @@ Three.js에서 텍스처를 이야기하기란 쉽지 않습니다. 텍스처는
 <li><a href="#memory">메모리 관리</a></li>
 <li><a href="#format">JPG vs PNG</a></li>
 <li><a href="#filtering-and-mips">필터링과 Mips</a></li>
-<li><a href="#uvmanipulation">반복하기, 파생하기, 회전하기, 감싸기</a></li>
+<li><a href="#uvmanipulation">텍스처의 반복(repeating), 위치 조절(offseting), 회전(rotating), 래핑(wrapping)</a></li>
 </ul>
 
 ## <a name="hello"></a> 하이, 텍스처
@@ -274,14 +274,14 @@ loadManager.onLoad = () => {
 
 이 이미지는 매우 고 배율로 압축되어 157kb 밖에 되지 않습니다. 상대적으로
 다운 속도는 빠를 것이나, 이 [이미지의 실제 크기는 3024 x 3761 픽셀입니다](resources/images/compressed-but-large-wood-texture.jpg).
-위 공식에 따르면 이 이미지를 적용해보면,
+위 공식에 따 이 이미지를 적용해보면,
 
     3024 * 3761 * 4 * 1.33 = 60505764.5
 
 무려 **약 60 메가바이트의 메모리**를 사용합니다. 이런 텍스처가 몇 개만 더
 있어도 메모리 부족으로 앱을 사용하지 못할 수 있죠(OUT_OF_MEMORY).
 
-극단적인 예제이기는 하나 이 예제는 텍스처를 사용하는데 숨겨진 비용을 고려해야
+극단적인 예제이기는 하나, 이 예제는 텍스처를 사용하는데 숨겨진 비용을 고려해야
 한다는 것을 잘 알려줍니다. Three.js가 텍스처를 사용하려면 GPU에 텍스처를
 넘겨주어야 하는데, GPU는 *일반적으로* 압축하지 않은 데이터를 사용하죠.
 
@@ -323,13 +323,13 @@ GPU는 작은 정육면체를 표현할 때 어떻게 각 픽셀의 색상을 
 
 이게 바로 필터링(filtering)이 있는 이유입니다.
 
-포토샵의 경우는 근처 픽셀의 평균을 내 해당 1, 2 픽셀의 형태를 결정할 겁니다.
+포토샵이라면 근처 픽셀의 평균을 내 해당 1, 2 픽셀의 형태를 결정할 겁니다.
 이는 매우 무거운 작업이죠. GPU는 이 문제를 해결하기 위해 [밉맵(mipmaps)](https://ko.wikipedia.org/wiki/%EB%B0%89%EB%A7%B5)을
 사용합니다.
 
 밉(mips)은 텍스처의 복사본으로, 각 밉은 축소된 이전 밉보다 반만큼 작습니다.
-밉은 1x1 픽셀 밉을 생성할 때까지 계속 생성되죠. 위 이미지의 경우 밉은 다음
-같이 생성됩니다.
+밉은 1x1 픽셀 밉을 생성할 때까지 계속 생성되죠. 위 이미지의 경우 밉은 다음처럼
+생성될 겁니다.
 
 <div class="threejs_center"><img src="resources/images/mipmap-low-res-enlarged.png" class="nobg" align="center"></div>
 
@@ -359,34 +359,34 @@ Three.js에서는 텍스처의 크기가 원본보다 클 때와 작을 때 각
   </div>
 </div>
 
-For setting the filter when the texture is drawn smaller than its original size
-you set the [`texture.minFilter`](Texture.minFilter) property to one of 6 values.
+텍스처가 원본 크기보다 작을 때의 필터는 [`texture.minFilter`](Texture.minFilter)
+속성을 다음 6가지 값 중 하나로 지정해 사용합니다.
 
 * `THREE.NearestFilter`
 
-   same as above, choose the closest pixel in the texture
+   원본보다 클 때와 마찬가지로 가장 가까운 픽셀을 선택합니다
 
 * `THREE.LinearFilter`
 
-   same as above, choose 4 pixels from the texture and blend them
+   원본보다 클 때와 마찬가지로 주변의 가까운 픽셀 4개를 골라 섞습니다
 
 * `THREE.NearestMipmapNearestFilter`
 
-   choose the appropriate mip then choose one pixel
+   적절한 밉을 고른 뒤 밉에서 픽셀 하나를 선택합니다
 
 * `THREE.NearestMipmapLinearFilter`
 
-   choose 2 mips, choose one pixel from each, blend the 2 pixels
+   두 개의 밉을 골라 픽셀을 하나씩 선택한 후, 두 픽셀을 섞습니다
 
 * `THREE.LinearMipmapNearestFilter`
 
-   chose the appropriate mip then choose 4 pixels and blend them
+   적절한 밉을 고른 뒤 픽셀 4개를 골라 섞습니다
 
 *  `THREE.LinearMipmapLinearFilter`
 
-   choose 2 mips, choose 4 pixels from each and blend all 8 into 1 pixel
+   두 개의 밉을 골라 각각 픽셀을 4개씩 선택하고, 선택한 8개의 픽셀을 하나의 픽셀로 혼합합니다
 
-Here's an example showing all 6 settings
+아래는 6개의 필터를 각각 적용한 예제입니다.
 
 <div class="spread">
   <div data-diagram="filterModes" style="
@@ -409,7 +409,7 @@ Here's an example showing all 6 settings
         border-radius: .5em;
         line-height: 1.2;
         user-select: none;"
-      >click to<br/>change<br/>texture</div>
+      >클릭해<br/>텍스처를<br/>변경</div>
     </div>
     <div class="filter-caption" style="left: 0.5em; top: 0.5em;">nearest</div>
     <div class="filter-caption" style="width: 100%; text-align: center; top: 0.5em;">linear</div>
@@ -420,65 +420,63 @@ Here's an example showing all 6 settings
   </div>
 </div>
 
-One thing to notice is the top left and top middle using `NearestFilter` and `LinearFilter`
-don't use the mips. Because of that they flicker in the distance because the GPU is
-picking pixels from the original texture. On the left just one pixel is chosen and
-in the middle 4 are chosen and blended but it's not enough come up with a good
-representative color. The other 4 strips do better with the bottom right,
-`LinearMipmapLinearFilter` being best.
+여기서 주의깊게 봐야할 건 상단 왼쪽 `NearestFilter`와 상단 중앙의 `LinearFilter`는
+밉을 사용하지 않는다는 점입니다. 두 텍스처를 보면 멀리 떨어질수록 픽셀이 깜빡이는 증상이
+보이죠. 이는 GPU가 픽셀을 원본 텍스처에서 선택하기 때문입니다. `NearestFilter`는 하나의
+픽셀을 선택하고, `LinearFilter`는 4개의 픽셀을 선택하기는 하나, 픽셀을 제대로 표현하진
+못합니다. 다른 4개 예제는 그나마 낫고, 그 중 `LinearMipmapLinearFilter`가 제일 깔끔해
+보이네요.
 
-If you click the picture above it will toggle between the texture we've been using above
-and a texture where every mip level is a different color.
+위 캔버스를 클릭해보면 텍스처를 바꿀 수 있습니다. 하나는 여태까지 사용하던 텍스처이고,
+또 하나는 밉의 각 단계가 다른 색으로 나타나는 텍스처이죠.
 
 <div class="threejs_center">
   <div data-texture-diagram="differentColoredMips"></div>
 </div>
 
-This makes it more clear
-what is happening. You can see in the top left and top middle the first mip is used all the way
-into the distance. The top right and bottom middle you can clearly see where a different mip
-is used.
+이 텍스처는 필터의 동작 원리를 이해하기 쉽도록 해줍니다. 위 예제에서 `NearestFilter`와
+`LinearFilter`는 아주 멀리까지도 첫 번째 밉을 사용합니다. 반면 상단 오른쪽과 하단 중앙을
+보면 밉의 경계가 뚜렷이 보이죠.
 
-Switching back to the original texture you can see the bottom right is the smoothest,
-highest quality. You might ask why not always use that mode. The most obvious reason
-is sometimes you want things to be pixelated for a retro look or some other reason.
-The next most common reason is that reading 8 pixels and blending them is slower
-than reading 1 pixel and blending. While it's unlikely that a single texture is going
-to be the difference between fast and slow as we progress further into these articles
-we'll eventually have materials that use 4 or 5 textures all at once. 4 textures * 8
-pixels per texture is looking up 32 pixels for ever pixel rendered.
-This can be especially important to consider on mobile devices.
+다시 원래 텍스처로 바꿔보면 하단 오른쪽이 가장 매끄러운 것이 보일 겁니다. 왜 항상 이
+필터를 쓰지 않는 걸까요? 뭐, 레트로 감성을 표현한다든가 하는 이유로 물체들이 픽셀화된
+것을 원할 수도 있죠. 하지만 보다 흔한 이유는 성능입니다. 8개의 픽셀 데이터를 처리하는
+것보다는 당연히 1개의 픽셀 데이터를 처리하는 게 훨씬 빠르겠죠. 하나의 텍스처로 이런
+성능 차이를 체감하기는 어렵지만, Three.js를 사용하다보면 하나의 물체에 4, 5개의 텍스처가
+들어가는 경우도 빈번합니다. 4개의 텍스처에서 각각 8개의 픽셀을 처리해야 하니, 이는 한
+프레임당 32개의 픽셀을 처리해야 함을 의미하죠. 이는 저사양 기기를 고려할 때 특히 중요히
+여겨야 하는 요소입니다.
 
-## <a name="uvmanipulation"></a> Repeating, offseting, rotating, wrapping a texture
+## <a name="uvmanipulation"></a> 텍스처의 반복(repeating), 위치 조절(offseting), 회전(rotating), 래핑(wrapping)
 
-Textures have settings for repeating, offseting, and rotating a texture.
+텍스처에는 반복, 위치, 회전 설정이 있습니다.
 
-By default textures in three.js do not repeat. To set whether or not a
-texture repeats there are 2 properties, [`wrapS`](Texture.wrapS) for horizontal wrapping
-and [`wrapT`](Texture.wrapT) for vertical wrapping.
+Three.js는 기본적으로 텍스처를 반복하지 않습니다. 반복 여부를 설정하는
+2가지 속성이 있는데, 하나는 수평 래핑을 설정하는 [`wrapS`](Texture.wrapS)이고,
+또 하나는 수직 래핑을 설정하는 [`wrapT`](Texture.wrapT)입니다.
 
-They can be set to one of:
+두 속성은 다음 중 하나로 지정할 수 있습니다.
 
 * `THREE.ClampToEdgeWrapping`
 
-   the last pixel on each edge is repeated forever
+   텍스처의 가장자리 픽셀을 계속해서 반복합니다
 
 * `THREE.RepeatWrapping`
 
-   the texture is repeated
+   텍스처 자체를 반복합니다
 
 * `THREE.MirroredRepeatWrapping`
 
-   the texture is mirrored and repeated
+   텍스처 자체를 반복하되, 매번 뒤집습니다.
 
-For example to turn on wrapping in both directions:
+양 방향의 래핑을 키려면 다음과 같이 설정할 수 있습니다.
 
 ```js
 someTexture.wrapS = THREE.RepeatWrapping;
 someTexture.wrapT = THREE.RepeatWrapping;
 ```
 
-Repeating is set with the [repeat] repeat property.
+반복은 `repeat` 속성으로 설정할 수 있죠.
 
 ```js
 const timesToRepeatHorizontally = 4;
@@ -486,30 +484,29 @@ const timesToRepeatVertically = 2;
 someTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);
 ```
 
-Offseting the texture can be done by setting the `offset` property. Textures
-are offset with units where 1 unit = 1 texture size. On other words 0 = no offset
-and 1 = offset one full texture amount.
+텍스처의 위치는 `offset` 속성을 설정해 조절할 수 있습니다. 텍스처 위치의 단위는
+텍스처의 크기와 1:1, 즉 0은 위치가 그대로인 것이고 1은 각 축에서 텍스처 크기만큼
+이동한 것을 의미하죠.
 
 ```js
-const xOffset = .5;   // offset by half the texture
-const yOffset = .25;  // offset by 1/4 the texture
+const xOffset = .5;   // 텍스처 너비의 반만큼 이동
+const yOffset = .25;  // 텍스처 높이의 1/4만큼 이동
 someTexture.offset.set(xOffset, yOffset);
 ```
 
-Rotating the texture can be set by setting the `rotation` property in radians
-as well as the `center` property for choosing the center of rotation.
-It defaults to 0,0 which rotates from the bottom left corner. Like offset
-these units are in texture size so setting them to `.5, .5` would rotate
-around the center of the texture.
+텍스처의 회전은 `rotation` 속성을 라디안(radians) 단위로 지정해 조절할 수 있습니다.
+`center` 속성은 회전의 중심을 정하는 데 사용하죠. `center` 속성의 기본값은 `0, 0`으로
+왼쪽 상단을 기준으로 회전하고, `offset`과 마찬가지로 텍스처의 크기를 기준으로
+단위가 정해지기에 `.5, .5`로 설정하면 텍스처의 중앙을 기준으로 회전합니다.
 
 ```js
 someTexture.center.set(.5, .5);
 someTexture.rotation = THREE.MathUtils.degToRad(45);
 ```
 
-Let's modify the top sample above to play with these values
+아까 작성한 예제를 수정해 위 설정을 테스트할 예제를 만들겠습니다.
 
-First we'll keep a reference to the texture so we can manipulate it
+먼저 텍스처를 별도 변수에 담아 나중에 수정할 수 있도록 합니다.
 
 ```js
 +const texture = loader.load('resources/images/wall.jpg');
@@ -519,15 +516,15 @@ const material = new THREE.MeshBasicMaterial({
 });
 ```
 
-Then we'll use [dat.GUI](https://github.com/dataarts/dat.gui) again to provide a simple interface.
+간단한 인터페이스를 만들어보죠.
+다시 한 번 [dat.GUI](https://github.com/dataarts/dat.gui)가 등장할 때입니다.
 
 ```js
-import {GUI} from '../3rdparty/dat.gui.module.js';
+import { GUI } from '../3rdparty/dat.gui.module.js';
 ```
 
-As we did in previous dat.GUI examples we'll use a simple class to
-give dat.GUI an object that it can manipulate in degrees
-but that will set a property in radians.
+이전 예제처럼 간단한 헬퍼 클래스를 만들어 각도(degrees)로 값을 조절하면
+알아서 호도(radians)로 변환해 지정하게끔 해줍니다.
 
 ```js
 class DegRadHelper {
@@ -544,9 +541,9 @@ class DegRadHelper {
 }
 ```
 
-We also need a class that will convert from a string like `"123"` into
-a number like `123` since three.js requires numbers for enum settings
-like `wrapS` and `wrapT` but dat.GUI only uses strings for enums.
+또 문자열을 숫자형으로 변환시켜줄 클래스도 만듭니다. dat.GUI는 값을 문자열로
+넘겨주는데, Three.js는 `wrapS`나 `wrapT` 등 enum 값을 지정할 때 숫자형만
+받기 때문이죠.
 
 ```js
 class StringToNumberHelper {
@@ -563,7 +560,7 @@ class StringToNumberHelper {
 }
 ```
 
-Using those classes we can setup a simple GUI for the settings above
+위에서 만든 클래스를 이용해 설정값을 조절할 GUI를 만듭니다.
 
 ```js
 const wrapModes = {
@@ -593,17 +590,16 @@ gui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)
   .name('texture.rotation');
 ```
 
-The last thing to note about the example is that if you change `wrapS` or
-`wrapT` on the texture you must also set [`texture.needsUpdate`](Texture.needsUpdate)
-so three.js knows to apply those settings. The other settings are automatically applied.
+텍스처의 `wrapS`나 `wrapT` 속성을 변경할 경우 [`texture.needsUpdate`](Texture.needsUpdate)를
+`true`로 설정해줘야 합니다. 나머지 설정만 변경한다면 굳이 이 값을 설정할 필요는 없죠.
 
 {{{example url="../threejs-textured-cube-adjust.html" }}}
 
-This is only one step into the topic of textures. At some point we'll go over
-texture coordinates as well as 9 other types of textures that can be applied
-to materials.
+뭔가 많은 것을 배운 것 같지만, 이는 맛보기에 불과합니다. 글을 진행하다보면
+텍스처의 정렬과 재질에 적용할 수 있는 다른 9가지의 텍스처에 대해 다룰 기회가
+있을 거예요.
 
-For now let's move on to [lights](threejs-lights.html).
+일단 다음 장에서는 [조명(lights)](threejs-lights.html)에 대해 알아보기로 하죠.
 
 <!--
 alpha