XNA - Создание множества частиц одновременно - PullRequest
6 голосов
/ 29 июня 2010

время для другого вопроса XNA. На этот раз это чисто с точки зрения технического дизайна.

Моя ситуация такова: Я создал движок частиц на основе вычислений на GPU, далеко не полный, но он работает. Мой графический процессор легко обрабатывает частицы размером 10 тыс. Без потери пота, и я не удивлюсь, если смогу добавить еще кучу.

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

Создание частиц все еще выполняется вызовами CPU, такими как:

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

Когда у меня около 4 излучателей, создающих одну частицу на кадр, мой FPS понижается (конечно, только 4 кадра в секунду, но 15 излучателей снижают мой FPS до 25).

Создание частицы:

        //### As you can see, not a lot of action here. ###
        ParticleVertex []tmpVertices = ParticleQuad.Vertices(Position,Velocity,this.TimeAlive);
        particleVertices[i] = tmpVertices[0];
        particleVertices[i + 1] = tmpVertices[1];
        particleVertices[i + 2] = tmpVertices[2];
        particleVertices[i + 3] = tmpVertices[3];
        particleVertices[i + 4] = tmpVertices[4];
        particleVertices[i + 5] = tmpVertices[5];

        particleVertexBuffer.SetData(particleVertices);

Я думаю, что, может быть, мне не следует создавать частицы так часто, может быть, есть способ, позволяющий GPU создавать все, или, может быть, я просто не знаю, как вы это делаете. ;)

Редактировать: Если бы я не создавал частицы так часто, то какой обходной путь для того, чтобы он все еще выглядел хорошо?

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

1 Ответ

4 голосов
/ 29 июня 2010

Невозможно заставить GPU создавать все (если не использовать Геометрические шейдеры , для которых требуется SM4.0).

Если бы я создавал систему частиц для максимальной эффективности процессора, я бы предварительно создал (просто для примера выбрал число) 100 частиц в буфере вершин и индексов, например:

  • Создайте буфер вершин, содержащий четыре квадрата (четыре вершины на частицу, а не шесть, как у вас)
  • Используйте пользовательский формат вершин, который может хранить значение «временного смещения», а также значение «начальной скорости» (аналогично XNA Particle 3D Sample )
  • Установите значение времени так, чтобы каждая частица имела временное смещение на 1/100 меньше, чем последняя (таким образом, смещения варьируются от 1,0 до 0,01 через буфер).
  • Установить начальную скорость случайным образом.
  • Используйте индексный буфер, который дает вам два необходимых треугольника, используя четыре вершины для каждой частицы.

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

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

  • Per-Vertex:
    • Смещение по времени
    • Начальная скорость
  • Параметры шейдера:
    • Текущее время
    • Время жизни частиц (которое также является значением времени обтекания частиц и долей используемых частиц в буфере)
    • Положение системы частиц / вращение / масштаб (мировая матрица)
    • Любые другие интересные материалы, которые вам нравятся, такие как: размер частиц, гравитация, ветер и т. Д.
    • Шкала времени (чтобы получить реальное время, чтобы расчеты скорости и другие физики имели смысл)

Этот вершинный шейдер (опять же как XNA Particle 3D Sample ) может затем определить положение вершины частицы на основе ее начальной скорости и времени, в течение которого эта частица находилась в симуляции.

Время для каждой частицы будет (псевдокод):

time = (currentTime + timeOffset) % particleLifetime;

Другими словами, с течением времени частицы будут выделяться с постоянной скоростью (из-за смещения). И всякий раз, когда частица умирает в time = particleLifetime (или это в 1,0 - модуль с плавающей запятой сбивает с толку) , время возвращается к time = 0.0, так что частица снова входит в анимацию. *

Затем, когда пришло время рисовать мои частицы, я бы установил параметры буфера, шейдера и шейдера и вызвал DrawIndexedPrimitives. Теперь вот умный бит: я бы установил startIndex и primitiveCount так, чтобы никакая частица не начиналась в середине анимации. Когда система частиц сначала запускается, я нарисую 1 частицу (2 примитива), и к тому времени, когда частица вот-вот умрет, я нарисую все 100 частиц, сотая из которых только начинается.

Затем, мгновение спустя, таймер 1-й частицы зациклится и сделает его 101-й частицей.

(Если бы я хотел, чтобы в моей системе было только 50 частиц, я бы просто установил время жизни моих частиц равным 0,5 и только нарисовал бы первые 50 из 100 частиц в буфере вершин / индексов.)

И когда пришло время выключить систему частиц - просто сделайте то же самое в обратном порядке - установите startIndex и primitiveCount так, чтобы частицы перестали вытягиваться после того, как они умрут.

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

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

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

Все это говорит: если возможно, возможно, стоит использовать существующую библиотеку частиц.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...