Browse Source

[ru] Add translation for threejs-scenegraph

NikitaIT 6 years ago
parent
commit
fc51192da2
1 changed files with 389 additions and 0 deletions
  1. 389 0
      threejs/lessons/ru/threejs-scenegraph.md

+ 389 - 0
threejs/lessons/ru/threejs-scenegraph.md

@@ -0,0 +1,389 @@
+Title: Граф сцены Three.js
+Description: Что такое граф сцены?
+
+Эта статья является частью серии статей о three.js. 
+Первая статья - [основы Three.js](threejs-fundamentals.html).
+Если вы её еще не читали, советую вам сделать это.
+
+Ядром Three.js, возможно, является граф сцены. Граф сцены в трехмерном 
+движке - это иерархия узлов в графе, где каждый узел представляет 
+локальное пространство.
+
+<img src="../resources/images/scenegraph-generic.svg" align="center">
+
+Это своего рода абстракция, поэтому давайте попробуем привести несколько примеров.
+
+Одним из примеров может быть солнечная система, солнце, земля, луна.
+
+<img src="../resources/images/scenegraph-solarsystem.svg" align="center">
+
+Земля вращается вокруг Солнца. Луна вращается вокруг Земли. Луна движется по 
+кругу вокруг Земли. С точки зрения Луны она вращается в «локальном пространстве» 
+Земли. Хотя его движение относительно Солнца с точки зрения Луны представляет 
+собой какой-то сумасшедший спирографический изгиб, ему просто нужно заниматься 
+вращением вокруг локального пространства Земли.
+
+{{{diagram url="resources/moon-orbit.html" }}}
+
+Чтобы думать об этом иначе, вы, живущие на Земле, не должны думать о вращении 
+Земли вокруг своей оси или о вращении вокруг Солнца. Вы просто идете или едете, 
+или плаваете, или бежите, как будто Земля вообще не движется и не вращается. 
+Вы идете, ездите, плаваете, бегаете и живете в «локальном пространстве» Земли, 
+хотя относительно Солнца вы вращаетесь вокруг Земли со скоростью около 
+1000 миль в час, а вокруг Солнца - около 67 000 миль в час. Ваше положение 
+в Солнечной системе похоже на положение Луны наверху, но вам не нужно 
+беспокоиться о себе. Вы просто переживаете за свое положение относительно 
+земли, ее "локального пространства".
+
+Давайте сделаем это один шаг за один раз. Представьте, что мы хотим сделать 
+диаграмму солнца, земли и луны. Мы начнем с солнца, просто сделав сферу 
+и поместив ее в начало координат. Примечание: мы используем солнце, 
+землю, луну в качестве демонстрации того, как использовать граф сцены. 
+Конечно, настоящее Солнце, Земля и Луна используют физику, но для наших 
+целей мы подделаем это с помощью графа сцены.
+
+```js
+// массив объектов, направление которых обновляется
+const objects = [];
+
+// использовать только одну сферу для всего
+const radius = 1;
+const widthSegments = 6;
+const heightSegments = 6;
+const sphereGeometry = new THREE.SphereBufferGeometry(
+    radius, widthSegments, heightSegments);
+
+const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
+const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
+sunMesh.scale.set(5, 5, 5);  // сделать солнце большим
+scene.add(sunMesh);
+objects.push(sunMesh);
+```
+
+Мы используем действительно низкополигональную сферу. 
+Всего 6 разделений вокруг его экватора. Это так легко увидеть вращение.
+
+Мы собираемся повторно использовать одну и ту же сферу для всего, 
+поэтому мы установим масштаб для солнечной полигональной сетки (mesh) в 5x.
+
+Мы также устанавливаем свойство материала "Затенение по Фонгу" `emissive` желтым. 
+Излучающее (emissive) свойство материала Phong - это цвет, который будет рисоваться 
+без попадания света на поверхность. Свет добавляется к этому цвету.
+
+Давайте также поместим один точечный источник света в центр сцены. Мы рассмотрим 
+более подробно о точечных источниках света позже, но пока простая версия 
+представляет собой точечный источник света.
+
+```js
+{
+  const color = 0xFFFFFF;
+  const intensity = 3;
+  const light = new THREE.PointLight(color, intensity);
+  scene.add(light);
+}
+```
+
+Чтобы было легче увидеть, мы поместим камеру прямо над источником, смотря вниз. 
+Самый простой способ сделать это - использовать `lookAt`. `lookAt`
+Функция будет ориентировать камеру из своего положения в "смотриНа 
+точку переданную `lookAt`. Перед тем, как сделать это, мы должны сказать 
+камере, в какую сторону направлена верхняя часть камеры или, скорее, 
+в какой стороне "верх" для камеры. Для большинства ситуаций положительный 
+Y - это достаточно хорошо, но, так как мы смотрим прямо вниз, 
+мы должны сказать камере, что положительный Z - вверхy.
+
+```js
+const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+camera.position.set(0, 50, 0);
+camera.up.set(0, 0, 1);
+camera.lookAt(0, 0, 0);
+```
+
+В цикле отрисовки, переделанном из предыдущих примеров, 
+мы вращаем все объекты в нашем массиве `objects` с помощью этого кода.
+
+```js
+objects.forEach((obj) => {
+  obj.rotation.y = time;
+});
+```
+
+Так как мы добавили `sunMesh` в массив `objects` он будет вращаться.
+
+{{{example url="../threejs-scenegraph-sun.html" }}}
+
+Теперь давайте добавим землю.
+
+```js
+const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
+const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
+earthMesh.position.x = 10;
+scene.add(earthMesh);
+objects.push(earthMesh);
+```
+
+Мы создаем материал синего цвета, но мы дали ему небольшое количество *излучающего* 
+синего цвета, чтобы он отображался на черном фоне.
+
+Мы используем ту же `sphereGeometry` с нашим новым синим `earthMaterial` чтобы сделать 
+`earthMesh`. Мы размещаем эти 10 единиц слева от солнца и добавляем их в сцену. 
+Поскольку мы добавили его в наш массив `objects`, он тоже будет вращаться.
+
+{{{example url="../threejs-scenegraph-sun-earth.html" }}}
+
+Вы можете видеть, что Солнце и Земля вращаются, но Земля не вращается вокруг Солнца. 
+Давайте сделаем землю дитя солнца
+
+```js
+-scene.add(earthMesh);
++sunMesh.add(earthMesh);
+``` 
+
+а также...
+
+{{{example url="../threejs-scenegraph-sun-earth-orbit.html" }}}
+
+Что случилось? Почему Земля такого же размера, как Солнце, и почему она так далеко? 
+На самом деле мне пришлось передвинуть камеру с 50 единиц сверху до 150 единиц 
+сверху, чтобы увидеть Землю.
+
+Мы сделали `earthMesh` ребенком `sunMesh`. `sunMesh` масштабирован на 5x 
+из-за `sunMesh.scale.set(5, 5, 5)`. Это означает, что локальное пространство
+`sunMesh` в 5 раз больше.  Все, что помещено в это пространство, будет умножено на 5. 
+Это означает, что Земля теперь в 5 раз больше и расстояние от Солнца 
+(`earthMesh.position.x = 10`) также в 5 раз.
+
+Наш граф сцены в настоящее время выглядит следующим образом
+
+<img src="../resources/images/scenegraph-sun-earth.svg" align="center">
+
+Чтобы это исправить, давайте добавим пустой узел графа сцены. 
+Мы будем связывать солнце и землю с этим узлом.
+
+```js
++const solarSystem = new THREE.Object3D();
++scene.add(solarSystem);
++objects.push(solarSystem);
+
+const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
+const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
+sunMesh.scale.set(5, 5, 5);
+-scene.add(sunMesh);
++solarSystem.add(sunMesh);
+objects.push(sunMesh);
+
+const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
+const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
+earthMesh.position.x = 10;
+-sunMesh.add(earthMesh);
++solarSystem.add(earthMesh);
+objects.push(earthMesh);
+```
+
+Здесь мы сделали `Object3D`. Как и `Mesh` он также является узлом в графе сцены, 
+но в отличие от `Mesh` он не имеет материала или геометрии. Это просто 
+представляет локальное пространство.
+
+Наш новый граф сцены выглядит следующим образом
+
+<img src="../resources/images/scenegraph-sun-earth-fixed.svg" align="center">
+
+И `sunMesh` и `earthMesh` дети `solarSystem`. Все 3 вращаются, и теперь, 
+поскольку они не являются потомками `earthMesh`, `sunMesh` больше не 
+масштабируются в 5 раз.
+
+{{{example url="../threejs-scenegraph-sun-earth-orbit-fixed.html" }}}
+
+Намного лучше. Земля меньше Солнца, и она вращается вокруг Солнца и вращается сама.
+
+Продолжая ту же самую модель, давайте добавим луну.
+
+```js
++const earthOrbit = new THREE.Object3D();
++earthOrbit.position.x = 10;
++solarSystem.add(earthOrbit);
++objects.push(earthOrbit);
+
+const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
+const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
+-solarSystem.add(earthMesh);
++earthOrbit.add(earthMesh);
+objects.push(earthMesh);
+
++const moonOrbit = new THREE.Object3D();
++moonOrbit.position.x = 2;
++earthOrbit.add(moonOrbit);
+
++const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
++const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
++moonMesh.scale.set(.5, .5, .5);
++moonOrbit.add(moonMesh);
++objects.push(moonMesh);
+```
+
+Снова мы добавили еще один невидимый узел графа сцены `Object3D` под названием `earthOrbit`
+и добавили `earthMesh` и `moonMesh` к нему. Новый граф сцены выглядит следующим образом.
+
+<img src="../resources/images/scenegraph-sun-earth-moon.svg" align="center">
+
+и вот что
+
+{{{example url="../threejs-scenegraph-sun-earth-moon.html" }}}
+
+Вы можете видеть, что луна следует шаблону спирографа, показанному в верхней части этой 
+статьи, но нам не пришлось вычислять ее вручную. Мы просто настраиваем наш 
+граф сцены, чтобы он сделал это за нас.
+
+Часто полезно рисовать что-то для визуализации узлов в графе сцены. 
+Three.js имеет несколько полезных ... ммм, помощников ... помоающих с этим.
+
+Один называется `AxesHelper`. Он рисует 3 линии, представляющие локальные оси
+<span style="color:red">X</span>,
+<span style="color:green">Y</span>, и
+<span style="color:blue">Z</span> Давайте добавим по одному к каждому узлу, 
+который мы создали.
+
+```js
+// добавляем AxesHelper к каждому узлу
+objects.forEach((node) => {
+  const axes = new THREE.AxesHelper();
+  axes.material.depthTest = false;
+  axes.renderOrder = 1;
+  node.add(axes);
+});
+```
+
+В нашем случае мы хотим, чтобы оси появлялись, даже если они находятся внутри сфер. 
+Чтобы сделать это, мы устанавливаем для их материала `depthTest` значение false, 
+что означает, что они не будут проверять, что они рисуются за чем-то другим. 
+Мы также устанавливаем их `renderOrder` в 1 (по умолчанию 0), чтобы они 
+рисовались после всех сфер. В противном случае сфера может накрыть их и закрыть их.
+
+{{{example url="../threejs-scenegraph-sun-earth-moon-axes.html" }}}
+
+Мы можем видеть оси
+<span style="color:red">x (красная)</span> и
+<span style="color:blue">z (синяя)</span> Поскольку мы смотрим прямо вниз, и каждый 
+из наших объектов вращается только вокруг своей оси y, мы не видим большую часть 
+осей <span style="color:green">y (зеленая)</span>.
+
+Может быть трудно увидеть некоторые из них, так как есть две пары перекрывающихся осей. 
+И `sunMesh` и  `solarSystem` находятся в одинаковом положении. Точно так же `earthMesh` и
+`earthOrbit`находятся в той же позиции. Давайте добавим несколько простых элементов управления, 
+чтобы мы могли включать и выключать их для каждого узла. Пока мы делаем это, 
+давайте также добавим еще одного помощника под названием `GridHelper`. 
+Создающего двумерную сетку на плоскости X, Z. По умолчанию сетка составляет 
+10х10 единиц.
+
+Мы также собираемся использовать [dat.GUI](https://github.com/dataarts/dat.gui) 
+библиотеку пользовательского интерфейса, которая очень популярна в проектах Three.js. 
+dat.GUI принимает объект и имя свойства для этого объекта и в зависимости от типа 
+свойства автоматически создает пользовательский интерфейс для управления этим свойством.
+
+Мы хотим сделать `GridHelper` и `AxesHelper` для каждого узла. Нам нужна метка для каждого 
+узла, поэтому мы избавимся от старого цикла и переключимся на вызов некоторой 
+функции, чтобы добавить помощники для каждого узла
+
+```js
+-// добавляем AxesHelper к каждому узлу
+-objects.forEach((node) => {
+-  const axes = new THREE.AxesHelper();
+-  axes.material.depthTest = false;
+-  axes.renderOrder = 1;
+-  node.add(axes);
+-});
+
++function makeAxisGrid(node, label, units) {
++  const helper = new AxisGridHelper(node, units);
++  gui.add(helper, 'visible').name(label);
++}
++
++makeAxisGrid(solarSystem, 'solarSystem', 25);
++makeAxisGrid(sunMesh, 'sunMesh');
++makeAxisGrid(earthOrbit, 'earthOrbit');
++makeAxisGrid(earthMesh, 'earthMesh');
++makeAxisGrid(moonMesh, 'moonMesh');
+```
+
+`makeAxisGrid` делает `AxisGridHelper` класс, который мы создадим, 
+чтобы сделать dat.GUI счастливым. Как сказано выше, dat.GUI автоматически 
+создаст пользовательский интерфейс, который манипулирует именованным свойством 
+некоторого объекта. Это создаст другой пользовательский интерфейс в зависимости 
+от типа свойства. Мы хотим, чтобы он создал флажок, поэтому нам нужно указать 
+`bool` свойство. Но мы хотим, чтобы и оси, и сетка появлялись / исчезали на основе 
+одного свойства, поэтому мы создадим класс, который имеет метод получения и 
+установки для свойства. Таким образом, мы можем позволить dat.GUI думать, 
+что он манипулирует одним свойством, но внутри мы можем установить видимое 
+свойство `AxesHelper` и `GridHelper` для узла.
+
+```js
+// Для включения и выключения видимых осей и сетки
+// dat.GUI требуется свойство, которое возвращает bool
+// это checkbox мы сделали сеттер и геттер
+// чтобы получить значение для `visible` от dat.GUI
+class AxisGridHelper {
+  constructor(node, units = 10) {
+    const axes = new THREE.AxesHelper();
+    axes.material.depthTest = false;
+    axes.renderOrder = 2;  // после сетки
+    node.add(axes);
+
+    const grid = new THREE.GridHelper(units, units);
+    grid.material.depthTest = false;
+    grid.renderOrder = 1;
+    node.add(grid);
+
+    this.grid = grid;
+    this.axes = axes;
+    this.visible = false;
+  }
+  get visible() {
+    return this._visible;
+  }
+  set visible(v) {
+    this._visible = v;
+    this.grid.visible = v;
+    this.axes.visible = v;
+  }
+}
+```
+
+Мы устанавливаем `renderOrder` в `AxesHelper`
+равным 2, а для `GridHelper` равным 1 так, что оси втянуться после появления сетки. 
+В противном случае сетка может перезаписать оси.
+
+{{{example url="../threejs-scenegraph-sun-earth-moon-axes-grids.html" }}}
+
+Включите `solarSystem` и вы увидите, что Земля находится точно в 10 единицах от центра, 
+как мы установили выше. Вы можете увидеть , как земля находится в 
+*локальном пространстве* `solarSystem`. Включите `earthOrbit` и вы увидите, 
+как луна ровно на 2 единицы от центра *локального пространства* `earthOrbit`.
+
+Еще несколько примеров графов сцены. Автомобиль в простом игровом мире 
+может иметь такой граф сцены
+
+<img src="../resources/images/scenegraph-car.svg" align="center">
+
+Если вы двигаете кузов автомобиля, все колеса будут двигаться вместе с ним. 
+Если вы хотите, чтобы кузов отскакивал отдельно от колес, вы можете привязать 
+тело и колеса к "рамному" узлу, который представляет раму автомобиля.
+
+Другой пример - человек в игровом мире.
+
+<img src="../resources/images/scenegraph-human.svg" align="center">
+
+Вы можете видеть, что график сцены становится довольно сложным для человека. 
+На самом деле этот граф сцены упрощен. Например, вы можете расширить его, 
+чтобы охватить каждый палец (по крайней мере, еще 28 узлов) и каждый палец 
+(еще 28 узлов), плюс для челюсти, глаз и, возможно, больше.
+
+Я надеюсь, что это дает некоторое представление о том, как работает граф сцены 
+и как вы можете его использовать. Создание `Object3D` узлов и родительских 
+объектов для них - важный шаг к хорошему использованию трехмерного движка, 
+такого как three.js. Часто может показаться, что какая-то сложная математика 
+необходима, чтобы заставить что-то двигаться и вращаться так, как вы хотите. 
+Например, без графа сцены, вычисляющего движение луны или куда поставить 
+колеса автомобиля относительно его тела, было бы очень сложно, но с 
+помощью графа сцены это становится намного проще.
+
+[Далее мы пройдемся по материалам](threejs-materials.html).