Webgl Wind И Программирование На Графическом Процессоре. Лекция На Fronttalks 2018

Для рендеринга сложной графики на веб-страницах существует библиотека веб-графики, или сокращенно WebGL. Разработчик интерфейса Дмитрий Васильев рассказал о программировании на GPU с точки зрения верстальщика, о том, что такое WebGL и как мы использовали эту технологию для решения задачи визуализации больших данных о погоде.

— Разрабатываю интерфейсы в екатеринбургском офисе Яндекса.

Я начинал в спортивной группе.

Мы разрабатывали специальные спортивные проекты во время чемпионатов мира по хоккею и футболу, Олимпийских игр, Паралимпийских игр и других крутых мероприятий.

Также я участвовал в разработке специального поисковика, посвященного новой сочинской трассе.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылка со слайда
Кроме того, через полтора часа мы перезапустили сервис «Работа над ошибками».

А потом началась работа в Pogoda, где я занимался поддержанием функциональности API, его развитием, написанием инфраструктуры вокруг этого API и написанием привязок узлов для обученных формул машинного обучения.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Дальше началась более интересная работа.

Принимал участие в редизайне наших метеорологических сервисов.

Десктопы, тачки.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Приведя в порядок стандартные прогнозы, мы решили сделать прогноз, которого нет ни у кого.

Этот прогноз представлял собой прогноз движения осадков по территориям.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Существуют специальные метеорадары, обнаруживающие осадки в радиусе 2000 км; они знают его плотность и расстояние до него.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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

Можно перемещать вперед и назад.

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылка со слайда
Мы посмотрели отзывы людей.

Людям это понравилось.

Стали появляться всякие мемы, были прикольные картинки, когда Москву затопило к чертям.

Поскольку формат всем понравился, мы решили пойти дальше и посвятить ветру следующий прогноз.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Уже есть сервисы, которые показывают прогнозы ветра.

Это пара крутых, которые действительно выделяются.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Посмотрев на них, мы поняли, что хотим сделать то же самое — или хотя бы не хуже.

Поэтому мы решили визуализировать частицы, которые плавно движутся по карте в зависимости от скорости ветра, и оставлять за собой какой-то след, чтобы их было видно, можно было увидеть траекторию ветра.

Поскольку мы уже сделали классную карту с осадками с использованием 2D-холста, мы решили сделать то же самое с частицами.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Посоветовавшись с дизайнером, мы поняли, что нам нужно заполнить частицами около 6% экрана, чтобы получился крутой эффект. Для отрисовки такого количества частиц стандартным подходом наш минимальный тайминг составил 5 мс.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Если мы думаем, что нам все равно нужно перемещать частицы и создавать какую-то красоту вроде рисования хвоста частиц, то можно предположить, что мы выпадем из минимального тайминга в 40 мс, чтобы показать плавную анимацию, чтобы произвести не менее 25 кадров в секунду.

Проблема в том, что здесь каждая частица будет обрабатываться последовательно.

А что, если обрабатывать их параллельно? Явную разницу в работе центрального и графического процессоров продемонстрировали «Разрушители мифов» на одной из конференций.

Выкатили машину с установленным пейнтбольным маркером, задачей которого было нарисовать смайлик одним цветом.

Примерно за 10 секунд он нарисовал эту картинку.

( Ссылка на видео — ок.

ред.)

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Потом ребята выкатили байдарку, которая представляет собой ГПУ, и парой вертелов нарисовали Мону Лизу.

Это примерно разница между скоростями вычислений процессора и графического процессора.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы воспользоваться такими возможностями браузера, была придумана технология WebGL. Что это? С этим вопросом я пошёл в Интернет. Добавив пару слов с анимацией частиц и ветром, я нашел пару статей.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылки со слайда: первый , второй
Одна из них — демо Владимира Агафонкина, инженера из Mapbox, который сделал именно ветер на WebGL и дал ссылку на блог Криса Уэллонса, в котором рассказывалось о том, как перемещать и хранить состояние частиц на GPU. Берем и копируем.

Мы ожидаем такого результата.

Здесь частицы движутся плавно.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Что мы получаем, я не понимаю что.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Пытаемся разобраться в коде.

Совершенствуемся, но снова получаем неудовлетворительный результат. Забираемся еще глубже – вместо ветра получаем дождь.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Хорошо, мы решаем сделать это сами.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Существуют фреймворки для работы с WebGL. Почти все они направлены на работу с 3D-объектами.

Нам не нужны эти 3D-возможности.

Нам нужно только нарисовать частицу и переместить ее.

Поэтому мы решили сделать все вручную.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

В настоящее время существует две версии технологии WebGL. Вторая версия, что круто, имеет высокую современную версию языка программирования, на которой программа запускается в графическом адаптере и может осуществлять прямые вычисления, а не только рендеринг.

Но у него плохая совместимость.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Что ж, решаем использовать старый проверенный WebGL 1, имеющий хорошую поддержку, кроме Opera Mini, которая никому не нужна.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

WebGL состоит из двух частей.

