Рендерим облака на мобильных девайсах
3 года назад художник спросил меня:
— Слушай, а можно в нашу мобильную игру добавить красивые облачка?
— Нет, это абсолютно невозможно, у нас постоянно вращается камера, так что билборды будут смотреться очень фальшиво даже если на них добавить карты нормалей, а другие способы…
*художник погружается в летаргический сон*
Для меня нет большего удовольствия, чем выяснять, что я был неправ.
Про фотореалистичный рендеринг облаков написано много статей, но если хочется рисовать облака на смартфоне, приходится придумывать кучу всяких хаков, упрощений и допущений.
Под катом подробное описание рендеринга облаков на мобильных и много html5 гифок.
Собираем данные
Нам понадобятся:
- Глубина мира:
- Глубина облаков:
- Нормали облаков:
Правая половина изображения — RGB каналы.
Единственный гарантированно поддерживаемый формат текстур на мобильных — ARGB32, так что его я и буду использовать.
Глубина зашифровывается в RG каналы текстуры, при этом .
Нормаль представляется как 3d вектор в пространстве камеры, причем , т.к. RGB не поддерживает отрицательные значения.
Размываем
Размываем нормали в 2 прохода:
- По горизонтали:
- По вертикали:
Аналогично размываем карту глубины облаков:
Проецируем шум
Неплохо было бы добавить шума в наши данные.
Есть 3 варианта:
- 3D текстура — требует много памяти, медленно работает на мобильных.
- Генератор шума в шейдере — для шума перлина нужно много раз вызывать ГСЧ => медленно работает; Нет художественного контроля: нельзя включить другой тип шума без переписывания кода.
- Трипланарная проекция 2d текстуры — генерируем текстуру с шумом, проецируем её по осям X, Y и Z. Эффективно; можно подставить любую текстуру; занимает мало памяти.
Временно отключим размытие чтобы лучше понять как работает проекция шума.
Так как длина вектора равна 1, сумма квадратов его координат дадут 1, сохранив яркость шума.
Проецируем шум по оси X:
Проецируем шум по осям X и Y:
Проецируем шум по осям X, Y, Z:
Теперь используем этот шум, чтобы изменить карту глубины облака по формуле:
И спроецируем шум заново:
Вернем размытие:
Освещение
Освещение складывается из 2 составляющих:
- Псевдо диффузное освещениеИллюстрации
Зависимость освещения от угла при различных значениях :
У этой формулы есть физический смысл: именно так выглядело бы наивное распространение света у сферического облака с линейным затуханием яркости и без учета рассеивания.
Результат: - Просвечивание
Если в этом пикселе нет ни одного объекта из твердого мира, добавляем просвечивание:
Где
— радиус просвечивания, а
— расстояние от солнца до текущего пикселя
Применяем шум
Пока мы применили шум только к карте глубины.
Давайте в процессе применения освещения тоже используем шум.
Прибавим вектор шума к нормалям:
Сдвинем позицию из которой мы читаем на :
Наложение облаков на остальной мир
Накладываем на остальной мир с помощью альфа-блендинга, добавляя прозрачность там, где объекты мира близки к поверхности облаков, или и вовсе заслоняют их.
Где — глубина, на котором объект пропадает из видимости внутри облака.
Конечный результат: