Как оживить абстрактную текстуру воды 2d сверху вниз?

24

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

Фактическая текстура прозрачна, я добавил зеленый цвет для ясности.

Теперь у меня проблема в том, что я не знаю, как анимировать эту текстуру, чтобы вода выглядела хорошо. Я попытался переместить текстуру синусоидальной волной: texture.y += sin(angle) . Конечно, теперь вся текстура движется, что выглядит нереально. Следующее, что я попробовал, это добавить еще один слой и реализовать эффект параллакса. Так что отражения под поверхностью воды также будут двигаться, но намного медленнее. Это выглядит немного лучше, но все еще не ... достаточно хорошо.

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

  • Какая математическая модель для этого? Что-то с пружинами, где силы толкать / тянуть?
  • И если да, то как мне сопоставить эту модель с заданной текстурой? Сохраняя все кривые, а что нет ...

(Я также открыт для различных идей / ответов о том, как оживить данную текстуру. Реализм здесь не главное, просто приятные на вид воды, похожие на движения ...)

Решение от DMGregory

В этом посте я опубликовал пример libgdx: 2d анимация воды неровная и не плавная (см. Ответ о фильтрации текстур)

morpheus05
источник

Ответы:

41

Обычный способ сделать это - использовать косвенный поиск текстур в шейдере, чтобы исказить текстуру дисплея:

Анимированный GIF, показывающий анимацию воды

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

введите описание изображения здесь

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

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

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

В Unity шейдер будет выглядеть следующим образом - он должен быть достаточно простым для перевода на язык шейдера по вашему выбору:

fixed4 frag (v2f i) : SV_Target
{               
    float2 waveUV = i.uv * _NoiseScale;
    float2 travel = _NoiseScrollVelocity * _Time.x;

    float2 uv = i.uv;
    uv += _Distortion * (tex2D(_Noise, waveUV + travel).rg - 0.5f);
    waveUV += 0.2f; // Force an offset between the two samples.
    uv += _Distortion * (tex2D(_Noise, waveUV - travel).rg - 0.5f);

    // Sample the main texture from the distorted UV coordinates.
    fixed4 col = tex2D(_MainTex, uv);

    return col;
}
Д.М.Григорий
источник
1
Это выглядит действительно красиво. Я не уверен, что понимаю все атрибуты: _NoseScale = скаляр для масштабирования «карты шума». _NoiseScrollVelocity = Vector2, с какой скоростью мы движемся по карте шума. _Noise =? _Distortion = Скалярное Я выбираю в качестве фактора искажения? v2f = вершина мы определяем цвет. я =?
morpheus05
_Noiseэто [текстурный сэмплер, который читает из] маленькой потертой случайной текстуры выше. v2f iэто интерполированные данные из вершинного шейдера - в основном мы используем его , чтобы получить координаты текстуры для пикселя мы рисуем, i.uv. И вы абсолютно правы во всем остальном.
DMGregory
Я реализовал шейдер, и почему-то он не работает (он не двигается или искажения слишком велики), я полагаю, что неправильно установил значения. время = разница с последним кадром в мс. noise_scale = 1 (я использую вашу текстуру, повтор режима обтекания) noise_scroll_velocity = [0,01, 0,01] искажение = 0,02
morpheus05
Обратите внимание, что переменная называется Time, а не DeltaTime. Если вы используете разницу во времени, и ваша частота кадров является постоянной, то вы всегда будете получать одно и то же число, и вы будете повторно запускать шейдер с теми же входами, получая одинаковый вывод (ничего не двигается). Хуже того, если ваша частота кадров непоследовательна, вы получите вибрацию туда-сюда. Вы хотите, чтобы общее время истекло, а не дельта время.
DMGregory
Вскоре я нажал кнопку «Отправить», и я понял, что теперь это почти работает. Анимация, кажется, вызывает волны из нижнего правого угла, и примерно через 10 секунд она просто затухает, как волны, которые останавливаются. Что может быть причиной этого?
morpheus05
6

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

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

Эд Марти
источник
Это очень интересно, я также посмотрю на этот генератор (хотя я попробую вариант с шейдером, если я его понимаю ...)
morpheus05
4

Это похоже на текстуру, которую вы можете сгенерировать из графа вороной, например:

введите описание изображения здесь

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

FacticiusVir
источник
4
В прошлом я действительно отображал каустику в шейдере. Это не обязательно так дорого, как вы думаете ( вот пример рендеринга ребер Вороного в реальном времени в шейдере WebGL ), хотя получить правильную плавную форму по краям, а не заостренные многоугольники, может быть непросто.
DMGregory
Ооо, это очень мило; У меня есть несколько генераторов местности, для которых это было бы очень удобно.
FacticiusVir
0

Существует метод oldschool, включающий нижний слой текстуры и две полупрозрачные текстуры для отражения сверху.

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

https://steamcdn-a.akamaihd.net/apps/valve/2010/siggraph2010_vlachos_waterflow.pdf

цицеро
источник
3
Хотя ссылки могут помочь, они никогда не дают хороших ответов. Не могли бы вы расширить оба эти тезисов? Как бы это осуществить?
Vaillancourt
Первый метод, по сути, очень старый метод, используемый для анимации воды - вы берете текстуру воды базового слоя, координаты UVW которой смещены в направлении по вашему выбору. Теперь вы дополнительно применяете обычную карту / карту рельефа, которую вы перемещаете в другом направлении - если все сделано хорошо, это выглядит убедительно для небольших рек. Это очень ограничено, хотя для больших водоемов - как что-либо, напоминающее волну, получит эффект муара. Ссылка объясняет использование потоковых карт намного лучше, чем я мог.
Пика
Пожалуйста, используйте функцию редактирования, чтобы улучшить вопрос с тем, что вы добавили здесь :) Люди привыкли искать ответы в посте, а не в комментариях.
Vaillancourt