Это JS, который выполняет состояние программ, работающих на видеокарте.

А есть компоненты, которые работают непосредственно на видеокарте.

Начнем с JS. WebGL — это просто подходящий контекст для элемента холста.

Более того, при получении этого контекста выделяется не просто конкретный объект, выделяются аппаратные ресурсы.

А если мы запустим в браузере что-нибудь красивое на WebGL, а потом решим поиграть в Quake, то вполне возможно, что и эти ресурсы потеряются, и контекст может потеряться, и вся ваша программа сломается.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Поэтому при работе с WebGL тоже нужно слушать потерю контекста и уметь его восстановить.

Вот почему я подчеркнул, что init существует.

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Тогда вся работа JS сводится к сборке программ, которые работают на графическом процессоре, отправке их на видеокарту, настройке некоторых параметров и команде «рендеринг».



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

В WebGL, если вы посмотрите на сам элемент контекста, вы увидите кучу констант. Эти константы означают ссылки на адреса в памяти.

На самом деле они не являются константами во время работы программы.

Потому что если контекст утерян и восстановлен заново, может быть выделен другой пул адресов, и для текущего контекста эти константы будут другими.

Поэтому почти все операции в WebGL на стороне JS выполняются через утилиты.

Никто не хочет заниматься рутинной работой по поиску адресов и прочей хрени.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Перейдем к тому, что выполняется на самой видеокарте — программе, состоящей из двух наборов инструкций, написанных на C-подобном языке GLSL. Эти инструкции называются вершинным шейдером и фрагментным шейдером.

Из их пары создается программа.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чем отличаются эти шейдеры? Вершинный шейдер задает поверхность, на которой что-то должно быть нарисовано.

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



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

В коде это выглядит так.

В шейдере есть раздел объявления переменных, которые задаются извне, из JS; определены их тип и название.

А также основная секция, выполняющая необходимый для этой итерации код. В большинстве случаев ожидается, что вершинный шейдер установит переменную gl_Position в некоторую координату в 4D-пространстве.

Это x, y, z и ширина пространства, знать которую на данный момент особо не обязательно.

Фрагментный шейдер ожидает установки цвета определенного пикселя.

В этом примере мы выбираем цвет пикселя из подключенной текстуры.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы перенести это на JS, вам просто нужно обернуть исходный код шейдера в переменные.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Эти переменные затем преобразуются в шейдеры.

Это контекст WebGL, мы создаём шейдеры из исходников, параллельно создаём программу и с помощью программы привязываем пару шейдеров.

Получаем рабочую программу.

Попутно проверяем, что компиляция шейдеров прошла успешно и программа собралась успешно.

Мы говорим, что вам нужно использовать эту программу, потому что программ для разных значений рендеринга может быть несколько.

Мы настраиваем его и приказываем рисовать.

Получается какая-то картинка.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Мы залезли глубже.

В вершинном шейдере все вычисления выполняются в пространстве от -1 до 1, независимо от размера выходной точки.

Например, пространство от -1 до 1 может занимать весь экран 1920x1080. Чтобы нарисовать треугольник в центре экрана, вам нужно нарисовать поверхность, охватывающую координату 0, 0.

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Фрагментный шейдер работает в пространстве от 0 до 1, а цвета здесь производятся четырьмя компонентами: R, G, B, Alpha. В примере с CSS вы, возможно, встречали подобное цветовое обозначение при использовании процентов.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы что-то визуализировать, вам нужно указать, какие данные вы хотите визуализировать.

Конкретно для треугольника мы определяем типизированный массив из трех вершин, каждая из которых состоит из трех компонентов: x, y и достаточно.

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

Здесь как есть, без преобразований, ставим точку на экране.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Фрагментный шейдер может раскрашивать константы, переданные из JS, также без дополнительных вычислений.

Причём, если какие-то переменные во фрагментном шейдере передаются извне или из предыдущего шейдера, то точность должна быть указана.

В этом случае достаточно средней точности, и почти всегда ее достаточно.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Перейдем к JS. Мы присваиваем переменным одни и те же шейдеры и объявляем функцию, которая будет создавать эти шейдеры.

То есть создается шейдер, загружается в него исходник, а затем компилируется.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Делаем два шейдера, вершинный и фрагментный.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

После этого создаем программу и загружаем в нее уже скомпилированные шейдеры.

Мы связываем программу, потому что шейдеры могут обмениваться переменными друг с другом.

И на этом этапе проверяется соответствие типов переменных, которыми обмениваются эти шейдеры.

Мы говорим: используйте эту программу.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Далее мы создаем список вершин, которые хотим визуализировать.

В WebGL есть интересная особенность для некоторых переменных.

Чтобы изменить конкретный тип данных, нужно задать глобальный контекст редактирования array_buffer, а затем что-то загрузить по этому адресу.

Не существует явного присвоения каких-либо данных переменной.

Здесь все делается через включение некоторого контекста.

Также необходимо установить правила чтения из этого буфера.

Видно, что мы указали массив из шести элементов, но программе нужно объяснить, что каждая вершина состоит из двух компонентов, тип которых — float, это делается в последней строке.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы установить цвет, программа ищет адрес переменной u_color и устанавливает значение для этой переменной.

