Партиклы и симуляция жидкости на GPU

Three.js предоставляет мощные инструменты для создания и управления системами частиц. Основным строительным блоком является THREE.Points, который сочетает в себе геометрию и материал. Геометрия определяет позиции частиц в пространстве, а материал — их визуальные характеристики: цвет, размер, прозрачность, текстуру.

Ключевые элементы:

  • THREE.BufferGeometry — хранение массивов позиций, цветов, размеров.
  • THREE.PointsMaterial — настройка цвета, размера, прозрачности, карты текстур частиц.
  • THREE.TextureLoader — загрузка текстур для частиц с эффектом свечения или рассеивания.

Пример создания простой системы частиц:

const particleCount = 1000;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);

for (let i = 0; i < particleCount * 3; i++) {
    positions[i] = (Math.random() - 0.5) * 100;
}

geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

const material = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5 });
const particles = new THREE.Points(geometry, material);
scene.add(particles);

Продвинутая оптимизация: использование BufferGeometry и атрибутов

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

  • velocity — скорость частицы в пространстве.
  • lifetime — срок жизни частицы для анимации исчезновения.
  • color — динамическое изменение цвета через шейдер.

Пример добавления пользовательских атрибутов:

const velocities = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount * 3; i++) {
    velocities[i] = (Math.random() - 0.5) * 0.1;
}
geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));

Эти атрибуты можно использовать в custom shaders для реализации сложных эффектов.


Шейдеры для частиц: Vertex и Fragment

Для продвинутой визуализации необходимы GLSL-шейдеры. Vertex shader отвечает за позицию каждой частицы, fragment shader — за цвет и прозрачность.

Пример минимального vertex shader:

attribute vec3 velocity;
uniform float time;

void main() {
    vec3 newPosition = position + velocity * time;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    gl_PointSize = 2.0;
}

Fragment shader позволяет создавать эффекты свечения и текстуры:

void main() {
    float dist = length(gl_PointCoord - vec2(0.5));
    if (dist > 0.5) discard;
    gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0);
}

Использование шейдеров дает полный контроль над движением, цветом, размером и прозрачностью каждой частицы.


Системы частиц на GPU (GPU Particle Systems)

Для симуляции тысяч и миллионов частиц на высокой частоте кадров применяется GPU-based particle system. Идея заключается в хранении данных о частицах в текстурах, которые обновляются с помощью THREE.ShaderMaterial и render-to-texture через THREE.WebGLRenderTarget.

Принцип работы:

  1. Инициализация текстуры с исходными данными частиц (позиция, скорость, масса).
  2. Использование ping-pong рендеринга для обновления данных на GPU без возврата на CPU.
  3. Использование этих текстур в vertex shader для визуализации частиц.

Пример генерации render target:

const gpuTextureSize = 128; // 128x128 частиц = 16384 частиц
const rtPosition = new THREE.WebGLRenderTarget(gpuTextureSize, gpuTextureSize, { 
    type: THREE.FloatType 
});

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


Симуляция жидкости на GPU

Симуляция жидкости требует моделирования гидродинамики, что реализуется через SPH (Smoothed Particle Hydrodynamics) или упрощенные подходы на GPU. Основная идея — каждая частица имеет:

  • Положение и скорость
  • Плотность и давление
  • Влияние соседних частиц через ядро сглаживания

На практике это делается через compute shaders или имитацию их с помощью fragment shader + render target. Основные шаги:

  1. Вычисление плотности и давления каждой частицы.
  2. Вычисление силы давления и вязкости.
  3. Обновление скорости и позиции.
  4. Рендеринг частиц с учетом освещения и прозрачности.

Пример псевдокода GPU update:

vec3 computeVelocity(vec3 pos, vec3 vel) {
    vec3 force = vec3(0.0);
    for each neighbor {
        float density = computeDensity(neighbor);
        float pressureForce = computePressureForce(pos, neighbor);
        force += pressureForce;
    }
    vel += force * dt;
    return vel;
}

Интеграция с Three.js

Для визуализации жидкостей и частиц в Three.js используются:

  • THREE.Points для частиц
  • THREE.ShaderMaterial для управления динамикой через GPU
  • THREE.WebGLRenderTarget для хранения состояния частиц
  • THREE.Clock для синхронизации времени

Обновление симуляции происходит в цикле рендеринга:

function animate() {
    requestAnimationFrame(animate);
    updateGPUPositions();
    renderer.render(scene, camera);
}

Эффекты жидкости усиливаются использованием текстур частиц с прозрачностью, блюром и цветовой градацией в fragment shader.


Важные оптимизации

  • Ping-pong render targets для предотвращения конфликтов данных.
  • Использование FloatType текстур для точного хранения позиций и скоростей.
  • Сжатие количества частиц с помощью LOD (Level of Detail).
  • Интерполяция во времени для плавности анимации без чрезмерного увеличения числа частиц.
  • Batch-рендеринг с использованием единого THREE.Points вместо отдельных объектов.

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