Water Simulation

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Появилась возможность рассказать о том как мы создавали жидкость для TReload. Нам всего лишь нужно было залить уровни кислотой. Кислоты должно быть много, площади затопления огромные :) Один из финальных результатов:

Визуально кислота должна была представлять из себя грязную воду с желтым оттенком. Вот референсы:

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

Разработка кислоты проводилась в несколько этапов:

  • разрабатывались инструменты для работы с кислотой (в основном это инструмент рисования текстурных масок пены)

  • разрабатывалась кислота (работали над шейдерами, материалами, логикой взаимодействия, звуковыми эффектами)

Инструмент рисования текстурных масок пены

Механизм рисования достаточно прост.

Условно есть 2 текстуры:

  • текстура маски пены (далее маска)

  • текстура кисти (далее кисть)

Задача состоит в том чтобы правильно произвести операцию Blit кисти с маской (использовать для кисти соответствующие “scale” и “offset”, чтобы корректно ее спроецировать в нужную область маски).

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

Здесь есть 2 решения по части перевода координат:

  • использовать “MeshColluder” и из него получать “texcoord.xy” области пересечения луча “Raycast”. В этом случае координаты будут уже приведены к “UV” виду, нам только останется проецировать текстуру кисти в нужную область маски.

  • использовать “BoxCollider” и самостоятельно переводить “worldSpace” координаты кисти к UV координатам маски.

Мы использовали второй вариант:

  • к модели кислоты добавляется “BoxColider”

  • делается RayCast

  • worldSpace точка пересечения луча кисти и кислоты переводится в “acidLocalSpace”

  • далее эта точка переводится в “UV-space”. Для этого мы делим координаты точки пересечения на размеры кислоты:

Доработчки: механизм отмены (ctrl+z)

Для ввода механизма отмены пришлось изменить подход: была создана ортографическая камера, которая рендерит только слой кистей. Размеры камеры соответствовали размерам кислоты. В области пересечения кисти и маски создавался меш кисти, который рендерился камерой, а далее делался “Blit” с маской. Таким образом появилась возможность отменять действия.

Небольшая демонстрация работы системы рисования масок:

Волны

Нами предпринимались разные попытки создания волн:

  • рисования волн на тектуре кислоты

  • волны созданные геометрическим шейдером поверх кислоты

  • тесселяция + GPU Instancing и волны

Справа на рисунке представлена волна, которая создана геометрическим шейдером. Волна слева - плоская.

Кстати, про кубики я писал здесь: сслыка

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

Как работает генерация волн

Опишу это простыми словами: есть 2-х мерное уравнение “колебаний”, которое нужно решать каждый кадр. Это уравнение позволяет генерировать распространение волн. С материалом по теме вы можете ознакомиться здесь: ссылка 1

А здесь еще один отличный материал: ссылка 2

Здесь крутой пример исходного кода для Unity: ссылка 3

Мой результат генерации волны (используется стандартная тесселяция от Unity и стандартный шейдер):

Но генерация волн это еще не все. Если у вас маленький бассейн, то примера с Github должно хватить. А если нужно рендерить море или океан, то возникает масса проблем оптимизации:

  1. оказывается Unity не поддерживает “Tessellation + GPU Instancing of Standard shaders”

  2. ближние участки кислоты должны быть высокополигональными (для этого нужно использовать систему “LOD”)

  3. дальние волны, пену можно не рендерить

  4. артефакты распространения волн

Самое важное я узнал в самом конце. Unity, почему “Tessellation + GPU Instancing” не не работают со стандартными шейдерами? Для решения этой проблемы пришлось посмотреть сгенерированный код Standard-шейдера, вытащить из него то что вам нужно и вставить это в “Fragment shader”.

Структура водной поверхности, распространение воды на соседние сегменты

Водная поверхность представляет из себя NxN объектов с “LOD”. По мере удаления, объекты с LOD подменяют друг друга так, что на расстоянии X вместо 4-х различных объектов с LOD, рисуется один:

То есть водная поверхность - это “умная” сетка из разных участков воды. Допустим, вода имеет размеры 8х8 и пусть источник волн возник в ячейке [2,4]:

Тогда мы резервируем соседние N ячеек (в моем примере по одной с каждой стороны) и проецируем текстуру распространения волн на этот участок общей водной поверхности. Проекция текстуры распространения волн показана красным. То есть мы растягиваем и смещаем текстуру для каждого участка воды. Поиск зоны отрисовки волн в Unity выглядит следующим образом:

