Что если в играх использовать видеокарточку для физики, а не для графики

Хочу рассказать сообществу о проведённом мной эксперименте.

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

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

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

Или ещё замечательный пример — Kerbal Space Program. Там физика уже является непосредственым источником геймплея.

Или, например, жанр 2д артиллерии. Часть его очарования базируется на уничтожаемой, динамичной земле. Но до чего он был бы лучше, если б земля не просто линейно осыпалась, а вела себя реалистично, разлетаясь от взрывов кусками.

Я давно мечтал сделать именно такой, до предела физически реалистичный римейк Scorched Earth. Но все мои эксперименты с моделированием физических систем упирались в неумолимо медленные процессоры. Тысяча-две частиц были пределом для real-time симуляции.

Но недавнее моё «открытие» изменило ситуацию. Мы живём во времена быстрого развития геймерского железа. Развивались главным образом видеокарточки, потому что производители игр с наибольшим энтузиазмом наращивали именно графическую составляющую игр, а производители железа поддерживали высокий fps. И не удивительно решение компании Nvidia добавить возможность для разработчиков писать код для графической системы, то есть осуществлять вычисления в графичесокм ядре.

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

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

Осознав это, я понял, что наконец-то смогу создать полностью физическую игру, в которой не будет заскриптовано ничего, кроме физики. Игра станет эквивалентна физической симуляции.

Я давно делаю игры на Юнити, и был рад, узнав, что в этом движке реализован класс ComputeShader, который позволяет использовать в проекте шейдеры на языке HLSL. Просто пишем шейдер, линкуем его к экземпляру ComputeShader, и диспатчим в Update.

Вникнуть в параллельные вычисления на GPU было не слишком просто. Туториалов маловато, а те, что есть, довольно ограничены в объёме поясняемых тонкостей. Но ключевых трудностей было не так уж много, довольно богата справочная информация по HLSL была на msdn, так что кое-как путём проб и ошибок я овладел спецификой и начал делать игру.

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

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

Кроме того, при взаимодействии частиц нужно было работать с данными в защищённом режиме, чтобы паралельные потоки были осведомлены об одновременной записи-чтении и не путались. Ведь если частица может одновременно взаимодействовать с десятком других частиц, все они могут параллельно изменять её скорость, а значит, необходимо делать это в защищённом режиме. Средства для этого в HLSL нашлись. Правда, операторы вроде InterlockedAdd () работают только с int-величинами, так что приходилось пожертвовать тоностью, и хранить скорость в видеопамяти в виде int-величин.

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

$O(n^2)$

до чего-то вроде

$O(log(N) * N)$

. Этого я добился, создав двумерную сетку 256×256, и в каждом её элементе на каждом шагу сохранял ссылки на все ближайшие частицы, так что при обсчёте взаимодействия частиц, каждая частица взаимодействует лишь с теми частицами, что находятся в пределах нескольких 3×3 элементов сетки.

Кстати, почему вместо уже реализованного в Юнити физического движка я создал свой? Потому что универсальный физический движок, подходящий для широкого спектра задач, связанных с моделированием системы твёрдых тел, плохо подходит для моделирования системы взаимодействующих материальных точек. Я предпочёл оптимизированный под специфику оригинальный движок. Если создать в юнити тысячу объектов с rigidbody, можно убедиться, что fps падает довольно сильно. В моём случае нужны десятки тысяч частиц, и написанный с нуля движок позволяет их вычислять с хорошим fps.

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

Возникает противоречие: нам нужна быстрая real-time симуляция. Но шаг должен быть очень маленький. Это противоречие я разрешил уменьшив шаг в десять раз по сравнению с первыми экспериментами, и производя десять циклов симуляции в каждом Update (). Цена этого решения — производительность. Так что пришлось сильно уменьшить количество частиц. Но всё же их осталось достаточно для сложного поведения всей системы.

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

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

Что в итоге получилось? Получилась игра в жанре »2д артиллерия», вроде Pocket Tanks, Scorched Earth или Worms.

Вот короткая гифка, в которой показан игровой процесс:
imgur.com/gallery/Q5w8cjF.gif

А вот длинное, в десять минут, видео, в котором тоже показан игровой процесс, но более подробно:

Можно заметить, что земля и строения выглядят как желе. Это исправляется за производительности. Можно уменьшить шаг и усилить коэффициент влияния полей на скорость частиц. Но пока я стараюсь удержать игру на уровне не слишком обременительном для большинства не слишком старых видеокарт. Скажем, на моей карте GTX 750m, в которой 384 ядра, игра с 20 тысячами частиц работает с частотой 25 fps, что делает её вполне играбельной.

Вывода здесь два, объективный и субъективный:

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

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

