Система частиц с WebGL

Современные игры не были бы так хороши без системы частиц.

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

Хорошая система частиц потребляет мало памяти, быстро работает и имеет многочисленные настройки.

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

В нашей системе у частицы будут такие параметры: — позиция — прозрачность — сила гравитации — ускорение — скорость

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

Сначала демонстрация в цветовой гамме MS-DOS (поводите мышкой по черной области), затем пояснения и исходники на JavaScript:

Частица представлена у нас в виде javascript объекта:

var Particle = function() {
    // Позиция частицы
    this.x = 0;
    this.y = 0;
 
    // Скорость движения частицы по x, y
    this.vx = 0;
    this.vy = 0;
 
    // Ускорение частицы по x, y
    this.ax = 0;
    this.ay = 0;
 
    // Прозрачность частицы
    this.alpha = 0;
    // Изменение прозрачности в секунду
    this.vAlpha = 0;
 
    // Размер частицы
    this.size = 0;
 
    // Флаг, что частица активна
    this.active = false;
};

Инициализация массива частиц:

    // Инициализируем массив частиц
    particles = new Array(numParticles);
 
    for (var i = 0; i < numParticles; ++i) {
        // Каждая частица изначально выключена
        particles[i] = new Particle();
    }

Чтобы частицы создавались непрерывно, введем переменную pps, она будет означать количество новых частиц в секунду. Для вычисления необходимого количества частиц, введем переменную realTime типа float. Она будет накапливать количество секунд для создания хотя бы одной частицы.

Формула для вычисления количества частиц:

newParticleCount = Math.floor(pps * realTime)

После вычисления количества, необходимо уменьшить таймер:

realTime -= newParticleCount / pps

Общий код создания частиц:

var newParticleCount = Math.floor(pps * realTime)
 
if (newParticleCount > 0) {
    realTime -= newParticleCount / pps;
 
    for (var i = 0; i < particles.length; ++i) {
        if (newParticleCount <= 0) {
            break;
        }
 
        var particle = particles[i];
 
        if (!particle.active) {
            add(particle);
            newParticleCount--;
        }
    }
}

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

function add(particle) {
    particle.x = someX;
    particle.y = someY;
 
    particle.vx = someVX;
    particle.vy = someVY;
 
    particle.ax = someAX;
    particle.ay = someAY;
 
    particle.alpha = someAlpha;
    particle.vAlpha = someVAlpha;
 
    particle.active = true;
}

Для придания реалистичности движения частицы используется ускорение и гравитация:

particle.vx += particle.ax * dt;
particle.vy += particle.ay * dt;
 
particle.vy -= gravity * dt;

Если частица полностью прозрачна или выходит за пределы экрана (слева, справа или снизу), то частица отключается и выпадает из обработки:

if (particle.alpha < 0 || 
    particle.x < -1 || particle.x > 1 || 
    particle.y < -1) {
 
    particle.active = false;
}

После обновления позиции и прозрачности частицы, заполняем вершинный буфер, в котором на каждую частицу приходится два треугольника, шесть вершин и по три значения на вершину (x, y, alpha):

// Слева снизу
vertices[index++] = left;
vertices[index++] = bottom;
vertices[index++] = alpha;
 
// Справа снизу
vertices[index++] = right;
vertices[index++] = bottom;
vertices[index++] = alpha;
 
// Слева сверху
vertices[index++] = left;
vertices[index++] = top;
vertices[index++] = alpha;
 
// Справа снизу
vertices[index++] = right;
vertices[index++] = bottom;
vertices[index++] = alpha;
 
// Справа сверху
vertices[index++] = right;
vertices[index++] = top;
vertices[index++] = alpha;
 
// Слева сверху
vertices[index++] = left;
vertices[index++] = top;
vertices[index++] = alpha

Дальше дело техники: отрисовать вершинный буфер с правильный шейдером и режимом смешивания.

Вы молодец, если дочитали до конца!

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

Отвратительно!Ужасно.НормальноХорошо.Отлично! (5 оценок, среднее: 5,00 из 5)
Загрузка...

Ваши комментарии к статье:

Оставить комментарий: