Around GPU Gems, часть 1, глава 1. Эффективное моделирование воды на основе физических моделей

Автор оригинала: Марк Финч, Cyan Worlds Inc.

Эта статья описывает подход к моделированию и рендерингу больших массивов воды на GPU. Подход комбинирует геометрические преобразования исходной триангуляции с динамической генерацией карт нормалей. Данный подход применялся в игре Uru: Ages Beyond Mist от Cyan Worlds.

Тихий омут

1.1 Цели и задачи

Последние несколько лет методы, используемые в фотореалистичном моделировании и рендеринге, плавно перетекают в сферу рендеринга в реальном времени и компьютерных игр. Так, методы на основе быстрого преобразования Фурье (БПФ, FFT), дающие великолепные результаты при расчётах на больших сетках, при уменьшении сеток до средних уже могут выполняться в режиме реального времени на обычных персональных компьютерах. Становятся доступны также методы решения упрощённых уравнений Навье-Стокса на вокселях. И пусть технологии реального времени пока отстают, разрыв уверенно сокращается.

В то же время модели симуляции водной поверхности, достаточно простые для параллельных расчётов на GPU, также развиваются.

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

1.2 Аппроксимация суммой синусов

Наше моделирование воды будет складываться их двух частей: моделирование геометрии сетки и моделирование карты нормалей для увеличения детализации. Мы начнём с простого суммирования элементарных функций и по ходу дела будем добавлять интересные усложнения к этой модели.

Сумма синусов как функция, описывающая волну, хороша сразу по ряду причин. Во-первых, она непрерывна и бесконечно дифференцируема, а так же имеет очень простой аналитический вид. Это позволяет легко вычислить как координаты точек сетки, так и нормали, текстурные координаты и так далее, причём для сколь угодно мелкой сетки (в этом её выгодное преимущество от, например, ландшафтов, задаваемых картой высот). Во-вторых, она периодична с периодом, стремительно возрастающим с количеством слагаемых. Так, например, синусы с периодами 11 и 13 дадут нам функцию с периодом 143. Это позволяет нам создавать практически не повторяющуюся поверхность воды. В-третьих, при таком аналитическом задании каждый синус можно двигать независимо, получая простую и при этом очень эффектно выглядящую анимацию волн.

 

1.2.1 Определение волновых функций

Параметры одной волновой функции

Одна синусоидальная волновая функция определяется следующими параметрами:

  • Длина волны \(L\): расстояние между двумя следующими друг за другом пиками волны. Она соотносится с частотой волны как \(\omega = 2\pi / L\).
  • Амплитуда \(A\): высота пика волны.
  • Скорость \(S\): расстояние, которое проходит любая точка волны за одну секунду. Скорость можно выразить через фазовую константу \(\varphi\): \(\varphi = S\cdot 2\pi / L\).
  • Направление \(D\): вектор на плоскости, перпендикулярный волновому фронту.

В результате высота волны в точке \((x, y)\) в момент времени t выражается по формуле

$$W_i(x, y, t) = A_i\cdot\sin\big(\omega_i \cdot D_i \cdot (x, y) + \varphi_i\cdot t\big),$$

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

$$H(x, y, t) = \sum_{i=1}^{n} A_i\cdot\sin\big(\omega_i \cdot D_i \cdot (x, y) + \varphi_i\cdot t\big),$$

где \(n\) — количество элементарных слагаемых.

 1.2.2 Нормали и касательные

Поскольку наша волна задаётся явной функцией, мы легко можем вычислить точные значения векторов нормали и касательных к каждой точке поверхности волны. Каждая точка \(P\) поверхности волны описывается уравнением

$$P(x, y, t) = \big(x, y, H(x, y, t)\big);$$

 дифференцируя это уравнение по \(x\) при каждом фиксированном \(t\), получим вектор бинормали

$$B(x, y, t) = \left( 1, 0, \frac{\partial H}{\partial x}(x, y, t) \right),$$

а по \(y\)nbsp;mdash; тангенциальный вектор

$$T(x, y, t) = \left( 0, 1, \frac{\partial H}{\partial y}(x, y, t) \right). $$

Вместе векторы \(B\) и \(T\) образуют базис касательного пространства в точке \(P\). Их векторное произведение даст нормаль:

$$N(x, y, t) = B(x, y, t) \times T(x, y, t) = \left( -\frac{\partial H}{\partial x}(x, y, t), -\frac{\partial H}{\partial y}(x, y, t), 1 \right).$$

 

Чтобы пики волн стали более выраженными и, соответственно, чуть более близкими к реальности, используется следующий простой приём. Синусоидальная волна нормируется до отрезка \([0,1]\), после чего возводится в небольшую степень \(k\), превышающую единицу. Это даёт чуть более острые пики:

Степени синусов

 

Кстати, если взять \(k\) из интервала \((0, 1)\), то волны, наоборот, раздуются, а более резкими станут минимумы.

 1.2.3 Сеточные волны

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

 Плоские и сферические волны

У нас всегда есть выбор между плоскими и сферическими волнами:

Плоские и сферические волны

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

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

Волны Герстнера

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

Волна Герстнера

 Волновая функция Герстнера имеет следующий вид:

$$ P(x, y, t) = \begin{bmatrix} x + \sum_{i=1}^{n} Q_i A_i \cdot D_{i, x} \cdot \cos \big(\omega_i D_i \cdot (x, y) + \varphi_i \cdot t\big) \\ y + \sum_{i=1}^{n} Q_i A_i \cdot D_{i, y} \cdot \cos \big(\omega_i D_i \cdot (x, y) + \varphi_i \cdot t\big) \\ \sum_{i=1}^{n} A_i \cdot \sin \big(\omega_i D_i \cdot (x, y) + \varphi_i \cdot t\big) \end{bmatrix} $$

Здесь \(Q_i\) — параметр, контролирующий крутизну гребня волны. \(Q_i = 0\) даёт обычную синусоидальную волну, а \(Q_i = 1 / (\omega_i A_i) \) — острый гребень. Большие этого значения \(Q_i\) следует избегать, поскольку они могут привести к артефактам.

Видно интересное преимущество волны Герстнера — она сгущает точки сетки к гребню волны, то есть как раз к наиболее интересующему нас в плане точности отображения месту. При этом производные и касательные всё так же можно найти в явном виде дифференцированием по \(x\) и \(y\) (что мы опустим).

 Длина и скорость волн

Выбрав соответствующие физике нашей сцены волны, мы должны теперь подобрать для них подходящие параметры. Начнём с длин волн. Лучше всего при небольшом количестве элементарных волн динамику воде придаёт использование близких значений длин волн. Поэтому разумно выбрать некую среднюю длину волны и далее вычислить случайные длины волн в интервале, например, от половины среднего до удвоенного среднего. Притом средняя длина может изменяться со временем, например, повышаться в шторм или снижаться в безветренную погоду. Но нельзя изменять длину уже имеющихся волн, даже постепенно — это даёт очень неестественную картинку. Поэтому необходимо дожидаться, пока старые волны умрут, прежде чем добавлять новые с новой длиной волны. (Возможно, можно постепенно снижать амплитуду устаревших волн до нуля, чтобы процесс «смерти» шёл быстрее.) То же самое касается направления волн.

Получив длину волны \(L\),  мы можем легко вычислить её скорость. Дисперсионное соотношение для воды (без учёта членов высокого порядка) имеет вид

$$\omega = \sqrt{g\cdot \frac{2\pi}{L}},$$

где \(\omega\) — частота волны, а \(g\) — ускорение свободного падения.

Амплитуда волн

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

Направление волн

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

1.2.4 Текстурные волны