Кстати, если источник волн на к краю воды, то мы располагаем текстуру с волнами так, чтобы она не уходила за границы воды (на видео этого нет).

А здесь мы спроецировали текстуру на которой должны рисоваться волны (настроили “tilling & offset”):

Таким образом распространение волны происходит на прилегающие соседние объекты, то есть за пределы одного участка воды.

Вот результаты работы симуляции воды и тесселяции:

Генерация волн от объектов сложной формы

До этого момента я упрощенно рассказал и сослался на литературу, описывающую то как генерировать волны в виде кругов. А что если в воду упадет параллелепипед, капсула или еще какой-то объект (в том числе и невыпуклый). В этом случае форма волн должна быть соответствующей.

Чтобы добиться “реалистичной” формы волн, мы поступили следующим образом:

  • падающие в воду объекты рендерятся в текстуру _FallTex. (Ортографическая камера рендерит значения глубины упавших объектов умноженные на скорость падения обьекта)

  • далее текстура _FallTex размывается и результат размытия передается в текстуру волн

То есть мы вмешиваемся в процесс симуляции воды, добавляем в симуляцию новые значения (новые источники волн).

Здесь показан результат симуляции волн от объектов сложной формы:

Распространение волн на дальние сегменты

Это одна из проблемных задач. Распространение волн осуществляется за счет использования дополнительных текстур. Игрок не способен летать над водной гладью со скоростью пули и присутствовать то в одном месте, то в другом. Поэтому есть возможность переставать генерировать те волны, которые “далеко”. А распространение тех волн, которые близко, нужно плавно переносить из одного водного сегмента на другой. Здесь видно как ведет себя вода при переходе между разными участками симуляции жидкости:

Чтобы передергов с водой не было, нужно иметь 2 текстуры симулирующие жидкость. Одна из них должна симулировать жидкость, а другая должна перекопировать на себя участок с волнами при перемещении игрока (то есть при смене центрального участка воды). Если этого не делать, то возможны такие баги:

Допустим упавший в воду объект поплыл из [2,4] в [3,4] :

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

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

Артефакты

Если объект - источник волн расположен на границе разных водных сегментов, то при копировании текстуры распространения волны могут возникнуть артефакты:

Эти артефакты связаны с тем, что текстура волн является “Clamp”. Для устранения данных артефактов, необходимо учитывать расположение объектов (проверять расположение относительно стыков) и, в случае необходимости, исключать часть объектов из процесса симуляции волн.

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

Вот мои тесты тесселяции и попытки объединения Tesselation + GPU Instancing в Standard shader:

Волны от объектов разной формы:

На этом все!

Надеюсь статья была полезна и позволила рядовому читателю понять часть трудностей с которыми сталкиваются разработчики в процессе работы над играми :)

Ссылки на нас:

VK // Twitter // Instagram // DTF // Pikabu // Habr

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Понравилась ли Вам статья?
100% Да, мне бы хотелось больше таких статей 1
0% Нет, статья бесполезна 0
Проголосовал 1 пользователь. Воздержались 0 пользователей.
Источник: https://habr.com/ru/post/573850/


Интересные статьи

Интересные статьи

Настройка любой площадки для CMS — это рутинный процесс, который должен быть доведен до автоматизма в каждой уважающей себя компании. А потому частенько воспринимается, как восход солнца — это происхо...
Много всякого сыпется в мой ящик, в том числе и от Битрикса (справедливости ради стоит отметить, что я когда-то регистрировался на их сайте). Но вот мне надоели эти письма и я решил отписатьс...
Несмотря на то, что “в коробке” с Битриксом уже идут модули как для SOAP (модуль “Веб сервисы” в редакции “Бизнес” и старше), так и для REST (модуль “Rest API” во всех редакциях, начиная с...
Приступая к животрепещущей теме резервного копирования на «Битрикс», прежде всего хотелось бы поблагодарить разработчиков, реализовавших автоматическое резервное копирование в облачное хранилище в вер...
Здравствуйте. Я уже давно не пишу на php, но то и дело натыкаюсь на интернет-магазины на системе управления сайтами Битрикс. И я вспоминаю о своих исследованиях. Битрикс не любят примерно так,...