Задаем цвет, красный 255, зеленый 0,8, синий 0 и полностью непрозрачный пиксель — цвет желтый.

И мы говорим, чтобы выполнить эту программу с использованием примитивов треугольника, в WebGL можно рисовать точки, линии, треугольники, треугольники сложной формы и так далее.

И сделайте три вершины.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Вы также можете указать, что массив, используемый для рисования, должен считаться с самого начала.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылка со слайда
Если немного усложнить пример, то можно добавить зависимость цвета от положения курсора.

При этом фпс зашкаливает.

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы рисовать частицы по всему миру, нужно знать скорость ветра в каждой точке этого мира.

Чтобы увеличить и как-то переместить карту, нужно создать контейнеры, соответствующие текущему положению карты.

Чтобы перемещать сами частицы, нам нужно придумать формат данных, который можно будет обновлять с помощью графического процессора.

Сделайте сам рисунок и рисунок поезда.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Все данные мы делаем через текстуру.

Мы используем 22 канала для определения горизонтальной и вертикальной скорости, где нулевая скорость ветра соответствует середине цветового диапазона.

Это примерно 128. Поскольку скорость может быть как отрицательной, так и положительной, цвет мы задаем относительно середины диапазона.

В результате получается вот такая картина.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы загрузить его на карту, нам нужно его разрезать.

Для подключения изображения к карте воспользуемся стандартным инструментом Яндекс.

Карты «Слой», в котором определим адрес, с которого нам нужно получить вырезанные тайлы, и добавим этот слой на карту.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылка со слайда
Получаем картинку, где неприятный зеленый цвет кодирует скорость ветра.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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

По умолчанию мы предполагаем, что будем использовать слой, но слой карты создает холст, из которого он немедленно извлекает 2D-контекст, который ему удается захватить.

Но если мы попробуем взять из холста, у которого уже есть контекст другого типа, и взять из него GL-контекст, то в результате мы получим null. Если вы получите к нему доступ, программа выйдет из строя.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



Ссылка со слайда
Поэтому мы использовали Pane, это контейнеры для слоев, и добавили туда наш холст, из которого уже взяли нужный нам контекст.

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы как-то расположить частицы на экране и иметь возможность их перемещать, использовался формат положения частиц в текстуре.

Как это работает? Для оптимизации создана квадратная текстура, известен размер ее стороны.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Рисуя частицы по порядку и зная порядковый номер частицы и размеры текстуры, в которой они хранятся, можно вычислить конкретный пиксель, в котором закодировано положение на реальном экране.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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

Далее мы определяем координаты x, y для этой частицы, считываем это значение и декодируем его.

Что это за магия: rg/255 + ba? Для положения частиц мы используем 20 двойных каналов.

Цветовой канал имеет значение от 0 до 255, и для экрана 1080 мы не можем размещать частицы в любой позиции экрана, потому что максимум, в котором мы можем разместить частицу, — это 255 пикселей.

Поэтому в одном канале мы храним знания о том, сколько раз частица прошла 255 пикселей, а во втором канале мы храним точное значение того, сколько раз она прошла после.

Далее вершинный шейдер должен преобразовать эти значения в свою рабочую область, то есть из -1 в 1, и установить эту точку на дисплее.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Чтобы просто посмотреть на наши частицы, просто покрасьте их в белый цвет. В GLSL есть такой сахар, что если мы определим тип переменной и передадим его в константу, то эта константа будет распределена по всем четырем компонентам, например.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Нарисовав такую программу, мы видим набор одинаковых квадратов.

Попробуем добавить им красоты.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Для начала добавим зависимость этих квадратов от текущей скорости ветра.

Мы просто читаем текущую скорость и соответствующие текстуры для каждой частицы.

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



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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

То есть наш шейдер превращается в такую штуку.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Вычисляем расстояние до нарисованного пикселя от центра.

Если оно превышает половину пространства, то мы его не показываем.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Мы получаем более разнообразную картину.

Дальше нужно как-то переместить эти вещи.

Поскольку WebGL 1 не может что-то вычислять или работать напрямую с данными, мы воспользуемся возможностью рендеринга программ в специальные компоненты — кадровые буферы.

Например, к буферам кадров можно прикрепить текстуры, которые можно обновлять.

Если фреймбуфер не объявлен, то рендеринг по умолчанию выполняется на экране.

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



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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

WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

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



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

В результате получается вот такая картина.

Состояние частиц постоянно меняется, и появляется какая-то анимация.

Если вы запустите такую анимацию на 5–10 минут, то увидите, что все частицы прибудут в конечный пункт назначения.

Они все соскользнут в воронку.

У вас получится такая картинка.



WebGL Wind и программирование на графическом процессоре.
</p><p>
 Лекция на FrontTalks 2018

Теги: #JavaScript #Визуализация данных #webgl #Геоинформационные сервисы #Яндекс погода #api Яндекс #ветер

Вместе с данным постом часто просматривают: