Как мы делали красивую двумерную воду в Unity

Cоздание почти трёхмерного окружения без трёхмерной графики.

Мы — это я, Егор, и Юра, наш художник, которому я упорно создаю след в сети, но он не торопится этим пользоваться. И, собственно, мы очень неспешно работаем над нашей игрой и иногда выкладываем всякие заметки о том, что там и как. В этот раз речь пойдёт о том, как мы захотели добавить водички в игру, но не смогли остановиться на слишком простом решении.

Сразу оговорюсь, что работаем мы в Unity и используем Universal Render Pipeline для отрисовки, но общие идеи можно перенести, конечно, и в другие пайплайны отрисовки и даже движки.

Макет

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

Макет

Сразу выделим, какие детали нужно будет реализовать:

  1. собственно, вода;
  2. пенка у колон и камней;
  3. туман.

План

Так как вся игра в 2D, доступа к высотам в точках у меня нет, и просто перенести 3D подход возможности не было. Поэтому я придумал другую хитрость — сделать отдельную камеру, копирующую основную, подменять в ней спрайты с помощью URP Renderer и дополнительных текстур и рисовать собственную карту высот, используя её. А по ней уже строить очертания моря и тумана.

Тут оговорюсь для людей, незнакомых с Unity, что URP (Universal Render Pipeline) — это один из вариантов пайплайнов отрисовки, который позволяет гибко настраивать, собственно, отрисовку. И идея тут в том, чтобы создать специальный способ отрисовки в специальной камере для некоторых игровых объектов.

Карта высот

Для начала нужно придумать, как её вообще рисовать. В обычном 3D она рисуется автоматически по мере записи в Depth Buffer (иногда и иначе, не будем задерживаться на этом), остаётся только взять её и использовать. Но так как у нас 2D-спрайты, то нам это, конечно же, не подходит.

Самый простой способ, как мне показалось, — это нарисовать специальную текстуру, копирующую текстуру колоны, где в каждом пикселе в B-канале (можно любой другой, это не так важно) будет записано, насколько этот пиксель текстуры ниже, чем точка «нуля».

Как мы делали красивую двумерную воду в Unity

В редакторе спрайтов Unity потом достаточно просто добавить вторичную текстуру и дать ей название.

Как мы делали красивую двумерную воду в Unity

Теперь нужно написать шейдер, который будет, собственно, заменять основную текстуру на дополнительную. Помимо этого, он должен немного перекодировать данные, потому что в текстуру влезет всего 256 значений (если она не sRGB, то больше, но это детали), а высота может быть раньше. В шейдер добавляем проперти _Meta и используем те же UV-координаты + вешаем математику сверху.

Как мы делали красивую двумерную воду в Unity

После я создал URP Renderer и добавил в него фичу для подмены материалов.

Как мы делали красивую двумерную воду в Unity

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

Как мы делали красивую двумерную воду в Unity

Вуаля, карта высот готова.

Вода

Изначально я хотел сделать поверх всего экрана спрайт, который был бы прозрачным там, где он «ниже», чем объект. И я даже это сделал, но создалось несколько проблем:

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

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

Как мы делали красивую двумерную воду в Unity

Собираем это всё и смотрим на море.

До макета еще далековато
До макета еще далековато

Оживляем море

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

Как мы делали красивую двумерную воду в Unity

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

Как мы делали красивую двумерную воду в Unity

На практике это выглядело вот так.

Как мы делали красивую двумерную воду в Unity

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

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

Но пена нам всё-таки не понравилась, решили переделать.

Пена 2.0

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

Как мы делали красивую двумерную воду в Unity

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

Туман

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

Как мы делали красивую двумерную воду в Unity

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

Переписав всё это добро, мы поигрались с самыми разными параметрами и добавили коня-колосса.

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

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

343
37 комментариев

Водичка чисто партиклами поверх голубой плоскости.

54
Ответить

Интересно выглядит) А можно скрин редактора с Gizmos иконками или описание в двух словах как оно работает? Здесь несколько систем частиц, стандартный ли эмитер или вы кастомили создание частиц.

Ответить

Комментарий недоступен

Ответить

- Вы в воде купаете?
- Нет, просто показываем, как сделали.
- Красивое.

35
Ответить

На asset store продаете?

2
Ответить

Комментарий недоступен

12
Ответить

Я не умею пользоваться реддитом, а Юра говорит, чтобы я разбирался. Ну а дальше мне лениво и как-то вот!

11
Ответить