Эта статья — дополненная транскрипция доклада технического художника UNIGINE Ивана Муравского с состоявшегося летом UNIGINE Open Air. Рассказываем, как была сделана система волюметрических облаков, а также про:  Облака в играх и симуляторах Что важно для симуляторов Облака в реальности Рендер облаков: как это делается Прежде, чем говорить про волюметрические облака, посмотрим, какие вообще подходы существуют для реалтайма, и какие у них есть плюсы и минусы.  Условно облака в играх можно поделить на три категории, исходя из подходов к их реализации: геометрия, текстуры и волюметрики. С технической точки зрения, такое деление будет не совсем корректным, но зато удобным для восприятия. При таком подходе облака буквально представляют собой набор треугольников с вершинами, и являются такой же геометрией в сцене, как персонаж, камень, стул и т. д. Геометрический подход к созданию облаков обусловлен в основном стилистикой игры. Нет смысла останавливаться на них подробно, достаточно просто взглянуть на примеры:  В большинстве игр облака и небо создаются именно при помощи двумерных текстур. Это довольно простой и малозатратный в плане ресурсов подход, дающий большую свободу художникам: можно нарисовать, что угодно, или взять готовое фото небесного купола и использовать в своей игре. Одной из самых распространённых и известных техник создания неба и облаков в трёхмерной графике является cube mapping, в котором используются кубические карты (cubemaps, кубмапы), представляющие собой развертку 6 граней воображаемого куба.  Панорамные фото или картинка, охватывающие 360 градусов обзора, включающие в себя часто и небо, и какие‑то другие дальние объекты или части ландшафта, назначаются на окружающий игровой мир куб (skybox). А с помощью перспективных проекций движок создаёт иллюзию того, что игрок находится «внутри» этой картинки. Он не видит граней куба и каких‑либо искажений на небе. Такой подход используется, например, в играх серии Assassin»s Creed, да и во многих других. А для достижения эффекта динамики, текстурка вращается по вертикальной оси, и, поскольку одномоментно в игре видна лишь часть небосклона, создается эффект движения облаков. Но, конечно, в случае с вращением, на кубмапе не должно быть ничего, кроме неба и облаков. К слову, кубмапы пришли на замену старым подходам, когда текстура окружения просто назначалась на вывернутую сферу, что было значительно ресурсозатратнее. Использование кубмап также одним выстрелом убивает несколько зайцев: та же кубическая карта окружения с небом и облаками применяется в том числе и для отражений на объектах, а также для амбиентного освещения. Поэтому такие текстуры почти всегда используются в форматах с высоким диапазоном яркости (hdr,exr и т. д.).  Чуть сложнее этот подход реализован, к примеру, в перезапуске God of War: разработчики использовали кубмапы, которые с помощью текстуры шума дополнительно искажаются при движении, и облака в это время будто слегка меняют форму, что добавляет динамики.  Немного усложнённый подход — облака в Ведьмак 3: к кубмапам добавляются геометрические плоскости, на которые накладываются текстуры отдельных облаков. Это добавляет многослойности и дает больший эффект объема.  Отдельно еще можно выделить подход с билбордами: например, в третьем Battlefield в миссии с полётами. Геймплей там завязан на том, что игрок на истребителе пролетает сквозь облака и рядом с ними. Статичная картинка вокруг не подошла бы для таких целей, поэтому облака там были реализованы с помощью билбордов — геометрических плашек, которые всегда направлены перпендикулярно взгляду и на которых назначены текстуры отдельных облаков. Более высокие облака и облака на дальнем плане, к которым в рамках игрового процесса невозможно подлететь, сделаны также кубической картой. Волюметрические облака давно используются в оффлайновой графике, и до недавних лет не хватало мощностей железа, чтобы рендить волюметрики в реалтайме. Но компьютерные мощности выросли, и волюметрические облака начали появляться в играх: например, в Horizon: Zero Dawn и Red Dead Redemption 2, из недавних — в Hogwarts Legacy и Atomic Heart. Можно поспорить, оправданно ли использование волюметриков конкретно в этих играх, но открытые миры подразумевают динамически изменяющуюся погоду и, соответственно, динамически меняющееся покрытие неба облаками. Наиболее это оправданно, конечно, в околосимуляторных играх, например, Microsoft Flight Simulator или Ace Combat. Принцип волюметрических облаков заключается в том, что это не геометрические, но объёмные облака, а их форма и поведение могут задаваться специальными текстурами.  Рассмотрим на трех примерах: симуляторе диспетчерской вышки, симуляторе самолета и симуляторе вертолета. Казалось бы, зачем диспетчерам достоверные волюметрические облака? Можно было бы обойтись обычным скайбоксом. Но диспетчеры, в том числе и визуально, по виду и состоянию облаков могут определять погодные изменения, которые напрямую влияют на принимаемые ими решения. Эти люди — «глаза» пилотов на земле, и от их навыков зависят тысячи жизней. Именно поэтому им так важно максимально возможное соответствие сгенерированной картинки и реального изображения за окном.  Самолеты большую часть времени летают на больших высотах, но при взлете и посадке они проходят сквозь слои плотных облаков, поэтому использование волюметрических облаков в таком случае вполне оправдано. Опять же, по виду облаков пилоты способны определять обстановку за бортом, что напрямую влияет на безопасность полета.  У винтокрылого воздушного транспорта есть свои нюансы: вертолеты летают на относительно небольшой высоте и с небольшой скоростью. Это позволяет пилотам более детально рассматривать облака и определять погодные условия для принятия соответствующих решений. При таких условиях волюметрические облака являются необходимым аспектом визуальной составляющей симулятора.  Итак, мы выяснили, что всем, кто связан с управлением воздушным транспортом, будь то на земле или в небе, нужны визуально достоверные и динамически изменяющиеся объёмные облака.  Пробежимся по основным видам, не вдаваясь в детали — в июне мы писали большую обзорную статью про облака и то, как они генерируются в UNIGINE. Логично, что для того, чтобы сделать фотореалистичные облака, сначала нужно разобраться в их видах, собрать референсы, углубиться в тему.  Принято выделять 10 типов облаков: Слоистые (Stratus) Слоисто‑кучевые (Stratocumulus) Слоисто‑дождевые (Nimbostratus) Кучевые (Cumulus) Кучево‑дождевые (Cumulonimbus) Перистые (Cirrus) Перисто‑слоистые (Cirrostratus) Перисто‑кучевые (Cirrocumulus) Высокослоистые (Altostratus) Высококучевые (Altocumulus) Можно заметить, что многие названия частично повторяют другие. Дело в том, что большинство типов облаков — смесь 2 из 3 базовых видов: слоистых, кучевых и перистых.  Слоистые (Stratus) встречаются ближе всего к земле (ниже 400 метров над уровнем моря) и иногда образуют туман. Кучевые (Cumulus) — те самые «барашки», самый известный вид облаков. Они формируются в ясную погоду на уровне 1–2 тысяч метров над уровнем моря и не всегда связаны с осадками. Перистые (Cirrus) облака встречаются в верхних слоях атмосферы (выше 5500 метров), имеют нитевидную форму и состоят из кристалликов льда. Их появление часто свидетельствует о скором ливне или буре. В некоторых названиях можно увидеть слово «nimbus» — такой «хвостик» означает, что эти облака способны генерировать дождь.  Все вышеперечисленные типы облаков можно сгенерировать в режиме реального времени в UNIGINE.  Для создания волюметрических облаков используются 3D‑текстуры.  Можно представить трехмерную текстуру в виде объекта, нарезанного на слои (обычные двухмерные текстуры), и сложенные вместе в одну картинку (атлас). На импорте эту послойную последовательность 2D‑текстур движок интерпретирует как 3D‑текстуру.Любую геометрию можно «запечь» в виде такой нарезки. Какого‑то общепринятого формата для самого файла 3D‑текстуры на данный момент не существует, поэтому в разных движках существуют разные условия для того, чтобы преобразовывать на импорте обычную двухмерную текстуру в 3D‑текстуру. Волюметрическое облако нужно как‑то визуализировать, но в таком объекте нет треугольников, нет вершин, рендер нельзя сделать традиционной растеризацией. На помощь приходит raymarching.  Raymarching — это техника рендера волюметриков, когда мы пускаем из пикселя луч и «шагаем» с определенным расстоянием внутри волюметрика, сэмплируя информацию на каждом из этих интервалов.  В UNIGINE в облаках информация на каждом шаге сэмплируется по конусу, направленному в сторону солнца.   Конечно, это упрощение, и для рендера облаков реймарчингом используется множество хитростей, вроде возврата на шаг назад для дополнительных сэмплирований, марчинга только в пределах слоя и др. Таким образом, отдельное облако или какой‑то участок облаков можно запечь в некую статичную текстуру и отрендить в движке в виде волюметрика. Однако, если задача — сделать систему с динамически изменяющимся покрытием неба, с разными погодными условиями и большим количеством типов облаков, невозможно будет каждое облако смоделировать, запечь в текстуру и использовать в движке.Для решения этих задач был использован процедурный подход к генерации волюметрических облаков. В этом случае мы можем уже внутри движка оперировать заранее заготовленными текстурами, кривыми, менять параметры, что дает нам свободу формировать различные типы облаков посредством инструментов движка.  Так как мы хотим покрывать облаками всё небо, нам придется использовать какие‑то бесшовные и пригодные для тайлинга текстуры. Возьмём для примера кучевые облака. Начать можно с самых крупных и грубых форм. Конкретно для этого этапа можно обойтись простой 2D текстурой. Мы заранее знаем, что в движке захотим в реалтайме менять степень покрытия от чистого неба до полностью затянутого облаками, поэтому у этой текстуры нет нулевых значений, нет ни одного текселя абсолютно черного цвета.  Ниже — текстура покрытия (coverage texture) и результат изменения контраста этой текстуры в движке. И, поскольку мы имеем дело с 2D‑текстурой, мы используем информацию из нее только в горизонтальной плоскости, а для формирования облаков, очевидно, нужна какая‑то информация по высоте, какой профиль будет у облака на срезе. По сути, можно использовать ту же самую текстуру уже в виде карты высот, где белый цвет — это самая высокая точка внутри слоя облака, а чем темнее, тем ниже. Благодаря этому можно избежать одного лишнего использования 3D‑текстуры.  Дальше нужно создать уже более детальные формы. Для этого можно взглянуть на референсные изображения и попробовать систематизировать детали строения облака, попытаться понять, есть ли какая‑то закономерность. Можно увидеть повторяющиеся крупные элементы-арки, которые присутствуют по всей высоте облака:   Есть элементы помельче, которые присутствуют только в верхней части:   И самые маленькие элементы, которые присутствуют только на самой вершине облаков: Так как нам нужна гибкость в настройке формы, то такого результата можно добиться с помощью текстур шума. 2D‑текстурами здесь уже не обойтись, нужны трехмерные текстуры. Мы используем разные октавы нойза, чтобы имитировать разный размер этих элементов облака. Неплохим выбором будут, например, Уорли (Worley noise), комбинированный с Перлином (Perlin noise) так как, на наш взгляд, они больше всего подходят для образования форм, похожих на облака. Сэмплирование трехмерной текстуры — очень «дорогой» процесс, поэтому для экономии видеопамяти разные октавы можно упаковать в одну текстуру. Мы используем четырехканальную 3D‑текстуру. В r‑канал помещен самый крупный шум, в альфа‑канал — самый высокочастотный шум.  Такие трехмерные бесшовные текстуры можно создать, к примеру, в Houdini. Они точно так же, как и coverage‑текстура, имеют определенную гистограмму, чтобы иметь возможность менять контраст этих текстур, добиваясь нужной формы. В UNIGINE за это отвечает параметр Noise Threshold.  Чтобы распределить по высоте слоя эти шумы, используем настраиваемые градиенты для каждого канала, и делаем, к примеру, так, что самый высокочастотный шум находится только в верхней части облаков. На этом моменте стоит сказать, что для структурированности этапов создания формы облаков в статье намеренно изменен порядок работы с текстурами. На самом же деле, именно трехмерные текстуры шума — это и есть облака. Именно с помощью них изначально создаются основные формы слоя облаков и только потом всё это модулируется с помощью coverage‑текстуры, которая является своего рода маской.  Если еще раз вернуться к нашему референсу, то можно заметить, что по низу облака расположены еще более высокочастотные и рваные турбулентные детали, которые, как правило, всегда есть на нижней юбке кучевых облаков: Чтобы добиться такого эффекта в форме, мы используем трёхканальную 3D‑текстуру, своего рода детейл. В ней также поканально хранится шум с разной частотностью. Такую текстуру можно использовать в меньшем разрешении. Особенность детейла, в отличие от основного четырехканального шума, заключается в том, что он вырезает облака по краям в менее плотных частях. Коэффициент интенсивности так же задается с помощью градиента по высоте слоя, чтобы вырезать облако с большей интенсивностью в нижней части, чем в верхней.  Чтобы добиться турбулентности нижних деталей облака, мы используем Curl Noise. Эта текстура используется для того, чтобы сместить текстурные координаты для детейла и создать эффект дисторсии. В движок была добавлена возможность использовать как 2D‑текстуру, так и 3D‑текстуру, для возможности оптимизации. Облако с точки зрения физики — это набор мельчайших частиц воды. Когда свет от солнца проходит через облако, он много раз переотражается в разные стороны, постепенно теряя свою энергию. Незначительная часть света поглощается.  Чтобы корректно визуализировать освещение облаков, сначала считаем освещение от солнца. Для этого используется закон Бера (Beer»s law), он же закон Бугера — Ламберта — Бера — физический закон, определяющий ослабление параллельного монохроматического пучка света при распространении его в поглощающей среде. Другими словами, этот закон говорит, что луч, который проходит сквозь какую‑то среду, теряет свою энергию в зависимости от плотности этой среды по экспоненте. Это базовый принцип, который применяется везде при рендере волюметриков. Именно по Beer»s Law считается альфа облаков и тень от солнца.  Еще одна важная часть в рендере облаков — анизотропия рассеяния.  На изображении видно, что лучи, которые уходят от солнца в сторону наблюдателя, более длинные, поскольку в облаках больше выражен эффект прямого рассеяния, бОльшая часть переотраженных лучей в итоге направлены от солнца. Анизотропия рассеяния выражается в нескольких визуальных эффектах. Со стороны солнца в менее плотных областях мы видим более тёмный цвет облаков. Можно увидеть это, обратив внимание на границы «барашков». А если смотреть на облака снизу, в сторону солнца, то можно увидеть, что края облаков, где меньшая плотность, очень яркие, они буквально светятся. Такой эффект, кстати, называется silver lining. Как добиться такого эффекта? Физически корректно это описывает функция Mie, но она слишком затратна для компьютерных вычислений в реальном времени, поэтому часто используются такие аппроксимации как, например, фазовая функция Henyey‑Greenstein.   Такая аппроксимация — тоже не новый метод, и в 3D‑графике используется часто. На вход подается значение анизотропии в каком‑то диапазоне. Нулевое значение говорит о том, система будет полностью изотропная и лучи будут распространяться равномерно во все стороны по сфере. Если на вход подается положительное значение, то рассеяние становится более прямое, а если негативное — то обратное.  На изображениях ниже можно увидеть влияние анизотропии рассеяния на то, как выглядит облако в UNIGINE. Кроме солнца, на облака влияет и окружение в их затененных участках, будь то просто свет от синего неба, либо же отражённый свет от зеленой лужайки снизу. Свет от окружения считается довольно просто: усредненный цвет от части кубмапы прибавляется для нижней части облаков, и для верхней отдельно.  Итак, с помощью всех вышеперечисленных способов можно «тайлить» и бесшовно создавать бесконечный слой облаков.  Но что если необходимо создать уникальное огромное облако высотой в несколько километров (Cumulonimbus, например)? Нужно, чтобы это был отдельный объект, который можно было поместить в любое место, т.к. обычно таких облаков встречается всего 1–2 на небосводе. Для этого в облаках UNIGINE есть два режима базовой формы: 2D‑слой и 3D‑слой. В режиме 3D‑слоя вместо coverage‑текстуры используется любая заранее запечённая в 3D‑текстуру форма: Форма облаков может быть любая:   При таком наборе параметров, когда можно менять шумы, сами текстуры, крутить пороги и контрасты, менять текстуру покрытия, можно создавать все 10 требуемых типов облаков, включая перистые и слоистые.  А для решения задачи создания региональной облачности были добавлены два режима слоя — бесконечный и ограниченный. В ограниченном имеется возможность указать дополнительную маску для слоя. С помощью этого инструмента можно добиться эффекта надвижения грозового фронта, например — что часто требуется клиентам. Или же просто рандомную область любой формы с облаками. И в заключении, давайте полюбуемся небом, которое было сгенерировано в UNIGINE с помощью методов и инструментов, описанных в статье: Увидеть все эти прекрасные облака в динамике, подвигать ползунки и изменить настройки, а так же создать новые облака абсолютно любой формы можно во всех изданиях UNIGINE SDK, включаю бесплатную версию (Community Edition).