Всем привет!
В предыдущей главе мы познакомились с основами бибилиотекиThree.js. Увидели несколько примеров и создали свою первую полноценную Three.js сцену. В этой главе мы немного глубже углубимся в эту библиотеку и попробуем более подробно объяснить основные компоненты, составляющие Three.js сцену. В этой главе вы узнаете о следующем:
Начнем мы с того, что посмотрим, как же можно создать сцену и добавить на нее объекты.
В предыдущей главе мы уже создавали объект THREE.Scene(), так что вы уже знакомы с этим компонентом библиотеки Three.js. Так же мы увидели, что для нормального отображения сцены нам необходимо три компонента:
Объект THREE.Scene() выступает в качестве контейнера для всех этих компонентов. У этого объекта самого по себе не слишком много опций и функций.
Лучший способ изучить функциональность сцены – это посмотреть на примере. В исходниках к этой главе (chapter-02) вы можете найти пример 01-basic-scene.html. Я буду использовать этот пример, чтобы объяснить функции и опции, которыми обладает сцена. Если мы откроем этот пример в браузере, то увидим примерно следующее:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/47d/133/c60/47d133c60a886209091e9da14ec32d34.png)
Это немного похоже на то, что мы видели в предыдущей главе. Несмотря на то, что сцена выглядит несколько пустой, она уже содержит несколько объектов. Глядя на следующий исходный код, мы видим, что мы использовали функцию
Прежде чем мы более детально начнем рассматривать объект THREE.Scene(), я бы хотел рассказать, что вы можете сделать в демонстраторе, а уже потом начнем разбираться в коде. Откройте пример в браузере и посмотрите на элементы управления в верхнем правом углу, вот что можно сделать:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/ad2/7d8/288/ad27d828847033f692a1a09ca0febb7f.png)
Работающий пример.
С помощью этих элементов управления можно добавить кубик на сцену, удалить последний добавленный кубик со сцены и отобразить число всех элементов, которые содержит сцена. Вероятно, вы заметили, что, когда вы только запускаете пример, то на сцена уже находится 4 объекта. Это наша плоскость, источник рассеянного света, источник точечного света и камера, о которой мы упоминали ранее. Далее мы более подробно рассмотрим каждый из компонентов в блоке управления и начнем с самого простого: с функции
Этот фрагмент кода должен довольно легко восприниматься в настоящее время. Здесь не вводится много новых понятий. При нажатии на кнопку addCube, создается новый экземпляр
Новым в этом фрагменте кода является то, что мы задаем имя кубу с помощью атрибута
Следующая функция, которую мы можем вызвать из управляющего интерфейса это
Чтобы добавить объект на сцену, мы будем использовать функцию
Последняя кнопка на нашемGUI помечена как outputObjects. Вы, наверное, уже нажали на нее, и, казалось бы, ничего не произошло. То, что делает эта кнопка, это распечатывает все объекты, которые в настоящее время отображены на нашей сцене и выводит их в консоль веб-браузера, как показано ниже:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/748/775/fdd/748775fdd6b7d968599ec963abdb6f48.png)
Код для вывода информации в лог консоли использует встроенный объект
Это очень удобно для отладки; особенно когда вы именуете свои объекты, это очень удобно при поиске проблем конкретных объектов на сцене. Например, свойства объекта с именем
До сих пор мы рассматривали следующие функции для сцены:
Это самые важные функции сцены, которые вы будете применять довольно часто. Однако, есть несколько вспомогательных функций, которые могут вам пригодиться, и я хотел бы познакомить вас с ними на примере кода, который обрабатывает вращение куба.
Как вы уже видели в предыдущей главе, мы использовали цикл внутри функции
Здесь мы можем видеть, что используется функция
Прежде чем погрузиться в детали объектов
Свойство
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/bcf/87c/48e/bcf87c48e7bbab891af657d61c839a28.png)
Работающий пример.
Включение свойства тумана происходит в библиотеке Three.js происходит очень легко. Просто добавьте следующую строку кода после того как вы определили свою сцену:
Здесь мы определили цвет тумана как белый (0xffffff). Последние два аргумента могут быть изменены уже после того, как туман появится. Значение 0,015 устанавливается для свойства
На этот раз мы не указывали свойства
Последнее свойство, которое мы рассмотрим, будет свойство
Результат приведен ниже:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/7b0/74b/82d/7b074b82df26352533cebbe01d3646c0.png)
На этом скриншоте хорошо видно, что все экземпляры объекта cube визуализированы с помощью одного и того же материала и цвета. В этом примере мы использовали объект
В этом разделе мы рассмотрели первую из основных концепций библиотеки Three.js: сцену. Самое главное, что надо помнить, это то, что это в основном контейнер для всех объектов, света и камеры, которые вы хотите отобразить. Напомним, наиболее важные функции и атрибуты объекта
В следующих разделах мы внимательно рассмотрим объекты, которые можно добавить на сцену.
В каждом из примеров, которые мы видели, использовалась геометрия и
Мы определили форму объекта, его геометрию, как этот объект должен выглядеть, свойство его материала, затем все это соединили и добавили на сцену. В этом разделе мы немного подробнее рассмотрим геометрические и соединительные объекты, начнем с геометрических.
Библиотека Three.js поставляется с большим набором готовых геометрических объектов, которые можно добавлять на 3D-сцену. Просто добавьте материал, создайте переменную
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/e1e/065/a83/e1e065a836763081672547b3e2560cb0.png)
Геометрические объекты в Three.js, как и в большинстве других 3D-библиотек, в основном это коллекция точек в пространстве, а так же ряд граней, связывающих все эти точки вместе. Возьмем, к примеру, куб:
При использовании библиотеки Thee.js, когда вы создаете геометрический объект, вы не должны определять все вершины и грани сами. Для куба вам нужно определить всего лишь его высоту, ширину и глубину. Библиотека Thee.js использует эту информацию и создает правильный геометрический объект с восьмью вершинами в нужном положении. Несмотря на то, что, обычно, при создании геометрических объектов используют стандартные объекты, предложенные библиотекой, вы все равно можете создавать объекты вручную, определив вершины и грани, как показано ниже:
Этот код показывает, как создать простой куб. Мы определили точки, которые составляют куб в массив вершин. Эти точки соединяются, чтобы получились треугольные грани, которые хранятся в массиве граней. Например, элемент
На следующем сриншоте показан пример, при помощи которого можно поиграть с позициями вершин:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/086/578/165/086578165db4e3e69bc0b40a92d211a7.png)
Работающий пример.
Этот пример, как и все рассмотренные ранее примеры, использует цикл внутри функции
В первой строке данного фрагмента кода мы обновляем массив отображаемых вершин в объекте
Последняя функциональность
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/0a3/808/602/0a3808602e3b52bf7377f635869bc97b.png)
Если вы нажмете на эту кнопку, то появится клон, который будет иметь ту же геометрию, что и старый объект в настоящее время, и новый объект создается из другого материала и добавляется на сцену. Код для этого довольно тривиален, но сделан немного сложнее из-за материалов, которые мы используем. Для начала давайте шаг за шагом разберем код, который был использован для создания материалов для куба:
Как можно увидеть мы использовали не один материал, а массив из двух материалов. Причина в том, что помимо показа прозрачного куба, мне также хотелось показать вам каркас, так как он ясно показывает, где расположены вершины и грани. Библиотека Three.js конечно же поддерживает использование нескольких материалов при создании
В этой функции библиотека Three.js делает то, что она не создает один экземпляр
Теперь вернемся к функции clone(), которую мы обсуждали ранее:
Этот кусок кода вызывается, при клике на кнопку clone. Здесь мы клонируем геометрию первого ребенка куба. Помните, что переменная
Мы уже выяснили, что для создания mesh нам необходима geometry и один или несколько материалов. Как только мы получили сетку, мы можем добавить ее на сцену и отрендерить. Есть несколько свойств, которые вы можете использовать, чтобы повлиять на то, где появится эта сетка на сцене. В первом примере мы рассмотрим следующий набор свойств и функций:
Как всегда, мы рассмотрим готовый пример, который позволяет поиграть всеми этими свойствами, он приведен на следующем скриншоте:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/baa/e44/3b7/baae443b7dd823c3178ba8dae8b7b96c.png)
Работающий пример.
Позвольте мне рассказать вам о них; начну я со свойства
Но также мы можем сделать это одной командой:
Существует также третий вариант. Свойство
Мне хотелось бы сделать небольшой отступ переде тем, как продолжить рассматривать другие свойства
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/3c6/618/de2/3c6618de23b0f68f3b23cbf97fb9dba7.png)
Ок, следующим в списке является свойство
Следующим свойством из нашего оговоренного списка является свойство
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/a74/842/94c/a7484294c1b938b6a878a5a8c85a3b27.png)
При использовании значений, больших единицы объект становится больше, это продемонстрировано на следующем скриншоте:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/6ff/c9a/7c1/6ffc9a7c135449f59987db93e09d4968.png)
В последнюю очередь в этой главе мы поговорим о перемещающих функциях. С помощью перемещения вы также можете изменить положение объекта, но вместо определения его абсолютного положения, в котором вы хотите, чтобы он находился, вы определяете, куда объект должен переместиться относительно его настоящего положения. Например, у нас есть объект
В библиотеке Three.js есть два вида камер: ортогональная камера и перспективная. В этот раз мы ограничимся рассмотрением основ работы с этими объектами. Лучший способ объяснить разницу между этими камерами – это посмотреть на пару примеров.
В примерах для этой главе вы можете найти пример под названием
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/bb2/cb9/aa8/bb2cb9aa861ac3cc33ecac129bf89f5a.png)
Это называется перспективным видом и является наиболее естественным. Как можно видеть из этого скриншота, чем дальше находятся кубы от камеры, тем меньше они оказываются.
Если же мы изменим камеру на другой тип, поддерживаемый библиотекой Three.js, то есть на ортогональную, то увидим примерно следующее:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/1fd/ae1/0da/1fdae10da622b4b3b00ad338da8f4719.png)
Работающий пример.
С ортогональной камеры все кубики оказываются одного размера; расстояние между объектом и камерой не имеет значения. Это часто используется в 2D-играх, таких как SimCity 4 и ранних версиях Цивилизации, как показано на следующем скриншоте:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/539/0e3/ed1/5390e3ed12363aa6056051c272e85281.png)
Чаще всего в наших примерах мы будем использовать перспективную камеру, так как это наиболее близко к реальному миру. Переключение камеры на самом деле очень просто. Следующий фрагмент коде показывает, что происходит, когда вы щелкаете по кнопке switchCamera в нашем последнем примере:
В этом листинге можно увидеть, что есть разница в том, как мы создаем объект
На следующем рисунке дается хороший обзор того, как эти свойства работают вместе:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/210/c37/f95/210c37f95c41ea0ae56fbdd915607990.png)
Чтобы настроить ортогональную камеру, мы должны использовать другие свойства. Ортогональная проекция не зависит от пропорций, которые мы используем или от поля зрения, с которым мы смотрим на сцену. Все объекты отображаются в одном размере. Для ортогональной камеры нам необходимо определить куб, который должен быть отображен. Рассмотрим, какими свойствами обладает объект
Все эти свойства показаны на следующем рисунке:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/5ff/8f6/168/5ff8f616881e469cc470ae754ec6a159.png)
До сих пор мы рассматривали, как создавать камеру и что означают различные ее свойства. В предыдущей главе мы также узнали, что камеру необходимо разместить где-то на сцене и что камера будет указывать на центр отображаемой сцены. Обычно камера направлена к центру сцены, используя координаты:
Рассмотрим пример, когда камера перемещается, глядя на помеченную красным точку, это показано на следующем скриншоте:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/d5d/c35/025/d5dc35025fb34fc03c3df2e37157344e.png)
Работающий пример.
Если вы откроете пример 08-cameras-lookat.html, то увидите, что сцена движется слева направо. На самом деле сцена не движется. Это камера смотрит с разных точек, что дает эффект движения сцены слева направо. В этом примере вы также можете переключиться на ортогональную камеру. Здесь вы увидите, какие произошли изменения и поймете отличия от перспективной камеры.
В этой второй вводной главе мы рассмотрели много пунктов, и это должно дать вам хороший обзор того, что такое сцена и чем являются наиболее важные компоненты на ней. В ближайших пару главах мы будем более глубоко рассматривать детали библиотеки Three.js. Ниже приведены некоторые из пунктов, которые вы должны запомнить из этой главы:
В следующей главе мы рассмотрим различные источники света, которые доступны в библиотеке Three.js. Вы узнаете, как ведут себя различные источники света, как их создавать и настраивать.
Ссылка на GitHub
P.S. Все недочеты перевода прошу присылать в ЛС.
В предыдущей главе мы познакомились с основами бибилиотекиThree.js. Увидели несколько примеров и создали свою первую полноценную Three.js сцену. В этой главе мы немного глубже углубимся в эту библиотеку и попробуем более подробно объяснить основные компоненты, составляющие Three.js сцену. В этой главе вы узнаете о следующем:
- какие компоненты используются в Three.js сцене
- что можно делать с объектом THREE.Scene()
- какая разница между ортогональной и перспективной камерами
Начнем мы с того, что посмотрим, как же можно создать сцену и добавить на нее объекты.
Создание сцены
В предыдущей главе мы уже создавали объект THREE.Scene(), так что вы уже знакомы с этим компонентом библиотеки Three.js. Так же мы увидели, что для нормального отображения сцены нам необходимо три компонента:
- свет: он оказывает воздействие на материалы и используется при создании эффекта тени
- камера: этот компонент определяет, что будет отображаться на сцене
- мотор объекты: это основные объекты, которые отображаются в перспективе камеры: кубы, сферы и т.д.
Объект THREE.Scene() выступает в качестве контейнера для всех этих компонентов. У этого объекта самого по себе не слишком много опций и функций.
Базовая функциональность сцены
Лучший способ изучить функциональность сцены – это посмотреть на примере. В исходниках к этой главе (chapter-02) вы можете найти пример 01-basic-scene.html. Я буду использовать этот пример, чтобы объяснить функции и опции, которыми обладает сцена. Если мы откроем этот пример в браузере, то увидим примерно следующее:
![](https://habrastorage.org/getpro/habr/post_images/47d/133/c60/47d133c60a886209091e9da14ec32d34.png)
Это немного похоже на то, что мы видели в предыдущей главе. Несмотря на то, что сцена выглядит несколько пустой, она уже содержит несколько объектов. Глядя на следующий исходный код, мы видим, что мы использовали функцию
Scene.add(object)
для объекта THREE.Scene()
, добавив тем самым такие объекты как THREE.Mesh
(это плоскость, которую вы видите), THREE.SpotLight
и THREE.AmbientLight
. Объект THREE.Camera
добавляется автоматически самой библиотекой Three.js, когда вы визуализируете сцену, но он так же может быть добавлен вручную, если вы того захотите.var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45,
window.innerWidth / window.innerHeight, 0.1, 1000);
...
var planeGeometry = new THREE.PlaneGeometry(60,40,1,1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
...
scene.add(plane);
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
...
var spotLight = new THREE.SpotLight( 0xffffff );
...
scene.add( spotLight );
Прежде чем мы более детально начнем рассматривать объект THREE.Scene(), я бы хотел рассказать, что вы можете сделать в демонстраторе, а уже потом начнем разбираться в коде. Откройте пример в браузере и посмотрите на элементы управления в верхнем правом углу, вот что можно сделать:
![](https://habrastorage.org/getpro/habr/post_images/ad2/7d8/288/ad27d828847033f692a1a09ca0febb7f.png)
Работающий пример.
С помощью этих элементов управления можно добавить кубик на сцену, удалить последний добавленный кубик со сцены и отобразить число всех элементов, которые содержит сцена. Вероятно, вы заметили, что, когда вы только запускаете пример, то на сцена уже находится 4 объекта. Это наша плоскость, источник рассеянного света, источник точечного света и камера, о которой мы упоминали ранее. Далее мы более подробно рассмотрим каждый из компонентов в блоке управления и начнем с самого простого: с функции
addCube
():this.addCube = function() {
var cubeSize = Math.ceil((Math.random() * 3));
var cubeGeometry = new THREE.CubeGeometry(cubeSize,cubeSize,cubeSize);
var cubeMaterial = new THREE.MeshLambertMaterial(
{color: Math.random() * 0xffffff });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.name = "cube-" + scene.children.length;
cube.position.x=-30 + Math.round((Math.random() * planeGeometry.width));
cube.position.y= Math.round((Math.random() * 5));
cube.position.z=-20 + Math.round((Math.random() * planeGeometry.height));
scene.add(cube);
this.numberOfObjects = scene.children.length;
};
Этот фрагмент кода должен довольно легко восприниматься в настоящее время. Здесь не вводится много новых понятий. При нажатии на кнопку addCube, создается новый экземпляр
THREE.CubeGeometry
со случайным размером от нуля до трех. Кроме произвольного размера, куб также получает случайное положение на сцене и цвет.Новым в этом фрагменте кода является то, что мы задаем имя кубу с помощью атрибута
name
. Его имя задается как cube
— и число уже имеющихся на сцене кубов (показывается с помощью атрибута scene.children.length
). Таким образом, получаются имена cube-1, cube-2, cube-3
и так далее. Имя может быть полезно для отладки, но также может использоваться для прямого поиска объекта на вашей сцене. Если вы используете функцию Scene.getChildByName(name)
, то вы можете напрямую получить объект и, например, изменить его расположение. Переменная numberOfObjects
используется нашим управляющим интерфейсом как размер списка числа элементов на сцене. Поэтому, когда мы добавляем или удаляем объект, то мы устанавливаем эту переменную в значение, равное новому количеству элементов на сцене.Следующая функция, которую мы можем вызвать из управляющего интерфейса это
removeCube
, и, как следует из названия, нажав на эту кнопку, со сцены удаляется последний добавленный куб. Ниже показано, как реализована данная функция:this.removeCube = function() {
var allChildren = scene.children;
var lastObject = allChildren[allChildren.length-1];
if (lastObjectinstanceofTHREE.Mesh) {
scene.remove(lastObject);
this.numberOfObjects = scene.children.length;
}
}
Чтобы добавить объект на сцену, мы будем использовать функцию
add
(). А чтобы удалить объект со сцены, как это не удивительно, функцию remove
(). В данном фрагменте кода мы использовали свойство children
объекта THREE.Scene()
, чтобы получить последний объект, который был добавлен. Нам также необходимо проверить, чтобы объект являлся Mesh
объектом, чтобы избежать удаления камеры и света. После того, как мы удалили объект, мы в очередной раз должны обновить элемент управляющего интерфейса, который отображает количество объектов на сцене.Последняя кнопка на нашемGUI помечена как outputObjects. Вы, наверное, уже нажали на нее, и, казалось бы, ничего не произошло. То, что делает эта кнопка, это распечатывает все объекты, которые в настоящее время отображены на нашей сцене и выводит их в консоль веб-браузера, как показано ниже:
![](https://habrastorage.org/getpro/habr/post_images/748/775/fdd/748775fdd6b7d968599ec963abdb6f48.png)
Код для вывода информации в лог консоли использует встроенный объект
console
, как показано ниже:this.outputObjects = function() {
console.log(scene.children);
}
Это очень удобно для отладки; особенно когда вы именуете свои объекты, это очень удобно при поиске проблем конкретных объектов на сцене. Например, свойства объекта с именем
cube-17
будут выглядеть следующим образом:__webglActive: true
__webglInit: true
_modelViewMatrix: THREE.Matrix4
_normalMatrix: THREE.Matrix3
_vector: THREE.Vector3
castShadow: true
children: Array[0]
eulerOrder: "XYZ"
frustumCulled: true
geometry: THREE.CubeGeometry
id: 20
material: THREE.MeshLambertMaterial
matrix: THREE.Matrix4
matrixAutoUpdate: true
matrixRotationWorld: THREE.Matrix4
matrixWorld: THREE.Matrix4
matrixWorldNeedsUpdate: false
name: "cube-17"
parent: THREE.Scene
position: THREE.Vector3
properties: Object
quaternion: THREE.Quaternion
receiveShadow: false
renderDepth: null
rotation: THREE.Vector3
rotationAutoUpdate: true
scale: THREE.Vector3
up: THREE.Vector3
useQuaternion: false
visible: true
__proto__: Object
До сих пор мы рассматривали следующие функции для сцены:
Scene.Add()
: этот метод добавляем объект на сценуScene.Remove(
): этот метод удаляет объект со сценыScene.children()
: этот метод получает список всех детей объекта сценаScene.getChildByName()
: этот метод получает конкретный объект со сцены по его атрибуту name
Это самые важные функции сцены, которые вы будете применять довольно часто. Однако, есть несколько вспомогательных функций, которые могут вам пригодиться, и я хотел бы познакомить вас с ними на примере кода, который обрабатывает вращение куба.
Как вы уже видели в предыдущей главе, мы использовали цикл внутри функции
render
для визуализации сцены. Давайте взглянем на этот же фрагмент кода для этого примера:function render() {
stats.update();
scene.traverse(function(e) {
if (e instanceofTHREE.Mesh&& e != plane ) {
e.rotation.x+=controls.rotationSpeed;
e.rotation.y+=controls.rotationSpeed;
e.rotation.z+=controls.rotationSpeed;
}
});
requestAnimationFrame(render);
renderer.render(scene, camera);
}
Здесь мы можем видеть, что используется функция
THREE.Scene.traverse()
. Мы можем передать функцию в качестве аргумента функции. Это будет передаваться в функцию и вызываться у каждого ребенка сцены. В функции render
() мы будем использовать функцию traverse
() для обновления вращения для каждого экземпляра cube
(мы явно игнорируем нашу основную плоскость). Мы бы могли сделать это с помощью перебора всех элементов массива по свойству children
в цикле for
.Прежде чем погрузиться в детали объектов
Mesh
и Geometry
, я бы хотел показать вам два интересных свойства, которые можно установить на объекте сцены: fog
и overrideMaterial
.Добавляем эффект тумана на плоскость
Свойство
fog
позволяет добавлять эффект тумана на нашу сцену. Чем дальше объект, тем больше он будет скрыт от глаз. На следующем скриншоте показано как это выглядит:![](https://habrastorage.org/getpro/habr/post_images/bcf/87c/48e/bcf87c48e7bbab891af657d61c839a28.png)
Работающий пример.
Включение свойства тумана происходит в библиотеке Three.js происходит очень легко. Просто добавьте следующую строку кода после того как вы определили свою сцену:
scene.fog=new THREE.Fog( 0xffffff, 0.015, 100 );
Здесь мы определили цвет тумана как белый (0xffffff). Последние два аргумента могут быть изменены уже после того, как туман появится. Значение 0,015 устанавливается для свойства
near
, а 100 для свойства far
. С помощью этих двух свойств вы можете определить, где туман будет начинаться и как быстро он будет становиться более плотным. Также существует другой способ установить туман для сцены: для этого нам понадобится следующее:scene.fog=new THREE.FogExp2( 0xffffff, 0.015 );
На этот раз мы не указывали свойства
near
и far
, а только цвет и плотность тумана. А вообще лучше самим поиграться, чтобы разобраться с этими свойствами, и вы получите тот эффект, который захотите.Использование свойства overrideMaterial
Последнее свойство, которое мы рассмотрим, будет свойство
overrideMaterial
, оно используется для установки материала всех объектов на сцене. Если вы используете это свойство, то, как показано ниже, все объекты, которые вы добавляете на сцену, будут иметь один и тот же материал:scene.overrideMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
Результат приведен ниже:
![](https://habrastorage.org/getpro/habr/post_images/7b0/74b/82d/7b074b82df26352533cebbe01d3646c0.png)
На этом скриншоте хорошо видно, что все экземпляры объекта cube визуализированы с помощью одного и того же материала и цвета. В этом примере мы использовали объект
MeshLambertMaterial
в качестве основного материала.В этом разделе мы рассмотрели первую из основных концепций библиотеки Three.js: сцену. Самое главное, что надо помнить, это то, что это в основном контейнер для всех объектов, света и камеры, которые вы хотите отобразить. Напомним, наиболее важные функции и атрибуты объекта
Scene
.add(object)
: добавляет объект на сцену, вы также можете использовать эту функцию для создания группы объектов.сhildren
: возвращает список всех объектов, которые были добавлены на сцену, в том числе камеру и свет.getChildByName(name
): когда вы создаете объект, вы можете дать ему имя с помощью атрибутаname
. ОбъектScene
имеет функцию, которую можно использовать для непосредственного возвращения объекта с определенным именем.remove(object)
: если у вас есть ссылки на объект на сцене, то вы можете удалить его со сцены, используя эту функциюtraverse(function)
: атрибутchildren
возвращает список всех объектов на сцене. С помощью функцииtraverse
можно получить доступ к этим объектам, передавая функцию обратного вызова в качестве аргумента.overrideMaterial
: с помощью этого свойства можно сделать так, чтобы все объекты на сцене использовали один и тот же материал.
В следующих разделах мы внимательно рассмотрим объекты, которые можно добавить на сцену.
Работа с геометрией и Mesh-объектами
В каждом из примеров, которые мы видели, использовалась геометрия и
Mesh
объекты. Например, чтобы добавить объект sphere
на сцену, мы делали следующее:var sphereGeometry = new THREE.SphereGeometry(4,20,20);
var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff);
var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
Мы определили форму объекта, его геометрию, как этот объект должен выглядеть, свойство его материала, затем все это соединили и добавили на сцену. В этом разделе мы немного подробнее рассмотрим геометрические и соединительные объекты, начнем с геометрических.
Свойства и функции геометрии объектов
Библиотека Three.js поставляется с большим набором готовых геометрических объектов, которые можно добавлять на 3D-сцену. Просто добавьте материал, создайте переменную
mesh
и все в принципе готово. Приведем небольшой пример:![](https://habrastorage.org/getpro/habr/post_images/e1e/065/a83/e1e065a836763081672547b3e2560cb0.png)
Геометрические объекты в Three.js, как и в большинстве других 3D-библиотек, в основном это коллекция точек в пространстве, а так же ряд граней, связывающих все эти точки вместе. Возьмем, к примеру, куб:
- у куба есть восемь вершин. Каждая из этих вершин может быть определена как комбинация координат х, у и z. Итак, каждый куб имеет восемь точек в пространстве. В библиотеке Three.js эти точки называются вершинами.
- у куба есть шесть сторон с одной вершиной в каждом из углов. В библиотеке Thee.js каждая из этих сторон называется гранью.
При использовании библиотеки Thee.js, когда вы создаете геометрический объект, вы не должны определять все вершины и грани сами. Для куба вам нужно определить всего лишь его высоту, ширину и глубину. Библиотека Thee.js использует эту информацию и создает правильный геометрический объект с восьмью вершинами в нужном положении. Несмотря на то, что, обычно, при создании геометрических объектов используют стандартные объекты, предложенные библиотекой, вы все равно можете создавать объекты вручную, определив вершины и грани, как показано ниже:
var vertices = [
new THREE.Vector3(1,3,1),
new THREE.Vector3(1,3,-1),
new THREE.Vector3(1,-1,1),
new THREE.Vector3(1,-1,-1),
new THREE.Vector3(-1,3,-1),
new THREE.Vector3(-1,3,1),
new THREE.Vector3(-1,-1,-1),
new THREE.Vector3(-1,-1,1)
];
var faces = [
new THREE.Face3(0,2,1),
new THREE.Face3(2,3,1),
new THREE.Face3(4,6,5),
new THREE.Face3(6,7,5),
new THREE.Face3(4,5,1),
new THREE.Face3(5,0,1),
new THREE.Face3(7,6,2),
new THREE.Face3(6,3,2),
new THREE.Face3(5,7,0),
new THREE.Face3(7,2,0),
new THREE.Face3(1,3,4),
new THREE.Face3(3,6,4),
];
var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeCentroids();
geom.mergeVertices();
Этот код показывает, как создать простой куб. Мы определили точки, которые составляют куб в массив вершин. Эти точки соединяются, чтобы получились треугольные грани, которые хранятся в массиве граней. Например, элемент
newTHREE.Face3(0,2,1)
создает треугольную грань с помощью точек 0, 2 и 1 из массива вершин.На следующем сриншоте показан пример, при помощи которого можно поиграть с позициями вершин:
![](https://habrastorage.org/getpro/habr/post_images/086/578/165/086578165db4e3e69bc0b40a92d211a7.png)
Работающий пример.
Этот пример, как и все рассмотренные ранее примеры, использует цикл внутри функции
render
. Всякий раз, когда вы меняете что-либо в раскрывающемся блоке управления, куб отображается правильно, на основе изменений положения одной из его вершин. Для повышения производительности, библиотека Three.js предполагает, что геометрия координатной сетки не будет меняться с течением времени. Чтобы убедиться, что наш пример работает необходимо добавить следующие строки кода внутрь цикла функции render
:mesh.geometry.vertices=vertices;
mesh.geometry.verticesNeedUpdate=true;
mesh.geometry.computeFaceNormals();
В первой строке данного фрагмента кода мы обновляем массив отображаемых вершин в объекте
mesh
. Нам не нужно перенастраивать грани, так как они по-прежнему ограничены теми же точками, что и раньше. После того как мы обновили массив вершин, мы должны явно сказать, что массив вершин необходимо обновить. Мы можем сделать — это установив свойство verticesNeedUpdategeometry
в true
. Наконец, мы делаем перерасчет граней полностью обновленной модели с помощью функции computeFaceNormals
().Последняя функциональность
geometry
, которую мы будем рассматривать – это функция clone
(). Мы упомянули, что geometry
определяет форму объекта, и в сочетании с материалом мы можем создать объект, который может быть добавлен на сцену, а потом отрисован средствами библиотеки Three.js. С помощью функции clone
(), как следуем из названия, мы можем сделать копию геометрии и использовать ее для создания другого mesh
-объекта с другим материалом. В предыдущем примере, вы можете увидеть кнопку clone в верхней части управляющего интерфейса, как показано на следующем скриншоте:![](https://habrastorage.org/getpro/habr/post_images/0a3/808/602/0a3808602e3b52bf7377f635869bc97b.png)
Если вы нажмете на эту кнопку, то появится клон, который будет иметь ту же геометрию, что и старый объект в настоящее время, и новый объект создается из другого материала и добавляется на сцену. Код для этого довольно тривиален, но сделан немного сложнее из-за материалов, которые мы используем. Для начала давайте шаг за шагом разберем код, который был использован для создания материалов для куба:
var materials = [
new THREE.MeshLambertMaterial( { opacity:0.6,
color: 0x44ff44,transparent:true } ),
new THREE.MeshBasicMaterial( { color: 0x000000,
wireframe: true } )
];
Как можно увидеть мы использовали не один материал, а массив из двух материалов. Причина в том, что помимо показа прозрачного куба, мне также хотелось показать вам каркас, так как он ясно показывает, где расположены вершины и грани. Библиотека Three.js конечно же поддерживает использование нескольких материалов при создании
mesh
-объекта. Для этого вы можете использовать функцию SceneUtils.createMultiMaterialObject()
как показано ниже:var mesh = THREE.SceneUtils.createMultiMaterialObject(geom,materials);
В этой функции библиотека Three.js делает то, что она не создает один экземпляр
THREE.Mesh
, а создает один экземпляр для каждого материала, который вы указали, и помещает все эти объекты в группу. Эта группа может быть использована также как, как и другие объекты на сцене. Вы можете добавлять сетки, получать сетки по имени и так далее. Например, чтобы добавить тени для всех детей в этой группе, мы сделаем следующее:mesh.children.forEach(function(e) {e.castShadow=true});
Теперь вернемся к функции clone(), которую мы обсуждали ранее:
this.clone = function() {
var cloned = mesh.children[0].geometry.clone();
var materials = [
new THREE.MeshLambertMaterial( { opacity:0.6,
color: 0xff44ff, transparent:true } ),
new THREE.MeshBasicMaterial({ color: 0x000000,
wireframe: true } )
];
var mesh2 = THREE.SceneUtils.createMultiMaterialObject(cloned,materials);
mesh2.children.forEach(function(e) {e.castShadow=true});
mesh2.translateX(5);
mesh2.translateZ(5);
mesh2.name="clone";
scene.remove(scene.getChildByName("clone"));
scene.add(mesh2);
}
Этот кусок кода вызывается, при клике на кнопку clone. Здесь мы клонируем геометрию первого ребенка куба. Помните, что переменная
mesh
состоит из двух детей: сетки, которая использует MeshLambertMaterial
и сетки, которая использует MeshBasicMaterial
. Исходя из этой клонированной геометрии, мы создали новую сетку и назвали ее mesh2
.Функции и атрибуты сетки
Мы уже выяснили, что для создания mesh нам необходима geometry и один или несколько материалов. Как только мы получили сетку, мы можем добавить ее на сцену и отрендерить. Есть несколько свойств, которые вы можете использовать, чтобы повлиять на то, где появится эта сетка на сцене. В первом примере мы рассмотрим следующий набор свойств и функций:
position
: определяет положение этого объекта по отношению к позиции его родителя. Чаще всего родителем объекта является объектTHREE.Scene()
rotation
: c помощью этого свойства можно задать вращение объекта вокруг одной из его осейscale
: это свойство позволяет масштабировать объект по осям х, у и ztranslateX(amount)
: перемещает объект на указанное значение вдоль оси хtranslateY(amount
): перемещает объект на указанное значение вдоль оси уtranslateZ(amount)
: перемещает объект на указанное значение вдоль оси z
Как всегда, мы рассмотрим готовый пример, который позволяет поиграть всеми этими свойствами, он приведен на следующем скриншоте:
![](https://habrastorage.org/getpro/habr/post_images/baa/e44/3b7/baae443b7dd823c3178ba8dae8b7b96c.png)
Работающий пример.
Позвольте мне рассказать вам о них; начну я со свойства
position
. Мы уже встречали это свойство пару раз, так что давайте быстренько закрепим его. С помощью этого свойства можно задать координаты х, у и z объекта. Положение объекта зависит от его родительского объекта, которым обычно выступает сцена. Мы можем установить свойство position объекта тремя различными способами; каждая из координат может быть непосредственно установлена следующим образом:cube.position.x=10;
cube.position.y=3;
cube.position.z=1;
Но также мы можем сделать это одной командой:
cube.position.set(10,3,1);
Существует также третий вариант. Свойство
position
по своей сути это объект THREE.Vector.
Это означает, что мы также можем сделать следующее, чтобы расположить объект в нужном месте:cube.postion=new THREE.Vector3(10,3,1)
Мне хотелось бы сделать небольшой отступ переде тем, как продолжить рассматривать другие свойства
mesh
. Я упомянул, что позиция объекта устанавливается в зависимости от положения его родителя. В предыдущем разделе, посвященному THREE.Geometry
для создания многосоставных объектов мы использовали функцию THREE.SceneUtils.createMultiMaterialObject
. Еще раз остановлюсь на том, что на самом деле эта функция возвращает не одну сетку, а целую группу, которая содержит отдельную сетку для каждого материала, основанную на той же геометрии. В нашем случае эта группа состоит из двух сеток. Если мы изменим позицию одной из сеток, которая будет создана, то можно будет явно увидеть, что это действительно отдельный объект. Однако, если мы теперь переместим созданную группу по кругу, то смещение останется прежним. Эти две сетки показаны на следующем скриншоте:![](https://habrastorage.org/getpro/habr/post_images/3c6/618/de2/3c6618de23b0f68f3b23cbf97fb9dba7.png)
Ок, следующим в списке является свойство
rotation
. Вы уже заметили, что мы использовали уже это свойство пару раз в этом, а также в предыдущих примерах. С помощью этого свойства можно задать вращение объекта вокруг одной из его осей. Вы можете установить это значение таким же образом, как мы это делали для свойства position
. Полный оборот, как вы должны помнить со школы это 2pi. В следующем примере можно посмотреть, как это настраивается:cube.rotation.x=0.5*Math.PI;
cube.rotation.set(0.5*Math.PI,0,0);
cube.rotation = new THREE.Vector3(0.5*Math.PI,0,0);
Следующим свойством из нашего оговоренного списка является свойство
scale
. В общем и целом название этого свойство говорит само за себя. Вы можете масштабировать объект по определенной оси. Если установить значение меньшее чем один, то объект будет уменьшаться, как показано ниже:![](https://habrastorage.org/getpro/habr/post_images/a74/842/94c/a7484294c1b938b6a878a5a8c85a3b27.png)
При использовании значений, больших единицы объект становится больше, это продемонстрировано на следующем скриншоте:
![](https://habrastorage.org/getpro/habr/post_images/6ff/c9a/7c1/6ffc9a7c135449f59987db93e09d4968.png)
В последнюю очередь в этой главе мы поговорим о перемещающих функциях. С помощью перемещения вы также можете изменить положение объекта, но вместо определения его абсолютного положения, в котором вы хотите, чтобы он находился, вы определяете, куда объект должен переместиться относительно его настоящего положения. Например, у нас есть объект
sphere
, который добавлен на сцену имеет координаты (1,2,3). Далее мы переместим этот объект вдоль оси х с помощью функции translateX
(4). Теперь он имеет координаты (5,2,3). Если мы хотим, чтобы он принял первоначальное положение, то должны вызвать translateX
(-4). Используя предыдущий пример, вы можете поиграть с этими свойствами.Использование различных камер
В библиотеке Three.js есть два вида камер: ортогональная камера и перспективная. В этот раз мы ограничимся рассмотрением основ работы с этими объектами. Лучший способ объяснить разницу между этими камерами – это посмотреть на пару примеров.
Ортогональная камера по сравнению с перспективной камерой
В примерах для этой главе вы можете найти пример под названием
07-both-cameras.html
. При открытии этого примера, вы увидите что-то вроде следующем скриншоте:![](https://habrastorage.org/getpro/habr/post_images/bb2/cb9/aa8/bb2cb9aa861ac3cc33ecac129bf89f5a.png)
Это называется перспективным видом и является наиболее естественным. Как можно видеть из этого скриншота, чем дальше находятся кубы от камеры, тем меньше они оказываются.
Если же мы изменим камеру на другой тип, поддерживаемый библиотекой Three.js, то есть на ортогональную, то увидим примерно следующее:
![](https://habrastorage.org/getpro/habr/post_images/1fd/ae1/0da/1fdae10da622b4b3b00ad338da8f4719.png)
Работающий пример.
С ортогональной камеры все кубики оказываются одного размера; расстояние между объектом и камерой не имеет значения. Это часто используется в 2D-играх, таких как SimCity 4 и ранних версиях Цивилизации, как показано на следующем скриншоте:
![](https://habrastorage.org/getpro/habr/post_images/539/0e3/ed1/5390e3ed12363aa6056051c272e85281.png)
Чаще всего в наших примерах мы будем использовать перспективную камеру, так как это наиболее близко к реальному миру. Переключение камеры на самом деле очень просто. Следующий фрагмент коде показывает, что происходит, когда вы щелкаете по кнопке switchCamera в нашем последнем примере:
this.switchCamera = function() {
if (camera instanceof THREE.PerspectiveCamera) {
camera = new THREE.OrthographicCamera(
window.innerWidth / - 16, window.innerWidth / 16,
window.innerHeight / 16, window.innerHeight / - 16,
-200, 500 );
camera.position.x = 2;
camera.position.y = 1;
camera.position.z = 3;
camera.lookAt(scene.position);
this.perspective = "Orthographic";
}
else {
camera = new THREE.PerspectiveCamera(45,
window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 120;
camera.position.y = 60;
camera.position.z = 180;
camera.lookAt(scene.position);
this.perspective = "Perspective";
}
};
В этом листинге можно увидеть, что есть разница в том, как мы создаем объект
THREE.PerspectiveCamera
в отличие от объекта THREE.OrthographicCamera
. Для начала давайте разберем объект THREE.PerspectiveCamera
. Он принимает следующие аргументы:fov
: выступает как поле зрения. Эта та часть сцены, которую можно увидеть с позиции камеры. Например, у людей поле зрения составляет почти 180 градусов, в то время как у некоторых птиц оно достигает 360 градусов. Но так как стандартный экран компьютера не полностью заполняет наше поле зрения, то, как правило, выбирается меньшее значение. Чаще всего, для игр, поле зрения колеблется от 60 до 90 градусов. По умолчанию это величина равна 45 градусам.aspect
: это аспект соотношения между горизонтальным и вертикальным размерами площади, куда будут отображаться выходные данные. Соотношение сторон определяет разницу между горизонтальным и вертикальным полем зрения, как это будет показано на рисунке ниже. Значения по умолчанию:window.innerWidth/window.innerHeight
.near
: это свойство определяет то, как близко к камере библиотека Three.js должна визуализировать сцену. Обычно это значение очень мало, так как мы отображаем все объекты, заранее определяя расстояние до них. По умолчанию эта величина равна 0,1far
: это свойство определяет как далеко камера может видеть с позиции камеры. Если мы установим значение этого свойства как очень маленькое, то часть нашей сцены может не отобразиться, а если слишком большое, то это может повлиять на производительность рендеринга. Значение по умолчанию: 1000.
На следующем рисунке дается хороший обзор того, как эти свойства работают вместе:
![](https://habrastorage.org/getpro/habr/post_images/210/c37/f95/210c37f95c41ea0ae56fbdd915607990.png)
Чтобы настроить ортогональную камеру, мы должны использовать другие свойства. Ортогональная проекция не зависит от пропорций, которые мы используем или от поля зрения, с которым мы смотрим на сцену. Все объекты отображаются в одном размере. Для ортогональной камеры нам необходимо определить куб, который должен быть отображен. Рассмотрим, какими свойствами обладает объект
OrthographicCamera
:left
: в документации по Three.js это описано как усечение камеры левой плоскостью. Вы можете понимать это как левая граница. То есть, если мы установим это значение в -100, то не увидим то, что находится левее.right
: то же самое, что и для свойстваleft
, но на этот раз для противоположной стороны экрана. Все, что правее отображаться не будет.top
: верхняя граница отображенияbottom
: нижняя граница отображенияnear
: из этой точки, в зависимости от положения камеры, будет отображена сценаfar
: до этой точки, в зависимости от положения камеры, будет отображена сцена.
Все эти свойства показаны на следующем рисунке:
![](https://habrastorage.org/getpro/habr/post_images/5ff/8f6/168/5ff8f616881e469cc470ae754ec6a159.png)
Фокусировка камеры на определенной точке
До сих пор мы рассматривали, как создавать камеру и что означают различные ее свойства. В предыдущей главе мы также узнали, что камеру необходимо разместить где-то на сцене и что камера будет указывать на центр отображаемой сцены. Обычно камера направлена к центру сцены, используя координаты:
position (0,0,0)
. Однако мы можем легко изменить точку, на которую смотрит камера, это делается следующим образом:camera.lookAt(new THREE.Vector3(x,y,z));
Рассмотрим пример, когда камера перемещается, глядя на помеченную красным точку, это показано на следующем скриншоте:
![](https://habrastorage.org/getpro/habr/post_images/d5d/c35/025/d5dc35025fb34fc03c3df2e37157344e.png)
Работающий пример.
Если вы откроете пример 08-cameras-lookat.html, то увидите, что сцена движется слева направо. На самом деле сцена не движется. Это камера смотрит с разных точек, что дает эффект движения сцены слева направо. В этом примере вы также можете переключиться на ортогональную камеру. Здесь вы увидите, какие произошли изменения и поймете отличия от перспективной камеры.
Заключение
В этой второй вводной главе мы рассмотрели много пунктов, и это должно дать вам хороший обзор того, что такое сцена и чем являются наиболее важные компоненты на ней. В ближайших пару главах мы будем более глубоко рассматривать детали библиотеки Three.js. Ниже приведены некоторые из пунктов, которые вы должны запомнить из этой главы:
- Сцена – главный контейнер в библиотеке Three.js. На нее вы можете добавлять любые объекты.
- У сцены есть немного специальных опций и свойств. Наиболее важные из них позволяют добавлять объекты, удалять их и работать с таким атрибутом сцены как
children
. - Вы легко можете добавить туман на сцену и настроить любой из предлагаемых параметров этого свойства.
- Геометрия и сетки работают в тесном взаимодействии друг с другом. Геометрия определяет форму объекта, а в сочетании с материалом, вы можете создать сетку.
- Библиотек Three.js поставляется с большим набором стандартных геометрий. Однако, можно создавать и свои объекты, но это очень трудоемко, если не сделано через специальные алгоритмы.
- Можно программно управлять положением, поворотом и масштабом сетки.
- С помощью свойства
translate
вы можете перемещать сетку относительно ее текущего положения. - Чтобы отобразить сцену, нам нужна камера. В библиотеке Three.js есть два различных вида камер: перспективная и ортогональная.
- Перспективная камера отображает сцену с точки зрения реального мира.
- Ортогональная камера делает все объекты одинаково размера и не принимает во внимание расстояние от камеры до объектов.
В следующей главе мы рассмотрим различные источники света, которые доступны в библиотеке Three.js. Вы узнаете, как ведут себя различные источники света, как их создавать и настраивать.
Ссылка на GitHub
P.S. Все недочеты перевода прошу присылать в ЛС.