3D-графика в браузере была предметом интереса с момента его появления. Но если бы вы создавали свои приложения, используя старый WebGL, это заняло бы много лет. Недавно появилось несколько действительно полезных библиотек. Three.js является одним из самых популярных, и в этой серии я покажу вам, как наилучшим образом использовать его, чтобы создавать потрясающие 3D-впечатления для ваших пользователей.
Я ожидаю, что у вас будет общее представление о трехмерном пространстве, прежде чем вы начнете читать этот учебник, так как я не буду объяснять такие вещи, как координаты, векторы и т. Д.
Слово о шейдерах
Если вы уже знаете, что такое шейдеры, вы можете пропустить этот шаг. Шейдеры — это программы, написанные на GLSL (язык сценариев графического слоя), которые выполняются на графическом процессоре. Это делает их чрезвычайно полезными, поскольку мы можем взять некоторую работу с ЦП и поместить ее в ГП для повышения производительности. Существует два вида: вершинный и фрагментный шейдеры. Вершинные шейдеры используются для изменения структуры объекта (перемещения вершин), а фрагментные шейдеры вносят изменения в рисуемые пиксели.
Шаг 1: Вершинный шейдер
Начнем с более простого. Этот шейдер изменит расположение векторов в сетке, что приведет к движению граней. Вставьте этот код в <head>
вашего приложения:
01
02
03
04
05
06
07
08
09
10
|
<script id=»cubeVertexShader» type=»x-shader/x-vertex»>
uniform float time;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 newPosition = position + normal * vec3(sin(time * 0.2) * 3.0);
gl_Position = projectionMatrix <i> modelViewMatrix </i> vec4(newPosition, 1.0);
}
</script>
|
Атрибут type
этого скрипта не будет понят браузером, поэтому он не будет выполнен (мы передадим его содержимое в материал Three.js позже). В первых двух строках мы определяем две переменные. Первый — равномерное float time
. Униформа передается как вершинным, так и фрагментным шейдерам. Далее есть разные vec2 vUv
. Изменения — это интерфейс между вершиной и фрагментным шейдером. time
будет содержать время в миллисекундах с момента запуска приложения, которое мы будем использовать для вычисления новых позиций вершин. В vUv
мы будем хранить UV (вектор текстуры) каждой вершины, чтобы мы могли использовать его в фрагментном шейдере.
Далее следует объявление void main()
. Все шейдеры должны иметь эту функцию. Здесь мы передаем UV вершины нашему vUv
и вычисляем новое положение вершины. Наконец, мы устанавливаем gl_Position
, который фактически устанавливает положение вершины. Но мы также должны умножить рассчитанную ранее позицию на projectionMatrix
и modelViewMatrix
, две матрицы, которые Three.js предоставляет нам. Это необходимо, потому что, если мы этого не сделаем, графический процессор не будет учитывать точку, с которой мы смотрим на вершину. Теперь перейдем к фрагментному шейдеру.
Шаг 2: Фрагмент шейдера
Теперь это место, где происходит вся магия. Фрагментные шейдеры отвечают за все эти красивые игры. Тот, который мы будем использовать, довольно прост, поэтому не ожидайте увидеть сцену из Crysis 3 после ее использования. Вставьте следующий код под ваш вершинный шейдер:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<script id=»cubeFragmentShader» type=»x-shader/x-fragment»>
uniform float time;
varying vec2 vUv;
void main() {
vec2 position = -1.0 + 2.0 * vUv;
float red = abs(sin(position.x * position.y + time / 5.0));
float green = abs(sin(position.x * position.y + time / 4.0));
float blue = abs(sin(position.x * position.y + time / 3.0 ));
gl_FragColor = vec4(red, green, blue, 1.0);
}
</script>
|
Как вы можете видеть в верхней части шейдера, снова есть две наши переменные. Вы должны помнить, что все переменные, которые вы используете (кроме переменных из Three.js), должны быть определены в каждом шейдере, в котором они используются.
В функции void main()
мы рассчитываем цвета на основе времени и УФ-фрагмента (фрагментные шейдеры работают с фрагментами, которые составлены из вершин, поэтому значения varying
переменных интерполируются в фрагментном шейдере). Не стесняйтесь связываться с этими числами и функциями (просто помните, что значения цвета должны быть положительными).
Наконец, мы устанавливаем переменную gl_FragColor
которая устанавливает цвет фрагмента.
Если вы откроете свой браузер сейчас, ничего не изменится, потому что мы должны изменить материал объекта, чтобы он использовал шейдеры.
Шаг 3: ТРИ. Шейдерный материал
Этот специальный материал используется всякий раз, когда нам нужно использовать шейдеры. Давайте изменим материал объекта, который мы прикрепили к нашей модели в предыдущей части этой серии. Сначала определите массив uniforms
который будет использоваться для передачи переменных в шейдеры:
1
2
3
4
5
|
var uniforms = {
time: { type: «f», value: 0 },
resolution: { type: «v2», value: new THREE.Vector2 },
texture: { type: «t», value: THREE.ImageUtils.loadTexture(‘./box.png’) }
};
|
Далее в loader.load
определите материал item
и используйте его:
1
2
3
4
5
6
|
var itemMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById(‘cubeVertexShader’).innerHTML,
fragmentShader: document.getElementById(‘cubeFragmentShader’).innerHTML
});
item = new THREE.Mesh(new THREE.CubeGeometry(100, 10, 10), itemMaterial);
|
Теперь, если вы откроете браузер, вы увидите, что красный луч изменил свои цвета:
Но цвета не меняются, и сетка тоже не анимируется. Чтобы изменить это, мы должны обновлять переменную time
в шейдерах каждый раз, когда рисуется кадр. Перейдите к функции render
и добавьте эту строку после clock.getDelta()
:
1
|
uniforms.time.value += delta * 10;
|
Теперь, если вы откроете браузер, вы должны увидеть красиво анимированный и красочный объект:
Слово о производительности
Если бы мы создавали такой эффект текстуры, используя, например, HTML5 Canvas, процесс занял бы слишком много циклов ЦП, что приводило бы к задержкам. Но все шейдеры выполняются на графическом процессоре, который оптимизирован для всех операций с графикой и ориентирован только на них. Разделение графических и неграфических вычислений — ключ к хорошему приложению.
Если вы хотите создать что-то реальное с использованием WebGL, позвольте мне заверить вас, что вам придется перенести как можно больше работы на GPU, чтобы сделать ваше приложение гладким и отзывчивым.
Вывод
Как видите, использование Three.js позволяет нам очень легко создавать 3D-графику в браузере, и результаты на самом деле довольно хорошие. Но, они могут быть даже лучше, взгляните на эти примеры с сайта Three.js:
Имея достаточно времени, творческого подхода и Three.js, вы можете создавать удивительные приложения, подобные этим. Я буду более чем рад видеть ваши творения Three.js. Спасибо за прочтение.