38
Знакомство фронтендера с WebGL: почему WebGL?
Мне и моему коллеге дизайнеру была поставлена задача разработать новую версию сайта-визитки компании. Коллега полгода учился работать с 3D-редакторами (в нерабочее время на Maxon Cinema 4D), поэтому он хотел использовать свои новые навыки при создании новой версии сайта. Его идея заключалась в том, что на каждой странице на первом экране будет крутиться какая-нибудь непонятная фигура с каким-нибудь красивым текстом. Выглядеть это должно было примерно так:

Реальных моделей пока не было, поэтому на первое время мне предоставили модель яблока.
Основная проблема заключалась в том, что у меня не было опыта работы с 3D, я очень плохо знал математику и геометрию, а также у меня никогда не было опыта работы с WebGL. В общем, в свои силы я верил слабо. По итогу с задачей я справился, и я хочу рассказать об этом опыте в небольшом цикле статей.
Слово WebGL у меня ассоциируется с 3D. Я считаю, что больше нет нормальных способов отрендерить что-то в 3D без WebGL. Помимо того, что слово WebGL само по себе звучит очень круто, нашлись и другие причины выбрать эту технологию:
Я думаю, первого пункта хватит, чтоб принять решение в пользу webGL.
Модель яблока была в формате OBJ, другие форматы я решил не рассматривать. Это текстовый формат и это придавало мне какой-то уверенности в том, что решений в интернете для него должно быть много.
Я знал про существования библиотек
Я гуглил «webgl obj model render» и находил только онлайн-просмотрщики или какие-то узко специфичные решения, которые запустить не смог.
Также искал демки на CodePen, но я не нашел ничего подходящего. А если что-то находил, не мог понять, что вообще происходит и что мне делать.
Понял, что нужно начать с основ, без базовых знаний webGL задачу не выполнить.
three.js
, Babylon.js
и PixiJS
(это вообще 2D-рендер). Вес 3D-библиотек огромный, как бы они не были сжаты. Не хотелось пускать таких монстров к себе на сайт, у меня и так был 100кб react-dom, куда еще больше? Да и чтоб разобраться в 3D-библиотеках, все равно нужно было иметь какие-то представления о 3D-графике. Я гуглил «webgl obj model render» и находил только онлайн-просмотрщики или какие-то узко специфичные решения, которые запустить не смог.
Также искал демки на CodePen, но я не нашел ничего подходящего. А если что-то находил, не мог понять, что вообще происходит и что мне делать.
Понял, что нужно начать с основ, без базовых знаний webGL задачу не выполнить.
Не знаю как так получилось, но в интернете на глаза ресурсы по WebGL мне не попадались, поэтому я пошел Телеграм-чат @webgl_ru (найти его было просто) и спросил:
— как начать в WebGL фронту?
— как начать в WebGL фронту?
Похоже, что в чат постоянно заходили подобные мне ребята с аналогичными вопросами, поэтому у ребят из чата уже был заготовлен список ресурсов, который они мне и скинули. Впоследствии участники данного чата мне еще не раз помогли, за что им огромное спасибо.
Из списка, который мне скинули, я выбрал ресурс WebGL Fundamentals, у которого было довольно говорящее название, а также перевод на русский. Обычно я не вижу ничего ужасного в английской документации, но WebGL казался мне чем-то инородным и страшным, а также состоящим из подходов, которые доселе не были мне знакомы. То, что WebGL рендерит это всё через Canvas — это было единственное, что я знал об этой технологии.
Первое, что бросается в глаза, — это необычный API. Привычное нам браузерное API — это просто вызов методов у каких-то встроенных объектов/классов, тогда как апи WebGL это как будто вы программно настраиваете repl node.js, а потом прокидываете в этот repl строки javascript кода и получаете какой-то результат из этого.
В случаи webgl вы настраиваете внутри браузера обрезанную версию OpenGL(библиотека которая заставляет наши видеокарты что-то рисовать) и прокидываете в нее код на языке GLSL. GLSL обрезанный C подобный язык, погрузится несложно. Как будто пишешь на es3 javascript.
В случаи webgl вы настраиваете внутри браузера обрезанную версию OpenGL(библиотека которая заставляет наши видеокарты что-то рисовать) и прокидываете в нее код на языке GLSL. GLSL обрезанный C подобный язык, погрузится несложно. Как будто пишешь на es3 javascript.
Если обобщить, то работа на webgl выглядит так:
Выше я говорил про программы на GLSL, программа всегда состоит из 2 шейдеров. Шейдер есть функция.
Каждая программа состоит из вершинного шейдера (Vertex Shader) и фрагментного шейдера (Fragment Shader).
Вершинный шейдер - позволяет разметить пространство, а фрагментный - закрашивает это пространство. Так работают видеокарты.
Сначала им нужно выставить точки в пространстве, потом соединить эти точки невидимыми линиями, а потом закрасить каждый пиксель внутри получившийся фигуры.
Если приводить пример из жизни, у вас есть стенка 1м х 1м и есть художник по имени Видеокарта. Вот вы и ему говорите:
Каждая программа состоит из вершинного шейдера (Vertex Shader) и фрагментного шейдера (Fragment Shader).
Вершинный шейдер - позволяет разметить пространство, а фрагментный - закрашивает это пространство. Так работают видеокарты.
Сначала им нужно выставить точки в пространстве, потом соединить эти точки невидимыми линиями, а потом закрасить каждый пиксель внутри получившийся фигуры.
Если приводить пример из жизни, у вас есть стенка 1м х 1м и есть художник по имени Видеокарта. Вот вы и ему говорите:
— Поставь мне точку на 30 сантиметров от верха и 50 см слева, потом точку в 50х50, потом в 70х80, соедини мне это линиями и закрась получившееся пространство красным.
Сами шейдеры выглядит так:
Вершинный шейдер (Vertex Shader)
Вершинный шейдер (Vertex Shader)
// атрибут, который будет получать данные которые мы передали. атрибут === переменная/пропс
attribute vec4 a_position;
// все шейдеры имеют функцию main
// стандартная тема для компилируемых языков
void main() {
// gl_Position - специальная переменная вершинного шейдера,
// которая отвечает за установку положения
gl_Position = a_position;
}
Фрагментный шейдер (Fragment Shader)
// фрагментные шейдеры не имеют точности по умолчанию, поэтому нам необходимо её
// указать. mediump подойдет для большинства случаев. Он означает "средняя точность"
precision mediump float;
void main() {
// gl_FragColor - специальная переменная фрагментного шейдера.
// Она отвечает за установку цвета.
gl_FragColor = vec4(1, 0, 0, 1); // вернёт красный
}
В вершинном шейдере вы увидели
attribute
. Шейдере есть несколько типов переменных (дальше копипаста с webglfundamentals.org):Атрибуты и буферы
Буферы - это массивы бинарных данных, загруженных в графический процессор. Обычно буферы содержат вещи вроде положений вершин, нормалей, координат текстур, цветов вершин и т.д., хотя вы вольны положить в них что угодно.
Атрибуты определяют, каким образом данные из ваших буферов передаются в вершинный шейдер. Например, вы можете поместить положения вершин в буфер как три 32-битных числа с плавающей точкой на одно положение. Вы указываете конкретному атрибуту, откуда брать положения вершин, какой тип данных используется (три 32-битных числа с плавающей точкой), начиная с какого индекса в буфере начинаются положения вершин и какое количество байтов нужно получить от одного положения до следующего.
Доступ к буферам не произвольный. Вместо этого вершинный шейдер выполняется заданное количество раз и каждый раз, когда он выполняется, выбирается следующее значение каждого из указанных буферов и назначается атрибуту.
Uniform-переменные
Uniform-переменные - это глобальные переменные, которые устанавливаются перед выполнением программы шейдера.
Текстуры
Текстуры - это массивы данных, к которым есть произвольный доступ в программе шейдера. Чаще всего в текстуру помещается картинка, но текстура - это просто набор данных и вы можете запросто поместить в неё что-то отличное от набора цветов.
constying-переменные
constying-переменные позволяют передавать данные из вершинного шейдера фрагментному шейдеру. Во фрагментном шейдере мы получим интерполированные значения вершинного шейдера - зависит от того, отображаем ли мы точки, линии или треугольники.
Вершинный шейдер выполняется на каждую порцию x,y,z (z может не указываться, если рисуем в 2D) координат. Каждые такие координаты создают вершину. А дальше уже эти вершины объединяются в треугольники (полигоны) и потом фрагментным шейдером эти треугольники закрашиваются.
Вы спросите, почему именно треугольники?
В процессе обучения я на это не обращал внимания, но когда начал предпринимать попытки нарисовать модельку тоже удивился, но оказывается любую фигуру можно нарисовать через ТРЕУГОЛЬНИКИ (ПОЛИГОНЫ) и потому, добавлять другие фигуры бессмысленно.
Треугольник есть абсолют.
В webgl можно рисовать только треугольниками, линиями и точками.
Треугольник есть абсолют.
В webgl можно рисовать только треугольниками, линиями и точками.
Также я узнал про матрицы. Это пришло из математики и для js разраба это выглядит как массив из 9 или 12 чисел (12 для 3D).
Матрицы решают вопросы того как трансформировать модель (а точней вершины), чтоб поставить модельку в нужное место в пространстве, увеличить или покрутить.
Матрицы еще позволяют создавать камеры, то есть менять вид обзора и другое. Вы могли с ними встречаться, если работали
Матрицы один из фундаментов 2D/3D графики. Наверно одна из немногих вещей которой можно пользоваться не разбираясь как она работает.
Достаточно запомнить, что чтоб применить несколько трансформаций нужно просто матрицы друг на друга перемножить и результат передать в шейдер.
Матрица 3х3 для 2D трансформаций, а 4х4 для 3D трансформаций.
Хорошие люди уже все за нас написали gl-matrix. Нам нужно вызывать только знакомые на слух название методов и получать нужный результат.
Более подробно про матрицы можно узнать на webgl fundumentals.
Матрицы решают вопросы того как трансформировать модель (а точней вершины), чтоб поставить модельку в нужное место в пространстве, увеличить или покрутить.
Матрицы еще позволяют создавать камеры, то есть менять вид обзора и другое. Вы могли с ними встречаться, если работали
transform: matrix(...n)
в css.Матрицы один из фундаментов 2D/3D графики. Наверно одна из немногих вещей которой можно пользоваться не разбираясь как она работает.
Достаточно запомнить, что чтоб применить несколько трансформаций нужно просто матрицы друг на друга перемножить и результат передать в шейдер.
Матрица 3х3 для 2D трансформаций, а 4х4 для 3D трансформаций.
Хорошие люди уже все за нас написали gl-matrix. Нам нужно вызывать только знакомые на слух название методов и получать нужный результат.
Более подробно про матрицы можно узнать на webgl fundumentals.
Так как все же выглядит hello world код на webgl? Что вообще требуется для того, чтоб это запустить и нарисовать треугольник?
И после такого количества кода (по ссылке), мы получаем треугольник.
Честно говоря, это безумное количества кода ради одного треугольника немного остудило желание, но автор учебника объяснил, что все это можно убрать под хелперы.
Посмотрев на этот пример, можно понять безумные размеры 3D либ.
Посмотрев на этот пример, можно понять безумные размеры 3D либ.
Продолжение здесь.
38