Комментарии (19)

  • 22 января 2017 в 18:16

    0

    А почему не Bullet?

    • 22 января 2017 в 18:21

      0

      Потому что вычисления для твёрдого тела не требуются, здесь всё построено на вычислениях для материальной точки. Кроме того, игры в жанре 2d-sandbox, вроде Powder Toy или OE Cake, не использовали gpu-вычисления, так что я сразу решил сделать своё, полагая, что такие движки пока просто не появились.
      • 22 января 2017 в 18:26

        0

        На самом деле новый Bullet не пригоден для OpenGL 2.1 / OpenGL ES 2.0 видеокарт. Да и с C API проблемы. Не такой уж радужный инструмент. Он вообще рассчитан на отдельную видеокарту под физику, дескать одна для физики — одна для графики.

  • 22 января 2017 в 18:20

    +2

    Хмм, очень странная статья. CUDA появилась в 2007, OpenCL в 2008, все даавно используют видеокарты для самых разных вычислений.


    По-моему, это решение было тихой революцией.

    Да, но это случилось 10 лет назад :)

    • 22 января 2017 в 18:24

      0

      Согласен, это всё появилось давно. Я хотел сосредоточить внимание не на самой технической возможности считать на видеокарточке, а на альтернативе обычному подходу к геймплею, когда физику используют как вспомогательную и незначительную составляющую. Статья посвящена возможности построить геймплей на физике полностью.
      • 22 января 2017 в 18:41

        +2

        Очень странно «открывать» на это глаза в мире победивших Angry Birds.
        • 22 января 2017 в 18:54

          0

          Angry Birds — тоже хороший пример физики, создающей геймплей. Хотя, у них геймплей слишком простой, нет передвигающихся персонажей. И физика тоже проста — несколько спрайтов с коллайдерами на уровень. Но это уже кое-что. С момента выпуска прошло несколько лет. Появилось много клонов, но все они не пытаются выйти из ограниченной физики оригинала. Я просто попытался пойти дальше, как на уровне геймплея, так и на уровне физики, чтоб посмотреть, насколько интересно довести процент физики до предела.
          • 22 января 2017 в 18:58

            0

            Это что-то из разряда «да, там красные пиксели есть, но не много. Но хочется довести их количества до предела.»
            Физика — сама по себе геймплей не создает. Это один из инструментов геймдизайнера, но не единственный. Если использовать только его получится либо песочница, либо симулятор козла-хирурга (впрочем тоже песочница). Игры не получится.
            • 22 января 2017 в 19:03

              0

              2D-артилерия состоит на 100% из физики. Причём примитивной. А геймплей создаёт чувак, которого надо загасить или он загасит тебя.

        • 22 января 2017 в 19:01

          0

          Кстати, ещё Cut The Rope — в каком-то смысле про физику.


          Сходу больше не могу вспомнить, но мне кажется, что я видел больше одной подобной «головоломки с физикой».

  • 22 января 2017 в 18:26

    +1

    OpenCL, CUDA. Может вам эти технологии жизнь облегчат.
    • 22 января 2017 в 18:32

      0

      CUDA — Nvidia specific. А компьют шейдер в юнити компилится в opnegl или directx.
  • 22 января 2017 в 18:40

    0

    Но все мои эксперименты с моделированием физических систем упирались в неумолимо медленные процессоры. Тысяча-две частиц были пределом для real-time симуляции.

    Всё упирается в вашу неспособность сделать это.
    В 2007 году вышла игра Maelstrom.
    С терраформингом и растекающимися жидкостями.
    А до этого был Периметр. Что уж греха таить, я и сам в 2005 году делал демки с растекающейся водой.
    2000 частиц — это даже близко не предел CPU начала 2000х. Не говоря уж о современных системах.
    • 22 января 2017 в 18:50

      0

      Да, возможно я что-то не так делал. Хотя, уделал оптимизации большое внимание.

      Терраформинг — это изменение координат вершин меша, это не требует больших вычислительных ресурсов.

      А как вы делали воду, частицами? Сколько удавалось в рилтайме моделировать?

      • 22 января 2017 в 18:55

        0

        Да, возможно я что-то не так делал. Хотя, уделал оптимизации большое внимание.

        ДА полюбому. Как минимум куча физически сложных игр, существующих задолго до CUDA тому показатель.
        Терраформинг — это изменение координат вершин меша, это не требует больших вычислительных ресурсов.

        Терраформинг — это немного больше, чем перемещение вершин. :)
        ТАк можно сказать, что и у вас просто вершины двигать надо.
        А как вы делали воду, частицами? Сколько удавалось в рилтайме моделировать?

        Простите, нет желания раскапывать архивы. А навскидку вспомнить не получится, это было очень давно.
        Вы можете глянуть Maelstrom в режиме сетки. Там вполне можно оценить сложность расчетов.
        Тем более что там еще и влиения ветра уникальное в каждой точке.
  • 22 января 2017 в 18:54

    0

    http://www.nvidia.ru/object/nvidia-physx-ru.html
    • 22 января 2017 в 18:57

      0

      PhysX — универсальный движок, но он не слишком хорошо годится для этой специфической задачи. У меня взаимодействуют не твёрдые тела, а материальные точки.
      • 22 января 2017 в 19:03

        0

        не только твердые тела.
  • 22 января 2017 в 18:58 (комментарий был изменён)

    0

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

© Habrahabr.ru