Как мы делали красивую двумерную воду в Unity Статьи редакции
Cоздание почти трёхмерного окружения без трёхмерной графики.
Мы — это я, Егор, и Юра, наш художник, которому я упорно создаю след в сети, но он не торопится этим пользоваться. И, собственно, мы очень неспешно работаем над нашей игрой и иногда выкладываем всякие заметки о том, что там и как. В этот раз речь пойдёт о том, как мы захотели добавить водички в игру, но не смогли остановиться на слишком простом решении.
Сразу оговорюсь, что работаем мы в Unity и используем Universal Render Pipeline для отрисовки, но общие идеи можно перенести, конечно, и в другие пайплайны отрисовки и даже движки.
Макет
Началось всё с макета и от него пошло. Первоначально он выглядел чуть темнее, но, немного поигравшись, мы получили вот такую картинку.
Сразу выделим, какие детали нужно будет реализовать:
- собственно, вода;
- пенка у колон и камней;
- туман.
План
Так как вся игра в 2D, доступа к высотам в точках у меня нет, и просто перенести 3D подход возможности не было. Поэтому я придумал другую хитрость — сделать отдельную камеру, копирующую основную, подменять в ней спрайты с помощью URP Renderer и дополнительных текстур и рисовать собственную карту высот, используя её. А по ней уже строить очертания моря и тумана.
Тут оговорюсь для людей, незнакомых с Unity, что URP (Universal Render Pipeline) — это один из вариантов пайплайнов отрисовки, который позволяет гибко настраивать, собственно, отрисовку. И идея тут в том, чтобы создать специальный способ отрисовки в специальной камере для некоторых игровых объектов.
Карта высот
Для начала нужно придумать, как её вообще рисовать. В обычном 3D она рисуется автоматически по мере записи в Depth Buffer (иногда и иначе, не будем задерживаться на этом), остаётся только взять её и использовать. Но так как у нас 2D-спрайты, то нам это, конечно же, не подходит.
Самый простой способ, как мне показалось, — это нарисовать специальную текстуру, копирующую текстуру колоны, где в каждом пикселе в B-канале (можно любой другой, это не так важно) будет записано, насколько этот пиксель текстуры ниже, чем точка «нуля».
В редакторе спрайтов Unity потом достаточно просто добавить вторичную текстуру и дать ей название.
Теперь нужно написать шейдер, который будет, собственно, заменять основную текстуру на дополнительную. Помимо этого, он должен немного перекодировать данные, потому что в текстуру влезет всего 256 значений (если она не sRGB, то больше, но это детали), а высота может быть раньше. В шейдер добавляем проперти _Meta и используем те же UV-координаты + вешаем математику сверху.
После я создал URP Renderer и добавил в него фичу для подмены материалов.
Теперь остаётся только создать камеру, поменять ей Renderer и настроить слои, получаем две картинки, одна из которых выводится на экран.
Вуаля, карта высот готова.
Вода
Изначально я хотел сделать поверх всего экрана спрайт, который был бы прозрачным там, где он «ниже», чем объект. И я даже это сделал, но создалось несколько проблем:
- нужно было бы заносить в карты высот вообще всё, а значит рисовать высоты вообще для всего;
- спрайт постоянно мешался, потому что отрисовывался вообще всегда даже на сцене.
Второе решалось простым отключением слоя с ним редакторе, но первое уже было действительно проблемой, поэтому мы решили рисовать море за колонами, а сами колоны отсекать в их же шейдере, делая альфу нулевой, если они ниже уровня воды.
Собираем это всё и смотрим на море.
Оживляем море
Для отрисовки пены изначально я взял карту высот, прошёлся по ней блюром и пастеризовал в цветах воды. Сразу заметил, что пенка появляется и сверху картинки рядом с колонами, а не под ними, потому что не понимает, что колоны над ней.
Чтобы это быстренько поправить, я начал размывать не вокруг точки, а в полукруге под ней. То есть, если обычный блюр — это усреднение цвета вокруг точки, то я сделал усреднение только для нижней половины круга.
На практике это выглядело вот так.
Уже получше. Далее я быстренько накинул шум, чтобы было что-то двигающееся. Выглядело оно так себе.
Но тут пришел Юра и рассказал, как делал волны на макете — просто брал три текстуры с шумом на основе фотки волн и двигал их каждую в свою сторону. После чего он же запрограммировал для этого шейкер и стало выглядеть гораздо лучше.
Но пена нам всё-таки не понравилась, решили переделать.
Пена 2.0
Для этого к каждой колоне я прикрепил спрайт с очертаниями пены, зафиксировал его на уровне воды и настроил камеру, которая рисовала только эти спрайты в отдельную текстуру.
Теперь добавляем новую пену и делаем так, чтобы волны бились по угасающей синусоиде.
Туман
Опять же, сначала я хотел сделать спрайт поверх, с переменной альфой, но появились те же самые проблемы, и туман откатился в тот же шейдер колон. Там просто подмешивается цвет тумана, пропихнутый через всяческую арифметику самым простым способом.
После этого я не выдержал создать очень много нод и переписал весь шейдер клеток на HLSL. Еще была проблема с переиспользованием функций между HLSL и шейдерграфом, потому что я иногда передавал семплер текстур. Проблема заключалась в том, что нельзя просто взять и передать семплер из шейдер графа и в HLSL ноду. Это, конечно, тоже добавило очков текстовым шейдерам.
Переписав всё это добро, мы поигрались с самыми разными параметрами и добавили коня-колосса.
Для коня, кстати, руками рисовалась карта высот и вообще весь процесс довольно весело выглядел.
По итогу получилось собрать псевдо-карту высот и красивую анимацию воды, которая когда-нибудь пойдет в полноценную игру. Геймплейно мы при этом двигаемся не слишком быстро (да и в части визуальной тоже, на самом деле), но мы просто любим делать гиммики, которые радуют глаз.
Водичка чисто партиклами поверх голубой плоскости.
Интересно выглядит) А можно скрин редактора с Gizmos иконками или описание в двух словах как оно работает? Здесь несколько систем частиц, стандартный ли эмитер или вы кастомили создание частиц.
Сейчас уже не найду тот проект.
Суть такова: В инете была найдена гифка с каустикой воды. Из нее была сделана несложная 16 кадровая текстура с фейдом по краям каждого кадра. Потом на плоскости воды в рандомных местах появляются 1-3 билборда на 0,7-1,5 сек с этой текстурой. Каустика готова :)
Пена ко краям объектов еще проще - это расставленные вручную такие же партикл системы, где спавнятся так же несколько билбордов, но уже с однокадровой текстурой. К двигающимся объектам, типа лодок, ящиков эти системы просто прилинкованы, чтобы двигаться вместе с ними. Ничего сложного.
Дело лишь за красиво нарисованным градиентом глубины воды на основном плейне, имитирующем воду.
Круто, спасибо) Плюс-минус так и представлял себе)
Комментарий недоступен
И без всяких шейдеров :)
Будто партиклы рисуются сами по себе :)
Эээ... 🤔🤔🤔
Комментарий недоступен
@Евгений Серегин тут тебе ответ пришёл
@Аккаунт удален я помогу)
Имелось ввиду, что не надо самому ничего писать. А на дефолтных шейдерах, материалах юнити все сделано. Из коробки так сказать.
- Вы в воде купаете?
- Нет, просто показываем, как сделали.
- Красивое.
На asset store продаете?
Комментарий недоступен
Я не умею пользоваться реддитом, а Юра говорит, чтобы я разбирался. Ну а дальше мне лениво и как-то вот!
Он по сути тот же дтф, только больше
Не, ну я на самом деле понимаю, просто пытаюсь спихнуть это на Юру, который там много времени проводит, а он отказывается
Комьюнити менеджмент на высоте, так сказать
Отличный материал!
Я правда не сразу понял, что туман это туман. Думал, что часть камней под водой и еще удивился почему волны бьются о подводные камни :)
Да, это многие замечают. Мы думали, как сделать туман менее водным, но каких-то супер очевидных идей у нас не было, поэтому мы решили забить. Все таки, основная идея "подсветить" активные тайлы и с этой задачей декор справляется. А все эти мис-андерстендинги некритичны и вообще красиво же :)
Так может просто сделать туман водой и сделать так, чтобы волны бились о более высокие камни? Сейчас камни, которые как-будто утоплены под водой дают гораздо лучший эффект глубины (есть мелководье, а есть глубокая вода). А подсвеченая игровая зона при этом останется без изменений.
Сделайте его белым
Ах, так это туман! )
А я усиленно ищу его на картинке и не понимаю, где он.
Водичка и правда хорошечная
А вот что у меня получилось.
Каустика - шейдер, натянутый на спрайт с картинкой.
Пена и волны у суши, отдельные спрайты с простым шейдером колыхания.
Я выложил данный шейдер и с ним можно поиграться.
https://www.shadertoy.com/view/wt2GRt
Красивое решение, изощренное, я бы даже сказал! Кайф
Комментарий недоступен
Классно выглядит :)
Красивое...
Вышло очень сочно)
21 век, а мы по прежнему ломаем голову над созданием воды. Я в том числе.
Спасибо за статью
Очень здорово выглядит! Красавчики)
Чисто ради интереса, если хотите посмотреть на красивую воду, поиграйте в недавний вышедший Капитан Блад. Игра на любителя, но водичка в морских сражениях на загляденье.
hl2, gta 4, rdr 2, far cry 5
ну это всё дорогие проекты и ожидать там чего-то некрасивого удивительно, а Блад - явная категория Б и там заморочились с водичкой))
полезная статья, сохраню себе на будущее :)
Совсем не то же самое, но тоже про воду.
Мне почему-то оч запомнилось это видео.