Three.js – это легковесная кроссбраузерная библиотека JavaScript, используемая для создания и отображения анимированной компьютерной 3D графики при разработке веб-приложений, обеспечивая хорошую производительность. В рамках этой статьи мы будем использовать TypeScript, что упростит работу и поможет избежать непредвиденные ошибки.
Что касается IDE, то допустимо использование любого редактора, поддерживающего JavaScript, в котором Вам будет удобно работать. В статье используется Visual Studio Code.
Установка: https://code.visualstudio.com/
Код проекта: https://github.com/mishaalexeev/Three.js-Getting-Started
1. Откройте командную строку (Win+R, введите CMD и нажмите Enter)
2. В командной строке, перейдите в папку будущего проекта (cd Название-папки), или создайте ее (mkdir Название-папки).
3. Далее, находясь в папке, пишем команду «code .», которая откроет папку в VS Code.
4. В Visual Studio Code необходимо открыть терминал (Ctrl+Shift+`) и инициализировать проект для работы с Node Package Manager (дефолтный пакетный менеджер для JavaScript, с его помощью можно управлять модулями и зависимостями.), командой «npm init». Нажимаем Enter 10 раз, при желании, указываем свое имя в строчке «author». Если все сделано правильно, в корне проекта создастся файл «package.json», содержащий в себе информацию о вашем приложении: название, версия, зависимости и так далее.
5. Затем установим TypeScript и библиотеку Three.js – пишем в терминал эти команды: «npm install -g typescript» и «npm install three» соответственно. После окончания загрузки, создастся папка «node_modules», а в файле «package.json» добавится новая строчка, содержащая информацию о версии Three.js.
6. Все в том же терминале прописываем еще несколько команд для установки модулей, которые не являются дефолтными: «npm install @types/node», «npm install express», «npm install @types/express» (всё это должно появиться в папке node_modules, нужно для работы с node.js и пригодится позднее).
7. Теперь нажмите на кнопку F1 и в открывшемся окне напишите «restart» из предложенных вариантов нужно выбрать «TypeScript: Restart TS Server», чтобы обновить привязки.
6. Продолжим создавать структуру проекта. Создадим две папки, одну назовем «dist» (в ней будут лежать скомпилированные JavaScript файлы), а другую «src» (для TypeScript файлов, с которыми мы будем работать).
7. Для «dist» и для «src» создадим 2 подпапки «client» (для клиентской части приложения) и «server» (для серверной).
8. В «src/client» создаем файл «tsconfig.json» и копируем туда код, затем создаем файл «client.ts», в котором мы и будем работать больше всего, и копируем туда код.
9. В «src/server» создаем файл «tsconfig.json» и копируем туда код, затем создаем файл «server.ts» и туда тоже вставляем код.
tsconfig.json нужны, чтобы при компилировании проекта TypeScript файлы преобразовывались в JavaScript файлы, которые будут использованы на клиентской стороне, в браузере.
10. В «dist/client» создаем файл «index.html» и копируем туда код.
11. Сравниваем получившуюся структуру с рисунком №1.
12. Можем попробовать скомпилировать и запустить приложение. В терминале пишем «tsc -p ./src/client», в папке «dist/client» создастся файл «client.js», преобразованный из файла «client.ts». Далее делаем то же самое, но уже с серверной частью кода «tsc -p ./src/server». Осталось только запустить сервер, пишем «node ./dist/server/server.js» и в браузере переходим по локальному адресу «http://127.0.0.1:3000/».
13. Безусловно, перезапускать сервер каждый раз, делая исправления в коде не очень удобно, поэтому установим Nodemon («npm install --save-dev nodemon»), который нужен для автоматического перезапуска сервера при изменениях.
14. Последним устанавливаем «npm install --save-dev concurrently» - модуль, нужный для параллельного запуска нескольких npm скриптов.
15. В файле package.json, в разделе scripts нужно вставить скрипт «"dev": "concurrently -k \"tsc -p ./src/server -w\" \"tsc -p ./src/client -w\" \"nodemon ./dist/server/server.js\"",», объединяющая 4 команды, которые мы использовали ранее. Теперь для запуска сервера достаточно одной команды «npmrun dev».dev».
"dev": "concurrently -k \"tsc -p ./src/server -w\" \"tsc -p ./src/client -w\" \"nodemon ./dist/server/server.js\"",
Scene – как можно догадаться из названия, это «сцена», или платформа на которой размещаются все объекты. Вне зависимости от того, добавлена ли физика, пол (planeGeometry), всё на экране, кроме самих фигур, называется scene.
Camera – камера «снимает» и отображает объекты, которые расположены на сцене. Самым популярным видом является PerspectiveCamera, она показывает реалистичную проекцию объектов так, как видит их человеческий глаз (чем дальше находится объект от нас, тем он кажется меньше). Существуют и другие виды камер, их можно сравнить с оптическими линзами. При добавлении камеры она статична и направлена под заданным углом, на заданном расстоянии.
Подробнее про виды камер: https://threejsfundamentals.org/threejs/lessons/ru/threejs-cameras.html
Renderer — визуализирует модели, обновляет их положение на сцене при движении или повороте камеры.
В Three.js можно строить по точкам, добавлять свои сторонние модели, но также имеется база стандартных геометрических фигур, которые можно использовать.
Подробнее про фигуры в документации: https://documentation.help/three.js-ru/geometries.htm
Mesh – полигональная сетка точек на фигуре. Используется для создания формы модели и, равномерной покраски и реалистичных теней. Чем больше точек, тем качественнее прорисовка.
Material – цвет и покраска моделей, текстуры. Существует множество видов, самым простым является MeshBasicMaterial, он не подходит для реалистичного изображения модели, так как не меняется от освещения и просто раскрашивает Mesh одним заданным цветом. MeshNormalMaterial окрашивает объект в градиент из стандартных цветов, MeshPhongMaterial учитывает освещение, что создает эффект объемности изображения.
Подробнее про материалы: https://threejsfundamentals.org/threejs/lessons/ru/threejs-materials.html
Light – источник освещения. Необходимо задать цвет и направление света. Примеры освещения: Ambient Light - равномерно подсвечивает все объекты на сцене, Directional Light – подсвечивает модели по определенному вектору, Point Light – источник света излучается из одной точки во всех направлениях.
Подробнее про освещение в документации: https://documentation.help/three.js-ru/lights.htm
Посмотрим на получившийся на этом этапе проект поподробнее, учитывая информацию, изложенную в основных понятиях. Начнем разбор в том порядке, в котором мы создавали его структуру.
В файле package.json содержатся метаданные проекта и список пакетов с версиями, необходимыми для корректной работы. Это пригодится при открытии проекта на другом компьютере. Внешние пакеты установлены в объект devDependecies, а используемые скрипты в объекте scripts, где мы видим уже знакомый нам скрипт dev, позволяющий заменить четыре терминальных команды одной для быстрого запуска проекта. «concurrently -k» позволяет запустить сразу несколько процессов, агрегирует их вывод в консоль и позволяет быстро и одновременно их завершить, если это потребуется. «tsc -p ./src/server -w» и «tsc -p ./src/client -w», как уже было разобрано ранее, компилируют соответствующие файлы TypeScript в JavaScript, а флаг -w устанавливает режим «watch», который перезапускает процесс при любом совершенном изменении. «nodemon ./dist/server/server.js» перезапускает сервер каждый раз, когда client.js или server.js меняются (что следует из предыдущего предложения).
package-lock.json создается автоматически и используется для блокировки зависимостей от определенного номера версии. Наличие этого файла необязательно, но он нужен для того, чтобы при запуске проекта на другом компьютере, при написании команды «npm i» установились корректные используемые версии пакетов с модулями.
Перейдем к папке «dist», в ней находятся две подпапки, каждая для соответствующего скомпилированного JavaScript файла. В подпапке «client», кроме этого, еще находится файл «index.html» - главная и единственная страница сайта для этого тестового приложения. В ней содержится минимальный код: заголовок, иконка сайта, в теге style задается параметр, делающий сайт на полный экран, что убирает полосу прокрутки. В теге script подключается JavaScript файл client.js. Файлы client.js и server.js – это последние откомпилированные в JavaScript версии файлов TypeScript client.ts и server.ts из папки «src».
В папке node_modules находятся модули, как дефолтные, так и дополнительные, которые мы устанавливали отдельно.
Перейдем к папке «src». В подпапках «client» и «server» находятся файлы tsconfig.json нужные для того, чтобы при компилировании проекта TypeScript файлы преобразовывались в JavaScript файлы, которые и будут отображаться в браузере. В server.ts содержится информация для запуска сервера, номер порта и, кроме того, полные пути к используемым в client.ts модулям.
Рассмотрим client.ts поподробнее, так как в нем будут происходить основные изменения и работа.
С самого начала импортируем модули, которые будут использованы. Для данного проекта достаточно двух – библиотеки Three.js для работы с 3D объектами и OrbitControls для работы с камерой.
Создаем сцену – «холст», на которому будут отображаться модели и объекты.
Затем создаем камеру. PerspectiveCamera принимает четыре аргумента:
Например, если в нашем проекте при «far=1000» установить «camera.position.z = 1001» мы увидим пустую сцену, так как объект слишком далеко от камеры.
Следующий важный элемент – WebGLRenderer, отвечающий за рендер фигуры. Используем метод SetSize, «renderer.setSize(window.innerWidth, window.innerHeight)», это растянет рендерер на все окно браузера, если заменить аргументы цифрами, обозначающими ширину и высоту соответственно, то рендерер изменит свой размер.
Далее нужно присоединить рендерер к DOM– это связывает рендерер и controls и нужно для визуализирования фигур.
«camera.position.z = 2» устанавливает первоначальное положение камеры по оси z. Число обозначает расстояние камеры до центра сцены. Можно использовать любую ось и любое расстояние, в зависимости от целей.
Следующий шаг – создание controls, что позволит из статичной камеры сделать динамичную.
Камеру можно приближать к кубу и отдалять от него колесиком мыши, перетаскивать по осям XYZ правой кнопкой и вращать левой – за это отвечает OrbitControls и константа controls. При каждом действии происходит ререндер фигуры («controls.addEventListener('change', render)»). Важно учитывать, что куб остается на месте, а положение меняет лишь камера.
Далее, используя стандартную геометрию Three.js создаем куб (BoxGeometry), задаем ему материал (MeshNormalMaterial) и добавляем на сцену («scene.add(cube)»)
Можно изменять фигуру, обозначив аргументы – например, для куба основными параметрами являются:
И дополнительными:
При «const geometry: THREE.BoxGeometry = newTHREE.BoxGeometry(1,1,2)», например, из куба получается параллелепипед. Если значение аргумента будет превышать значение положения камеры, то, получится, что камера находится внутри фигуры и рендер не произойдет. Чтобы увидеть фигуру необходимо увеличить «camera.position» или покрутить колесиком мыши, чтобы камера «вылетела» из фигуры.
Можно заменить куб на любую из доступных фигур (https://documentation.help/three.js-ru/geometries.htm) и задать один из стандартных материалов (http://blog.cjgammon.com/threejs-materials), чтобы потренироваться.
Продолжим разбирать код. За созданием фигуры следует функция onWindowResize, обеспечивающая ререндер при каждом изменении формата окна браузера.
И в самом конце функция renderer, позволяющая рисовать сцену 60 раз в секунду. Также для отрисовки можно использовать функцию animate, но renderer меньше нагружает ресурсы графического процессора и для простых проектов вполне подходит.
Функция animate, согласно названию, позволяет анимировать объекты и добавлять им движение. Раскомментируем ее. «cube.rotation.x» и «cube.rotation.y» обеспечивают вращение куба по соответствующим осям за указанное в секундах время.
В ходе статьи мы познакомились с основами Three.js, создали проект-шаблон и настроили его для работы, и немного посмотрели возможности стандартных фигур и материалов. Надеюсь, это поможет Вам сделать первые шаги в изучении этой библиотеки.
Благодарю авторов всех упомянутых статей и материалов, а также Всеволода Кочнева и Никиту Краевого.