Статьи

WebGL с Three.js: основы

3D-графика в браузере была горячей темой с момента ее появления. Но если бы вы создавали свои приложения с использованием простого WebGL, это заняло бы много лет. Именно поэтому недавно появились действительно полезные библиотеки. Three.js является одним из самых популярных, и в этой серии я покажу вам, как лучше всего использовать его для создания потрясающих 3D-впечатлений для ваших пользователей.

Прежде чем мы начнем, я ожидаю, что у вас будет общее представление о трехмерном пространстве, прежде чем вы начнете читать этот учебник, поскольку я не буду объяснять такие вещи, как координаты, векторы и т. Д.


Сначала создайте три файла: index.html , main.js и style.css . Теперь загрузите Three.js (весь zip-файл с примерами и исходным кодом или только файл JavaScript , на ваш выбор). Теперь откройте index.html и вставьте этот код:

01
02
03
04
05
06
07
08
09
10
<!DOCTYPE html>
<html>
<head>
    <link rel=»stylesheet» href=»./style.css»>
    <script src=»./three.js»></script>
</head>
<body>
    <script src=»./main.js»></script>
</body>
</html>

Это все, что вам нужно в этом файле. Просто объявление скриптов и таблицы стилей. Вся магия произойдет в main.js , но прежде чем мы доберемся до этого, нам нужно еще один трюк, чтобы приложение выглядело хорошо. Откройте style.css и вставьте этот код:

1
2
3
4
5
canvas {
    position: fixed;
    top: 0;
    left: 0;
}

Это поместит холст в левый верхний угол, потому что по умолчанию body будет иметь 8px поля. Теперь мы можем перейти к коду JavaScript.


Three.js использует концепцию списка отображения. Это означает, что все объекты сохраняются в списке и затем выводятся на экран.

Three.js использует концепцию списка отображения. Это означает, что все объекты сохраняются в списке и затем выводятся на экран. Здесь это объект THREE.Scene . Вам нужно добавить любой объект, который вы хотите нарисовать на экране к сцене. Вы можете иметь столько сцен, сколько хотите, но один рендерер может нарисовать только одну сцену одновременно (конечно, вы можете переключать отображаемую сцену).

Средство визуализации просто рисует все от сцены до холста WebGL. Three.js также поддерживает рисование на SVG или 2D Canvas, но мы сосредоточимся на WebGL.

Для начала давайте сохраним ширину и высоту окна в переменных, мы будем использовать их позже:

1
2
var width = window.innerWidth;
var height = window.innerHeight;

Теперь определите рендер и сцену:

1
2
3
4
5
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
 
var scene = new THREE.Scene;

Первая строка определяет рендерер WebGL. Вы можете передать параметры рендерера в первом аргументе как карту. Здесь мы устанавливаем antialias в true, потому что мы хотим, чтобы края объектов были гладкими, а не неровными.

Во второй строке размер рендерера задается равным размеру окна, а в третьей мы добавляем элемент canvas рендерера в документ (вы также можете сделать это с помощью библиотеки, например, jQuery: $('body').append(renderer.domElement) ).

Последний определяет сцену, аргументы не нужны.


Теперь давайте добавим что-нибудь для рисования. Пусть это будет куб, так как это самый простой трехмерный объект. В Three.js объекты, которые рисуются на экране, называются сетками. Каждая сетка должна иметь свою геометрию и материал. Геометрия — это набор точек, которые необходимо соединить для создания объекта. Материал — это просто краска (или живопись, но это не тема этого урока), которая будет покрывать объект. Итак, давайте создадим наш куб. К счастью, в Three.js есть несколько вспомогательных функций для создания примитивов (простых фигур):

1
2
3
4
5
6
7
var cubeGeometry = new THREE.CubeGeometry(100, 100, 100);
var cubeMaterial = new THREE.MeshLambertMaterial({ color: 0x1ec876 });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
 
cube.rotation.y = Math.PI * 45 / 180;
 
scene.add(cube);

Как видите, сначала мы создаем геометрию. Аргументы определяют размер куба: ширину, высоту и глубину.

Далее мы определяем материал куба. В Three.js есть несколько типов материалов, но на этот раз мы будем использовать THREE.MeshLambertMaterial , так как мы хотим иметь некоторое освещение позже (этот материал использует алгоритм Ламберта для расчета освещенности). Вы можете передать параметры в первом аргументе в виде карты, так же, как в случае с рендерером — это в значительной степени правило для более сложных объектов в Three.js. Здесь мы используем только цвет, который передается как шестнадцатеричное число.

В третьей строке мы создаем сетку, используя геометрию и материал, созданный ранее. Затем мы поворачиваем куб на 45 градусов по оси Y, чтобы он выглядел лучше. Мы должны изменить градусы на радианы, что определяется уравнением, которое вы, вероятно, помните из своего урока физики в старшей школе: Math.PI * 45 / 180 . Наконец, куб добавлен в сцену.

Теперь вы можете открыть index.html в вашем браузере, чтобы увидеть результаты, но вы ничего не увидите, потому что сцена еще не отрисована.


Чтобы что-то визуализировать, сначала нам нужно добавить камеру на сцену, чтобы рендерер знал, с какой точки зрения он должен рендерить материал. В Three.js есть несколько типов камер, но вы, вероятно, будете использовать только THREE.PerspectiveCamera . Этот тип камеры представляет сцену, когда мы видим наш мир. Давайте создадим один:

1
var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);

«Чтобы что-то визуализировать, сначала нам нужно добавить камеру на сцену, чтобы рендерер знал, с какой точки зрения он должен рендерить материал».

