XNA-создание большого количества частиц одновременно
время для другого вопроса XNA. На этот раз это чисто с точки зрения технического дизайна.
моя ситуация такая: Я создал движок частиц на основе GPU-вычислений, далеко не полный, но он работает. Мой GPU легко обрабатывает частицы 10k, не потея, и я не удивлюсь, если смогу добавить еще кучу.
моя проблема: всякий раз, когда у меня есть много частиц, созданных в то же время, моя частота кадров ненавидит мне. Почему? Много использования процессора, хотя я минимизировал его, чтобы содержать почти только операции с памятью.
создание частиц по-прежнему выполняется CPU-вызовами, такими как:
- метод хочет создать частицу и делает вызов.
- Quad создается в виде вершин и хранится в буфере
- буфер вставляется в GPU, и мой процессор может сосредоточиться на других вещах
когда у меня есть около 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 создавать все, или, может быть, я просто не знаю, как вы это делаете. ;)
Edit: если бы я не создавал частицы так часто, каков обходной путь для того, чтобы он выглядел хорошо?
Так я размещаю здесь в надежде, что вы знаете, как должен быть спроектирован хороший двигатель частиц, и, возможно, я выбрал неправильный маршрут где-то.
1 ответов
нет никакого способа, чтобы GPU создавал все (кроме использования Геометрические Шейдеры что требует SM4.0).
если бы я создавал систему частиц для максимальной эффективности процессора, я бы создать (просто чтобы выбрать число для примера) 100 частиц в буфере вершин и индексов, как это:
- сделайте буфер вершин, содержащий квадроциклы (четыре вершины на частицу, а не шесть, как у вас)
- использовать пользовательские формат вершин, который может хранить значение "смещение по времени", а также Значение" начальная скорость " (аналогично образец частицы 3D XNA)
- установите значение времени таким образом, чтобы каждая частица имела смещение по времени на 1/100 меньше, чем последняя (поэтому смещения варьируются от 1,0 до 0,01 через буфер).
- установить начальную скорость случайным образом.
- используйте буфер индекса, который дает вам два треугольника, которые вам нужны, используя четыре вершины для каждого крупица.
и самое классное, что вам нужно сделать это только один раз - вы можете повторно использовать один и тот же буфер вершин и буфер индексов для всех ваших систем частиц (при условии, что они достаточно велики для вашей самой большой системы частиц).
тогда у меня будет вершинный шейдер, который будет принимать следующие входные данные:
-
Per-Vertex:
- смещение по времени
- начальный скорость
-
Шейдер Параметры:
- настоящее время
- время жизни частиц (которое также является значением времени обертывания частиц и долей частиц в используемом буфере)
- положение/вращение/масштаб системы частиц (мировая матрица)
- любые другие интересные входы, которые вам нравятся, такие как: размер частиц, гравитация, ветер и т. д.
- шкала времени (чтобы получить реальное время, поэтому скорость и другие физические расчеты имеют смысл)
что вершинный шейдер (опять же, как образец частицы 3D XNA) может затем определить положение вершины частицы на основе ее начальной скорости и времени, которое эта частица была в моделировании.
времени для каждой частицы будет (псевдо код):
time = (currentTime + timeOffset) % particleLifetime;
другими словами, с течением времени частицы будут высвобождаться при постоянной ставка (за счет смещения). И всякий раз, когда частица умирает в time = particleLifetime
(или это в 1.0? модуль с плавающей запятой сбивает с толку), петли времени назад к time = 0.0
Так что частица снова входит в анимацию.
затем, когда пришло время рисовать мои частицы, я бы установил свои буферы, шейдер и параметры шейдера и позвонил DrawIndexedPrimitives
. Теперь вот умный бит: я бы установил startIndex
и primitiveCount
такой, что ни одна частица не начинается в середине анимации. Когда сначала я нарисую 1 частицу (2 примитива), и к тому времени, когда эта частица умрет, я нарисую все 100 частиц, 100-я из которых только начнется.
затем, мгновение спустя, таймер 1-й частицы сделает петлю вокруг и сделает ее 101-й частицей.
(если бы я хотел только 50 частиц в моей системе, я бы просто установил время жизни частиц на 0,5 и только когда-либо рисовал первые 50 из 100 частиц в вершине / индексе буфер.)
и когда пришло время отключить систему частиц-просто сделайте то же самое в обратном направлении - установите startIndex
и primitiveCount
такие, что частицы перестают быть нарисованными после того, как они умирают.
один недостаток кругового буфера заключается в том, что, когда вы перестаете испускать частицы, если вы не остановитесь, когда текущее время кратно времени жизни частицы, вы получите активный набор частиц, охватывающих концы буфера с зазором посередине, что требует двух вызовов рисования (немного медленнее). Чтобы избежать этого, вы можете подождать, пока не наступит время перед остановкой - для большинства систем это должно быть нормально, но может выглядеть странно для некоторых (например: a "медленная" система частиц, которая должна немедленно остановиться).
другим недостатком этого метода является то, что частицы должны выделяться с постоянной скоростью, хотя это обычно довольно типично для систем частиц (очевидно, это для каждой системы, и скорость регулируется). При небольшой настройке эффект взрыва (все частицы, выпущенные сразу) должен быть возможен.
все, что было сказано: Если возможно, возможно, стоит использовать существующую библиотеку частиц.