Browse Source

update translations r115 to r119

SeemsPyo 5 years ago
parent
commit
e782d5bfd3
2 changed files with 91 additions and 101 deletions
  1. 20 51
      threejs/lessons/kr/threejs-backgrounds.md
  2. 71 50
      threejs/lessons/kr/threejs-offscreencanvas.md

+ 20 - 51
threejs/lessons/kr/threejs-backgrounds.md

@@ -202,63 +202,32 @@ const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
 
 <div class="threejs_center"><img src="../resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg" style="width: 600px"></div>
 
-등장방형도법을 사용하려면 몇 가지를 추가해야 합니다. 별도의 `Scene`과
-`BoxBufferGeometry`를 만들고, 내장 쉐이더를 이용해 `ShaderMaterial`를
-만듭니다. 만든 요소들은 기존 장면을 렌더링하기 전 배경을 렌더링할 때
-사용할 겁니다.
+등장방형도법의 사용법은 별로 다르지 않습니다. 먼저 등장방형도법 이미지를 텍스처로 불러온 뒤, 콜백에서 불러온 이미지 텍스처를 `WebGLCubeRenderTarget.fromEquirectangularTexture`를 호출할 때 넘겨주면 큐브맵(정육면체를 펼친 모양의 텍스처)를 만들 수 있습니다. `WebGLCubeRenderTarget`을 생성할 때 큐브맵의 크기를 지정해주기만 하면 되죠. 예제의 경우 등장방형도법 이미지의 높이를 넘겨주면 될 겁니다.
 
 ```js
-const bgScene = new THREE.Scene();
-let bgMesh;
 {
-  const loader = new THREE.TextureLoader();
-  const texture = loader.load('resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg');
-  texture.magFilter = THREE.LinearFilter;
-  texture.minFilter = THREE.LinearFilter;
-
-  const shader = THREE.ShaderLib.equirect;
-  const material = new THREE.ShaderMaterial({
-    fragmentShader: shader.fragmentShader,
-    vertexShader: shader.vertexShader,
-    uniforms: shader.uniforms,
-    depthWrite: false,
-    side: THREE.BackSide,
-  });
-  material.uniforms.tEquirect.value = texture;
-  const plane = new THREE.BoxBufferGeometry(2, 2, 2);
-  bgMesh = new THREE.Mesh(plane, material);
-  bgScene.add(bgMesh);
+-  const loader = new THREE.CubeTextureLoader();
+-  const texture = loader.load([
+-    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',
+-    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',
+-    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',
+-    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',
+-  ]);
+-  scene.background = texture;
++  const loader = new THREE.TextureLoader();
++  const texture = loader.load(
++    'resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg',
++    () => {
++      const rt = new THREE.WebGLCubeRenderTarget(texture.image.height);
++      rt.fromEquirectangularTexture(renderer, texture);
++      scene.background = rt;
++    });
 }
 ```
 
-상자(BoxBufferGeometry)는 카메라의 `near`보다는 커야 하나, 동시에 `far`보다는
-작아야 합니다.
-
-또 상자의 안이 보여야 하니 `side: THREE.BackSide`로 설정하고, 깊이에 관한
-연산을 하지 않도록 `depthWrite: false`로 설정합니다.
-
-렌더링 시 배경 상자와 기존 카메라가 같은 위치에 있도록 설정하고, 배경용 장면을
-렌더링합니다.
-
-```js
-function render(time)
-
-    /* ... */
-
-+    bgMesh.position.copy(camera.position);
-+    renderer.render(bgScene, bgCamera);
-    renderer.render(scene, camera);
-```
-
-Three.js는 기본적으로 `renderer.render` 메서드를 호출 할 때마다 canvas를
-초기화합니다. 예제의 경우 `renderer.render`를 2번 호출하므로, 만약 설정을
-바꾸지 않으면 첫 결과물을 초기화할 테니 배경이 제대로 보이지 않겠죠. 설정을
-끄려면 `renderer.autoClearColor = false`를 설정하면 됩니다.
-
-```js
-const renderer = new THREE.WebGLRenderer({ canvas });
-+renderer.autoClearColor = false;
-```
+어렵지 않게 등장방형도법 텍스처를 구현했습니다.
 
 {{{example url="../threejs-background-equirectangularmap.html" }}}
 

