Наївна система частинок зі 100 000 частинок поставить навколішки
більшість GPU. Цей урок будує високопродуктивну систему за допомогою
BufferGeometry з типізованими масивами, спрайтів-білбордів,
що рендеряться через власний вершинний шейдер, та рівнів деталізації на
основі відстані для пропуску невидимої роботи.
1Плоский пул частинок з типізованими масивами
Зберігайте всі дані у плоских Float32Array замість
об'єктів JS. Кожен індекс відповідає одній частинці в кількох
паралельних масивах:
const MAX = 100_000; // Паралельні типізовані масиви — один слот на частинку
const pos = new Float32Array(MAX * 3); // xyz const vel = new
Float32Array(MAX * 3); // vxvyvz const life = new Float32Array(MAX);
// залишковий час життя (с) const maxLife = new Float32Array(MAX); //
початковий час життя для t=life/maxLife const size = new
Float32Array(MAX); // радіус частинки const dead = new
Uint8Array(MAX); // 0=жива, 1=мертва (для пулу) let alive = 0; // кількість
живих частинок let head = 0; // курсор випуску кільцевого буфера //
Ініціалізуємо всі як мертві dead.fill(1);
Типізовані масиви розміщуються в неперервній пам'яті, тож їх ітерація
дружня до кешу — значно швидша за масиви об'єктів на кшталт
{x,y,z,vx,vy,...}.
2Завантаження на GPU як BufferGeometry
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
const geo = new THREE.BufferGeometry(); const posAttr = new
THREE.BufferAttribute(pos, 3); const sizeAttr = new
THREE.BufferAttribute(size, 1); const lifeAttr = new
THREE.BufferAttribute(life, 1);
posAttr.setUsage(THREE.DynamicDrawUsage); // підказка: оновлюється щокадру
sizeAttr.setUsage(THREE.DynamicDrawUsage);
lifeAttr.setUsage(THREE.DynamicDrawUsage);
geo.setAttribute('position', posAttr); geo.setAttribute('a_size',
sizeAttr); geo.setAttribute('a_life', lifeAttr); geo.setDrawRange(0,
MAX); // відмальовуємо до MAX точок const particles = new THREE.Points(geo,
/* власний матеріал нижче */); scene.add(particles);
// Пропускаємо оновлення частинок далі за порогову відстань від камери
const camPos = new THREE.Vector3(); function updateLOD(camera) {
camera.getWorldPosition(camPos); for (let i = 0; i < MAX; i++) { if
(dead[i]) continue; const i3 = i * 3; const dx = pos[i3] - camPos.x;
const dy = pos[i3+1] - camPos.y; const dz = pos[i3+2] - camPos.z;
const d2 = dx*dx + dy*dy + dz*dz; // Пропускаємо оновлення, але все одно рендеримо
частинки далі за 200 одиниць if (d2 > 200 * 200) continue; // Повне
оновлення лише для близьких частинок // (інтегрування швидкості тощо вже
пропущене перевіркою LOD вище) } }
6Адитивне змішування для вогню та диму
// Вогонь: адитивне змішування — яскраво там, де частинки накладаються //
помаранчево-жовтий градієнт за часом життя const fireFrag = /* glsl */ ` varying
float v_life; void main() { float r = length(gl_PointCoord - 0.5); if
(r > 0.5) discard; // Колір: розпечений білий → помаранчевий → червоний vec3 hot =
vec3(1.0, 1.0, 0.7); vec3 mid = vec3(1.0, 0.4, 0.0); vec3 cool =
vec3(0.4, 0.0, 0.0); float t = clamp(v_life, 0.0, 1.0); vec3 col = t >
0.5 ? mix(mid, hot, (t - 0.5) * 2.0) : mix(cool, mid, t * 2.0); float
alpha = (1.0 - r * 2.0) * v_life; gl_FragColor = vec4(col * alpha,
alpha); } `; // Дим: звичайне змішування (src-alpha) із затемненням у відтінках
сірого з часом const smokeFrag = /* glsl */ ` varying float
v_life; void main() { float r = length(gl_PointCoord - 0.5); if (r >
0.5) discard; float grey = 0.1 + v_life * 0.4; float alpha = (1.0 - r
* 2.0) * v_life * 0.6; gl_FragColor = vec4(grey, grey, grey, alpha); }
`;
Поєднайте емітер вогню (адитивний) з емітером диму (альфа-змішування) —
додайте меш диму як окремий об'єкт Points, який
відмальовується після вогню, щоб забезпечити правильне сортування.
Часті запитання
Чого я навчуся в цьому уроці?
Побудуйте високопродуктивну систему частинок у Three.js за допомогою BufferGeometry, частинок-білбордів та перемикання рівнів деталізації для 100 тис.+ частинок.
Які теми розглядаються в цьому уроці?
Цей урок охоплює такі теми: Плоский пул частинок з типізованими масивами, Завантаження на GPU як BufferGeometry, Вершинний шейдер білбордів, Випуск та переробка частинок, LOD на основі відстані, Адитивне змішування для вогню/диму.
Які інструменти й технології використовуються?
Цей урок використовує: Three.js, BufferGeometry, Шейдери, Продуктивність.