Создание камеры немного сложнее, чем остальные вещи, которые мы делали до сих пор. Первый аргумент определяет FOV (поле зрения), угол, который можно увидеть с того места, где находится камера. FOV 45 градусов выглядит естественно. Далее мы определяем соотношение камеры. Это всегда ширина рендерера, деленная на его высоту, если только вы не хотите добиться каких-либо специальных эффектов. Последние два числа определяют, насколько близко и насколько далеко объект может быть отрисован.

Теперь нам нужно немного переместить камеру назад и вверх, так как все объекты, созданные в Three.js, имеют положение, установленное в середине сцены (x: 0, y: 0, z: 0) по умолчанию:

1
2
camera.position.y = 160;
camera.position.z = 400;

Координата z положительна в направлении зрителя, поэтому объекты с более высокой позицией z будут отображаться ближе к вам (в этом случае, поскольку мы переместили камеру, все объекты будут появляться дальше от вас).

Теперь давайте добавим камеру к сцене и отрендерим ее:

1
2
3
scene.add(camera);
 
renderer.render(scene, camera);

Вы добавляете камеру так же, как вы добавили куб. Следующая строка отображает сцену с помощью этой камеры. Теперь вы можете открыть браузер, и вы должны увидеть следующее:

first_rendering

Вы должны видеть только верхнюю часть куба. Это потому, что мы переместили камеру вверх, и она все еще смотрит прямо перед ней. Это можно исправить, сообщив камере о том, в каком положении она должна выглядеть . Добавьте эту строку после строк, устанавливающих положение камеры:

1
camera.lookAt(cube.position);

Единственный переданный аргумент — это позиция, на которую будет смотреть камера. Теперь сцена выглядит лучше, но куб все еще черный, независимо от того, какой цвет вы установили при его создании:

fixed_camera_lookat

Куб чёрный, потому что на сцене нет света, поэтому он похож на полностью чёрную комнату. Вы видите белый фон, потому что нечего рисовать, кроме куба. Чтобы избежать этого, мы будем использовать технику, называемую скайбокс. По сути, мы добавим большой куб, который будет отображать фон сцены (обычно какой-то дальний ландшафт, если это открытое пространство). Итак, давайте создадим коробку. Этот код должен идти перед вызовом renderer.render :

1
2
3
4
5
var skyboxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
var skyboxMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.BackSide });
var skybox = new THREE.Mesh(skyboxGeometry, skyboxMaterial);
 
scene.add(skybox);

Этот код похож на тот, который создает куб. Но на этот раз геометрия намного больше. Мы также использовали THREE.MeshBasicMaterial так как нам не нужно зажигать скайбокс. Также обратите внимание на дополнительный аргумент, передаваемый материалу: side: THREE.BackSide . Поскольку куб будет отображаться изнутри, мы должны изменить сторону, которая будет нарисована (обычно Three.js рисует только внешние стены).

Теперь визуализированная сцена полностью черная. Чтобы исправить это, мы должны добавить свет на сцену. Мы будем использовать THREE.PointLight , который излучает свет, как лампочка. Добавьте эти строки после скайбокса:

1
2
3
4
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(0, 300, 200);
 
scene.add(pointLight);

Как вы можете видеть, мы создали точечный источник света белого цвета, затем устанавливаем его положение немного вверх и назад, чтобы осветить переднюю и верхнюю часть куба. Наконец свет добавляется к сцене, как и любой другой объект. Откройте браузер, и вы должны увидеть цветной, затененный куб:

colored_shaded_cube

Но куб все еще довольно скучный. Давайте добавим немного движения к нему.


Теперь мы добавим немного движения на сцену. Давайте заставим куб вращаться вокруг оси Y. Но сначала мы должны изменить способ рендеринга сцены. Один вызов renderer.render визуализирует текущее состояние сцены один раз. Поэтому, даже если мы каким-то образом оживим куб, мы не увидим его движения. Чтобы изменить это, мы должны добавить цикл рендеринга в наше приложение. Это может быть достигнуто с renderAnimationFrame функции renderAnimationFrame , которая была создана специально для этой цели. Он поддерживается в большинстве основных браузеров, а для тех, кто его не поддерживает, Three.js поставляется с собственным полифилом. Итак, давайте изменим это:

1
renderer.render(scene, camera);

к этому:

1
2
3
4
5
6
7
function render() {
    renderer.render(scene, camera);
     
    requestAnimationFrame(render);
}
 
render();

На самом деле, там нет петли, потому что это приведет к зависанию браузера. Функция requestAnimationFrame ведет себя немного как setTimeout , но она вызывает функцию, переданную так быстро, как браузер готов. Таким образом, в отображаемой сцене ничего не изменилось, и куб все еще не движется. Давайте исправим это. Three.js поставляется с THREE.Clock который можно использовать для достижения плавной анимации объектов. Сначала инициализируйте его перед определением функции render :

1
var clock = new THREE.Clock;

Теперь каждый раз, когда вы вызываете clock.getDelta он будет возвращать время с момента последнего вызова в миллисекундах. Это может быть использовано для вращения куба следующим образом:

1
cube.rotation.y -= clock.getDelta();

Добавьте эту строку между вызовами renderer.render и requestAnimationFrame в функции render . Это просто вычитание времени, прошедшего от вращения куба по оси Y (помните, что это в радианах), чтобы повернуть куб по часовой стрелке. Теперь откройте браузер, и вы увидите, что ваш куб плавно вращается по часовой стрелке.


В этой части серии вы узнали, как подготовить сцену, добавить объекты и источники света, а также как оживить вещи. Вы можете экспериментировать с приложением, добавлять больше или разные объекты, фонари. Тебе решать. В следующий раз я покажу вам, как использовать текстуры и как создавать приятные эффекты с частицами. Не забудьте взглянуть на документацию, если у вас возникли проблемы.