+ 71 - 50
threejs/lessons/kr/threejs-offscreencanvas.md

@@ -4,8 +4,6 @@ TOC: 웹 워커에서 OffscreenCanvas 사용하기
 
 [`OffscreenCanvas`](https://developer.mozilla.org/ko/docs/Web/API/OffscreenCanvas)는 비교적 최근 도입된 브라우저 API로 아직 크로미움 기반 브라우저에서만 사용가능하지만, 갈수록 대부분의 브라우저에서 이 API를 사용할 수 있을 겁니다. `OffscreenCanvas`를 이용하면 [웹 워커(Web Worker)](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API)에서 캔버스를 렌더링해 복잡한 3D 장면 등의 무거운 작업을 별도 프로세스에서 처리할 수 있습니다. 이러면 무거운 작업을 처리할 때 브라우저가 덜 버벅이도록 할 수 있죠. 또한 데이터도 워커에서 불러와 처리하므로 페이지 초기 로드 시 버벅임을 훨씬 줄일 수 있습니다.
 
-OffscreenCanvas의 문제점 중 하나는 웹 워커가 아직 ES 모듈(import, export 키워드 등)을 지원하지 않는다는 겁니다. 때문에 다른 예제와 달리 이 글에서는 클래스 스크립트 방식을 사용할 겁니다.
-
 사용법은 꽤나 직관적입니다. 먼저 [반응형 디자인에 관한 글](threejs-responsive.html)에서 썼던 예제를 가져오도록 하죠.
 
 이 사이트 대부분의 예제는 스크립트를 해당 HTML 파일에 인라인으로 작성했습니다. 반면에 워커는 일반적으로 별도의 스크립트 파일로 분리해 작성합니다.
@@ -22,13 +20,13 @@ function main() {
   ...
 ```
 
-그리고 `new Worker(워커 스크립트 경로)`로 워커를 생성한 뒤, 워커에 `offscreen` 객체를 넘깁니다.
+그리고 `new Worker(워커 스크립트 경로, { type: 'module' })`로 워커를 생성한 뒤, 워커에 `offscreen` 객체를 넘깁니다.
 
 ```js
 function main() {
   const canvas = document.querySelector('#c');
   const offscreen = canvas.transferControlToOffscreen();
-  const worker = new Worker('offscreencanvas-cubes.js');
+  const worker = new Worker('offscreencanvas-cubes.js', { type: 'module' });
   worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
 }
 main();
@@ -38,7 +36,7 @@ main();
 
 워커에 메시지를 보내려면 [`worker.postMessage`](https://developer.mozilla.org/ko/docs/Web/API/Worker/postMessage)에 하나 또는 두 개의 인자를 넘겨주어 호출하면 됩니다. 첫 번째 인자는 워커에 전달할 객체로, 이 객체는 그대로 전달되지 않고 [복사됩니다](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). 두 번째 인자는 옵션으로 첫 번째 인자 중 그대로 *전달하기* 원하는 객체를 배열로 지정합니다. 여기에 지정한 객체는 복사되지 않죠. 그대로 워커에 *전달되지만* 객체를 보낸 스크립트에서는 더 이상 사용이 불가능해집니다. 이것도 모든 객체를 전달할 수 있는 게 아니라 특정 타입의 객체만을 전달할 수 있죠. 당연하게도 이 중에는 `OffscreenCanvas`도 있습니다. 정리하자면 `offscreen` 객체를 전달하고 나면 이 객체는 이 스크립트에서 더 이상 쓸모가 없어집니다.
 
-워커의 `message` 이벤트를 이용하면 메시지를 받을 수 있습니다. `postMessage`에서 넘긴 객체는 `event.data`에 담겨 리스너에 전달되죠. 아까 위 코드에서는 `type: 'main'` 속성을 객체에 선언해 워커에 넘겨줬습니다. 이 `type` 속성으로 워커 내에 다른 함수를 호출하도록 해봅시다. 이러면 메인 스크립트에서 워커 내의 함수를 호출하기가 훨씬 쉬워질 겁니다.
+워커의 `message` 이벤트를 이용하면 메시지를 받을 수 있습니다. `postMessage`에서 넘긴 객체는 `event.data`에 담겨 리스너에 전달되죠. 아까 위 코드에서는 `type: 'main'` 속성을 객체에 선언해 워커에 넘겨줬습니다. 이 `type` 속성은 브라우저의 메인 스레드에서는 쓸 일이 없는 값으로, 워커 내에 다른 함수를 호출하는 키값으로 사용할 겁니다. 이러면 메인 스크립트에서 워커 내의 함수를 호출하기가 훨씬 쉬워지겠죠.
 
 ```js
 const handlers = {
@@ -58,13 +56,7 @@ self.onmessage = function(e) {
 
 이제 [반응형 디자인에 관한 글](threejs-responsive.html)에서 가져온 예제의 `main` 함수를 수정해야 합니다.
 
-물론 그전에 먼저 Three.js를 `offscreencanvas-cubes.js`에 불러와야겠죠.
-
-```js
-importScripts('resources/threejs/r119/build/three.min.js');
-```
-
-DOM에서 캔버스에 접근하는 대신 이벤트의 `data`에서 캔버스를 받습니다.
+DOM에서 캔버스에 접근하는 대신 이벤트의 `data` 속성에서 캔버스 요소를 받도록 합니다.
 
 ```js
 -function main() {
@@ -150,7 +142,7 @@ function render(time) {
 메인 스크립트로 돌아와 페이지 크기가 바뀔 때마다 워커의 `size` 함수를 실행하도록 합니다.
 
 ```js
-const worker = new Worker('offscreencanvas-picking.js');
+const worker = new Worker('offscreencanvas-picking.js', { type: 'module' });
 worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
 
 +function sendSize() {
@@ -203,7 +195,7 @@ function main() {
 +    return;
 +  }
   const offscreen = canvas.transferControlToOffscreen();
-  const worker = new Worker('offscreencanvas-picking.js');
+  const worker = new Worker('offscreencanvas-picking.js'. { type: 'module' });
   worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
 
   ...
@@ -231,17 +223,49 @@ function main() {
 
    `offscreencanvas-worker-cubes.js`
 
-`shared-cubes.js`와 `offscreencanvas-worker-cubes.js`는 단순히 이전 `offscreencanvas-cubes.js` 파일을 쪼갠 것입니다. 메인 HTML 파일에 `main`이라는 함수가 있어 `main` 함수만 `init`으로 바꿨죠.
+`shared-cubes.js`와 `offscreencanvas-worker-cubes.js`는 단순히 이전 `offscreencanvas-cubes.js` 파일을 쪼갠 것입니다. 먼저 `offscreencanvas-cube.js`를 `shared-cube.js`로 옮긴 뒤, 메인 HTML 파일에 이미 `main` 함수가 있어 `main` 함수의 이름만 `init`으로 바꿔야 하죠. 여기에 추가로 `init`과 `state` 함수를 export 시켜줘야 합니다.
 
-`offscreencanvas-worker-cubes.js`의 구조는 아주 단순합니다.
+```js
+import * as THREE from './resources/threejs/r119/build/three.module.js';
+ 
+-const state = {
++export const state = {
+  width: 300,   // 캔버스 기본값
+  height: 150,  // 캔버스 기본값
+};
+ 
+-function main(data) {
++export function init(data) {
+  const { canvas } = data;
+  const renderer = new THREE.WebGLRenderer({ canvas });
+```
+
+그리고 Three.js와 관련 없는 부분을 잘라냅니다.
 
 ```js
-'use strict';
+-function size(data) {
+-  state.width = data.width;
+-  state.height = data.height;
+-}
+-
+-const handlers = {
+-  main,
+-  size,
+-};
+-
+-self.onmessage = function(e) {
+-  const fn = handlers[e.data.type];
+-  if (!fn) {
+-    throw new Error('no handler for type: ' + e.data.type);
+-  }
+-  fn(e.data);
+-};
+```
 
-/* global importScripts, init, state */
+방금 잘라낸 부분을 `offscreencanvas-worker-cubes.js`에 붙여넣고, `shared-cubes.js`를 import 합니다. 또한 `main` 대신 `init`을 호출하도록 합니다.
 
-importScripts('resources/threejs/r119/build/three.min.js');
-+importScripts('shared-cubes.js');
+```js
+import { init, state } from './shared-cubes.js';
 
 function size(data) {
   state.width = data.width;
@@ -263,13 +287,11 @@ self.onmessage = function(e) {
 };
 ```
 
-위 코드에서는 Three.js 관련 스크립트인 `shared-cubes.js`를 불러왔습니다.
-
 메인 페이지에서도 마찬가지로 Three.js와 `shared-cubes.js`를 추가합니다.
 
 ```html
-<script src="resources/threejs/r119/build/three.min.js"></script>
-<script src="shared-cubes.js"><script>
+<script type="module"></script>
++import { init, state } from './shared-cubes.js';
 ```
 
 이전에 추가했던 에러 메시지용 HTML과 CSS를 제거합니다.
@@ -306,7 +328,7 @@ function main() {
 -    return;
 -  }
 -  const offscreen = canvas.transferControlToOffscreen();
--  const worker = new Worker('offscreencanvas-picking.js');
+-  const worker = new Worker('offscreencanvas-picking.js', { type: 'module' });
 -  worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
 +  if (canvas.transferControlToOffscreen) {
 +    startWorker(canvas);
@@ -321,8 +343,8 @@ function main() {
 ```js
 function startWorker(canvas) {
   const offscreen = canvas.transferControlToOffscreen();
-  const worker = new Worker('offscreencanvas-worker-cubes.js');
-  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);
+  const worker = new Worker('offscreencanvas-worker-cubes.js', { type: 'module' });
+  worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
 
   function sendSize() {
     worker.postMessage({
@@ -339,6 +361,13 @@ function startWorker(canvas) {
 }
 ```
 
+메시지에 `'main'` 대신 `'init'`을 보냅니다.
+
+```js
+-  worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
++  worker.postMessage({ type: 'init', canvas: offscreen }, [ offscreen ]);
+```
+
 워커를 사용할 수 없는 경우 다음과 같이 실행합니다.
 
 ```js
@@ -451,7 +480,7 @@ self.onmessage = function(e) {
 
 function startWorker(canvas) {
   const offscreen = canvas.transferControlToOffscreen();
-  const worker = new Worker('offscreencanvas-worker-picking.js');
+  const worker = new Worker('offscreencanvas-worker-picking.js', { type: 'module' });
   worker.postMessage({ type: 'init', canvas: offscreen }, [ offscreen ]);
 
 +  sendMouse = (x, y) => {
@@ -541,7 +570,7 @@ window.addEventListener('touchend', clearPickPosition);
 
 좀 더 욕심을 내 `OrbitControls`까지 추가해봅시다. `OrbitControls`는 DOM에 꽤 다양하게 접근하기에 처리해줘야 할 것이 좀 많습니다. 제대로 작동하려면 마우스 이벤트, 터치 이벤트, 키보드 이벤트를 모두 처리해줘야 하죠.
 
-여태까지는 전역 `state` 객체를 사용했지만, `OrbitControls`의 경우는 객체 속성이 너무 많아 그걸 전부 다 하드 코딩하는 건 너무 번거롭습니다. `OrbitControls`는 필요한 DOM 이벤트의 대부분을 인자로 받는 HTML 요소에 바인딩합니다. 이를 이용해 DOM 요소와 같은 구조의 객체를 넘겨준다면 어떨까요? `OrbitControls`에 필요한 기능만 살려서 말이죠.
+여태까지는 전역 `state` 객체를 사용했지만, `OrbitControls`의 경우는 객체 속성이 너무 많아 그걸 전부 다 하드 코딩하는 건 너무 번거롭습니다. `OrbitControls`는 필요한 DOM 이벤트의 대부분을 인자로 받는 `HTMLElement`에 바인딩합니다. 이를 이용해 DOM 요소와 같은 구조의 객체를 넘겨준다면 어떨까요? `OrbitControls`에 필요한 기능만 살려서 말이죠.
 
 [`OrbitControls`의 소스 코드](https://github.com/gfxfundamentals/threejsfundamentals/blob/master/threejs/resources/threejs/r119/examples/js/controls/OrbitControls.js)를 분석해보니 아래의 이벤트만 지원하면 될 듯합니다.
 
@@ -568,7 +597,9 @@ wheel 이벤트는 `deltaY` 속성만,
 아래는 워커 안의 코드입니다.
 
 ```js
-class ElementProxyReceiver extends THREE.EventDispatcher {
+import { EventDispatcher } from './resources/threejs/r119/build/three.module.js';
+
+class ElementProxyReceiver extends EventDispatcher {
   constructor() {
     super();
   }
@@ -641,18 +672,13 @@ self.onmessage = function(e) {
 };
 ```
 
-`OrbitControls` 모듈도 불러와야겠죠.
+Three.js의 공통 코드에 `OrbitControls` 모듈도 불러와 설정해야 합니다.
 
 ```js
-importScripts('resources/threejs/r119/build/three.min/js');
-+importScripts('resources/threejs/r119/examples/js/controls/OrbitControls.js');
-*importScripts('shared-orbitcontrols.js');
-```
-
-그리고 공통 Three.js 코드를 아래와 같이 수정합니다.
+import * as THREE from './resources/threejs/r119/build/three.module.js';
++import { OrbitControls } from './resources/threejs/r119/examples/jsm/controls/OrbitControls.js';
 
-```js
-function init(data) {
+export function init(data) {
 -  const { canvas } = data;
 +  const { canvas, inputElement } = data;
   const renderer = new THREE.WebGLRenderer({ canvas });
@@ -752,7 +778,7 @@ class ElementProxy {
 ```js
 function startWorker(canvas) {
   const offscreen = canvas.transferControlToOffscreen();
-  const worker = new Worker('offscreencanvas-worker-orbitcontrols.js');
+  const worker = new Worker('offscreencanvas-worker-orbitcontrols.js', { type: 'module' });
 
 +  const eventHandlers = {
 +    contextmenu: preventDefaultHandler,
@@ -979,7 +1005,7 @@ class ElementProxy {
 이제 공통 Three.js 코드에서 `state` 전역 변수를 쓰지 않으니 삭제합니다.
 
 ```js
--const state = {
+-export const state = {
 -  width: 300,   // 캔버스 기본값
 -  height: 150,  // 캔버스 기본값
 -};
@@ -1011,22 +1037,17 @@ function render(time) {
   ...
 ```
 
-`OrbitControls`는 키보드 이벤트를 감지하기 위해 `window`에 이벤트 리스너를 바인딩합니다. 이게 좋은 방식인지는 모르겠지만, 소스 코드를 직접 수정하지 않는 한 이 동작을 고칠 수는 없습니다. 워커에는 `window` 객체가 존재하지 않거든요.
+`OrbitControls`는 마우스 이벤트를 감지하기 위해 해당 요소의 `ownerDocument`에 `mousemove`와 `mouseup` 리스너를 추가합니다(마우스가 창 밖으로 나갔을 경우를 위해).
 
-또 `document`의 `mousemove`와 `mouseup` 이벤트도 사용합니다. 이는 마우스가 창 밖으로 나간다든가 하는 경우에도 마우스의 움직임을 감지하기 위한 것이지만, `window`와 마찬가지로 워커에는 `document` 객체가 없습니다.
-
-거기다 `OrbitControls`에 넘겨준 요소가 `document`와 일치하는지의 여부도 검사합니다.
+또한 코드는 전역 `document` 객체를 참조하지만 워커에는 전역 `document` 객체가 없습니다.
 
 이 문제는 간단한 편법(hack)을 써 해결할 수 있습니다. 다시 한 번 워커의 경유 객체를 이용하도록 하죠.
 
 ```js
 function start(data) {
   const proxy = proxyManager.getProxy(data.canvasId);
-+  self.window = proxy;  // HACK!
-+  self.document = {  // HACK!
-+    addEventListener: proxy.addEventListener.bind(proxy),
-+    removeEventListener: proxy.removeEventListener.bind(proxy),
-+  };
++  proxy.ownerDocument = proxy; // HACK!
++  self.document = {} // HACK!
   init({
     canvas: data.canvas,
     inputElement: proxy,