3 года назад художник спросил меня:
— Слушай, а можно в нашу мобильную игру добавить красивые облачка?
— Нет, это абсолютно невозможно, у нас постоянно вращается камера, так что билборды будут смотреться очень фальшиво даже если на них добавить карты нормалей, а другие способы…
*художник погружается в летаргический сон*
Для меня нет большего удовольствия, чем выяснять, что я был неправ.
Про фотореалистичный рендеринг облаков написано много статей, но если хочется рисовать облака на смартфоне, приходится придумывать кучу всяких хаков, упрощений и допущений.
Под катом подробное описание рендеринга облаков на мобильных и много html5 гифок.
Нам понадобятся:
Размываем нормали в 2 прохода:
Аналогично размываем карту глубины облаков:

Неплохо было бы добавить шума в наши данные.
Есть 3 варианта:
Временно отключим размытие чтобы лучше понять как работает проекция шума.
Проецируем шум по осям X,Y,Z:
Теперь используем этот шум, чтобы изменить карту глубины облака по формуле:

И спроецируем шум заново:
Вернем размытие:
Освещение складывается из 2 составляющих:
Пока мы применили шум только к карте глубины.
Давайте в процессе применения освещения тоже используем шум.
Прибавим вектор шума к нормалям:
Сдвинем позицию из которой мы читаем на
:
Накладываем на остальной мир с помощью альфа-блендинга, добавляя прозрачность там, где объекты мира близки к поверхности облаков, или и вовсе заслоняют их.

Где
— глубина, на котором объект пропадает из видимости внутри облака.
Если запустить все эти преобразования в разрешении Full HD, ваш топовый смартфон заплачет, свернется в клубочек и начнет показывать слайдшоу с частотой 15-20 fps: телефоны вытягивают 3d графику только с помощью технологии early-z, которая уменьшает количество операций до выполнения трёх-четырёх простых шейдеров на пиксель. А мы выполняем много вычислений в каждом пикселе.
Что делать? Пора избавляться от операций. Формы у облаков расплывчатые, а движущийся шум прикроет наши грязные делишки: будем резать разрешение!
Итоговая производительность:
7 ms на nexus 5x при средних настройках графики (бюджетный телефон 2015 года).
0.5 ms на ноутбуке с GTX 940M, что означает что такие облака по производительности отлично подойдут для VR, где важна высокая частота кадров.
Не знаю, можно ли выкладывать ссылку на Asset Store, но кто ищет — тот всегда найдёт:)
UPD: Добавил раздел про производительность
— Слушай, а можно в нашу мобильную игру добавить красивые облачка?
— Нет, это абсолютно невозможно, у нас постоянно вращается камера, так что билборды будут смотреться очень фальшиво даже если на них добавить карты нормалей, а другие способы…
*художник погружается в летаргический сон*
Для меня нет большего удовольствия, чем выяснять, что я был неправ.
Про фотореалистичный рендеринг облаков написано много статей, но если хочется рисовать облака на смартфоне, приходится придумывать кучу всяких хаков, упрощений и допущений.
Под катом подробное описание рендеринга облаков на мобильных и много html5 гифок.
Собираем данные
Нам понадобятся:
- Глубина мира:
- Глубина облаков:
- Нормали облаков:
Немного о формате
Левая половина изображения — Aльфа канал. Чем темнее — тем прозрачнее.
Правая половина изображения — RGB каналы.
Единственный гарантированно поддерживаемый формат текстур на мобильных — ARGB32, так что его я и буду использовать.
Глубина зашифровывается в RG каналы текстуры, при этом
.
Нормаль представляется как 3d вектор в пространстве камеры, причем
, т.к. RGB не поддерживает отрицательные значения.
Правая половина изображения — RGB каналы.
Единственный гарантированно поддерживаемый формат текстур на мобильных — ARGB32, так что его я и буду использовать.
Глубина зашифровывается в RG каналы текстуры, при этом
Нормаль представляется как 3d вектор в пространстве камеры, причем
Размываем
Размываем нормали в 2 прохода:
- По горизонтали:
- По вертикали:
Примечание: нормали размываем активнее чем прозрачность, это даст облакам стать мягкими, но не даст им потерять очертания


Аналогично размываем карту глубины облаков:

Проецируем шум
Неплохо было бы добавить шума в наши данные.
Есть 3 варианта:
- 3D текстура — требует много памяти, медленно работает на мобильных.
- Генератор шума в шейдере — для шума перлина нужно много раз вызывать ГСЧ => медленно работает; Нет художественного контроля: нельзя включить другой тип шума без переписывания кода.
- Трипланарная проекция 2d текстуры — генерируем текстуру с шумом, проецируем её по осям X, Y и Z. Эффективно; можно подставить любую текстуру; занимает мало памяти.
Временно отключим размытие чтобы лучше понять как работает проекция шума.
Трипланарная проекция
Если
— координаты точки в 3d пространстве, а
— нормаль к поверхности, то проекция рассчитывается так:

Так как длина вектора
равна 1, сумма квадратов его координат дадут 1, сохранив яркость шума.
Проецируем шум по оси X:
Проецируем шум по осям X и Y:
Так как длина вектора
Проецируем шум по оси X:
Проецируем шум по осям X и Y:
Проецируем шум по осям X,Y,Z:
Теперь используем этот шум, чтобы изменить карту глубины облака по формуле:
И спроецируем шум заново:
Вернем размытие:
Освещение
Освещение складывается из 2 составляющих:
- Псевдо диффузное освещение
Иллюстрации
Зависимость освещения от углапри различных значениях
:
У этой формулы есть физический смысл: именно так выглядело бы наивное распространение света у сферического облака с линейным затуханием яркости и без учета рассеивания.
Результат:
- Просвечивание
Если в этом пикселе нет ни одного объекта из твердого мира, добавляем просвечивание:
Где
— радиус просвечивания, а
— расстояние от солнца до текущего пикселя
Применяем шум
Пока мы применили шум только к карте глубины.
Давайте в процессе применения освещения тоже используем шум.
Прибавим вектор шума к нормалям:
Сдвинем позицию из которой мы читаем на
Наложение облаков на остальной мир
Накладываем на остальной мир с помощью альфа-блендинга, добавляя прозрачность там, где объекты мира близки к поверхности облаков, или и вовсе заслоняют их.
Где
Производительность
Если запустить все эти преобразования в разрешении Full HD, ваш топовый смартфон заплачет, свернется в клубочек и начнет показывать слайдшоу с частотой 15-20 fps: телефоны вытягивают 3d графику только с помощью технологии early-z, которая уменьшает количество операций до выполнения трёх-четырёх простых шейдеров на пиксель. А мы выполняем много вычислений в каждом пикселе.
Что делать? Пора избавляться от операций. Формы у облаков расплывчатые, а движущийся шум прикроет наши грязные делишки: будем резать разрешение!
- Карта глубины мира рисуется в полном или половинном разрешении (всё равно у смартфонов очень мелкие пиксели).
- Все остальные операции производятся в разрешении
, уменьшая площадь в 16 раз => увеличивая производительность в 16 раз.
- Операцию наложения мы производим уже в полноразмерный экранный буфер, при этом используя исходную карту глубины мира, что позволяет показывать четкие контуры объектов, поддерживая иллюзию того, что облака рендерились в полном разрешении.
Итоговая производительность:
7 ms на nexus 5x при средних настройках графики (бюджетный телефон 2015 года).
0.5 ms на ноутбуке с GTX 940M, что означает что такие облака по производительности отлично подойдут для VR, где важна высокая частота кадров.
Конечный результат:
Не знаю, можно ли выкладывать ссылку на Asset Store, но кто ищет — тот всегда найдёт:)
UPD: Добавил раздел про производительность