Флокінг у 3D: Boids з перешкодами та лідером
Три оригінальні правила Рейнольдса були створені для плоскої площини. Перенесіть ту саму зграю у повне 3D — крізь систему печер, навколо хмарочоса, за мігруючим лідером — і кожна частина алгоритму, від пошуку сусідів до орієнтації, потребує додаткової уваги через новий вимір.
1. Від 2D до 3D: додатковий ступінь свободи
Оригінальна демонстрація Boids 1986 року працювала на плоскій площині. Додавання третього просторового виміру — це більше, ніж "просто додати z". Кожен запит сусідів тепер має шукати в об'ємі простору замість площі, тобто рівномірна сітка перевіряє 27 сусідніх комірок (3×3×3) замість 9, а орієнтація — вже не один кут напряму θ, а повне 3D-обертання.
Птахи, риби та дрони також кренять під час поворотів — вони обертаються навколо напряму руху, а не просто повертаються до нього. Щоб це відобразити, потрібні кути Ейлера (прості, але страждають від шарнірного замка) або кватерніони (числово стабільні, стандартний вибір у промислових 3D-рушіях).
Розлучення, вирівнювання і зближення переносяться незмінними як 3D-векторні операції — див. нашу статтю про 2D Boids щодо базових правил. Ця стаття зосереджується на тому, що дійсно нове у 3D: перешкодах, лідері та орієнтації.
2. Уникнення перешкод за допомогою променів
Статична геометрія — стіни печер, будівлі, рельєф — має відхиляти зграю, не руйнуючи її цілісності. Стандартна техніка випромінює один або кілька зондувальних променів вперед від кожного boid:
hit = intersect(ray, obstacles)
if hit: v_avoid = reflect(vel[i], hit.normal) · weight_avoid
- Один прямий промінь — дешевий, але пропускає перешкоди, які boid зачепить збоку.
- Віяло з трьох променів (центральний + два зміщені на невеликий кут) — виявляє близькі промахи за помірну додаткову ціну.
- Sphere-cast — прокидає сферу власного радіуса boid уздовж шляху, гарантуючи, що жодна частина тіла не зачепить перешкоду.
Самі перешкоди майже завжди спрощують до сфер чи капсул для швидких перевірок перетину, навіть коли візуальний меш складний — фізичний колайдер і рендерний меш зберігаються окремо, як в ігрових рушіях.
3. Слідування за лідером
Щоб зграя слідувала конкретним маршрутом — міграція через гірський хребет, супровід персонажа гравця — один або кілька boids призначаються лідерами. Лідери рухаються за незалежно скриптованим шляхом (сплайн Безьє чи Catmull-Rom, або пряме керування гравцем/ШІ) і виключені зі стандартних сил флокінгу.
де offset розташовує послідовників трохи позаду і збоку від лідера (уникаючи "конга-лінії" boids, що вишиковуються прямо за ним), а w_leader зазвичай важить більше, ніж звичайне зближення.
Послідовники все ще застосовують розлучення та вирівнювання між собою, тож зграя зберігає свою органічну, "дихаючу" форму, відстежуючи загальну траєкторію лідера — це техніка, що лежить в основі більшості поведінок "слідуй за птахом-провідником" чи супроводу в іграх.
4. Комбінування правил і пріоритети
З п'ятьма чи шістьма конкуруючими силами (розлучення, вирівнювання, зближення, уникнення перешкод, слідування за лідером, обмеження меж) наївне сумування може дати сумарну силу, яка все ще заганяє boid прямо у стіну, якщо зближення достатньо сильне. Дві надійні стратегії комбінування:
- Зважена сума з пріоритетними вагами — уникнення перешкод отримує вагу на порядок більшу за соціальні правила, тож завжди домінує, коли активне.
- Пріоритетне перекриття — правила оцінюються по порядку; якщо уникнення перешкод дає ненульову силу вище порогу, використовуємо лише її, пропускаючи решту правил на цьому тику.
Пріоритетне перекриття передбачуваніше і саме його на практиці використовують більшість промислових систем флокінгу (включно з симуляцією натовпу в ігрових рушіях), бо гарантує, що жорсткі обмеження (ніколи не вдарятись об стіну) не можуть бути розбавлені м'якими перевагами (триматися ближче до зграї).
5. Крен і кватерніонна орієнтація
Візуальна орієнтація boid має відстежувати і куди він рухається (вектор вперед = нормалізована швидкість), і наскільки різко він повертає (крен пропорційний швидкості повороту). Стандартна побудова:
right = normalize(cross(worldUp, forward))
up = cross(forward, right)
bankAngle = clamp(turnRate · BANK_FACTOR, -MAX_BANK, MAX_BANK)
orientation = lookRotation(forward, up) · rotateAroundForward(bankAngle)
Подання результату як кватерніона та використання сферичної
лінійної інтерполяції (slerp) між кадрами уникає
розривів і шарнірного замка, що псують інтерполяцію сирих кутів
Ейлера, даючи плавний крен навіть під час різких маневрів.
6. Просторовий поділ у 3D
Той самий трюк з рівномірною сіткою з 2D boids поширюється напряму: розмір комірки ≈ радіус огляду, і кожен boid перевіряє 27 (3×3×3) сусідніх комірок замість 9. Для дуже нерівномірної щільності зграї (щільне ядро, помножене на розсіяних відсталих) октодерево адаптує розмір комірки локально й може перевершити рівномірну сітку, ціною складнішого обліку вставки/видалення щокадру.
Для 10 000+ boids у WebGL позиції кодуються через код Мортона (крива Z-порядку, що перемежовує біти x, y, z) і сортуються radix-сортуванням щокадру, тож просторово близькі boids опиняються поруч у GPU-буфері — це зберігає суміжність пошуку сусідів навіть у трьох вимірах.
7. Псевдокод
function stepBoids3D(boids, obstacles, leader, dt):
for each boid i:
// 1. Перевірка пріоритету: спершу уникнення перешкод
avoidForce = probeObstacles(boid[i], obstacles, LOOKAHEAD)
if length(avoidForce) > AVOID_THRESHOLD:
a = avoidForce
else:
// 2. Стандартний флокінг (3D вектори)
neighbors = getNeighbors3D(i, VIEW_RADIUS)
v_sep = separation(i, neighbors)
v_align = alignment(i, neighbors)
v_coh = cohesion(i, neighbors)
// 3. Слідування за лідером, якщо призначено
v_leader = leader ? followLeader(i, leader) : 0
a = W_SEP*v_sep + W_ALIGN*v_align + W_COH*v_coh + W_LEADER*v_leader
// 4. Інтегрування руху
vel[i] = clampLength(vel[i] + a * dt, MAX_SPEED)
pos[i] += vel[i] * dt
// 5. Оновлення орієнтації з креном
orientation[i] = computeBankedOrientation(vel[i], prevVel[i])
Типові константи: VIEW_RADIUS = 12,
LOOKAHEAD = 6,
AVOID_THRESHOLD = 0.1,
W_LEADER = 1.5–2.0 (вище за зближення, щоб зграя
надійно відстежувала лідера крізь повороти).
Часті запитання
Що змінюється, коли boids переходять з 2D до 3D?
Позиція та швидкість отримують z-компоненту, пошук сусідів має сканувати об'єм комірок (27 замість 9 для рівномірної сітки), а орієнтація — вже не один кут напряму, а повне 3D-обертання, зазвичай кватерніон, яке має відстежувати і напрям руху, і крен.
Як працює уникнення перешкод у 3D для boids?
Найпоширеніший підхід випромінює один або кілька зондувальних променів від boid у напрямі руху і перевіряє їх проти сферичних чи капсульних колайдерів. Якщо промінь перетинає перешкоду на відстані передбачення, додається сила уникнення перпендикулярно поверхні перешкоди з достатньою вагою, щоб перекрити зближення та вирівнювання.
Чим слідування за лідером відрізняється від звичайного флокінгу?
Один або кілька призначених boids рухаються за незалежно скриптованим маршрутом і виключені зі стандартних правил флокінгу. Звичайні члени зграї додають додатковий член, схожий на зближення, що тягне їх до недавньої позиції лідера, тож уся зграя слідує маршруту лідера, все ще застосовуючи розлучення та вирівнювання між собою.
Чому для орієнтації boid використовують кватерніони, а не кути Ейлера?
Чим відрізняється зважена сума від пріоритетного перекриття при комбінуванні сил?
Чому для виявлення перешкод випромінюють кілька променів, а не один?
Чому зміщення при слідуванні за лідером уникає прямолінійної "конга-лінії"?
Чим октодерево покращує рівномірну сітку для 3D-флокінгу?
Чи можуть уникнення перешкод і слідування за лідером конфліктувати, і як це вирішується?
Скільки boids може обробити 3D-симуляція флокінгу в реальному часі?
🐦 Спробуйте симуляцію 3D boids
Спостерігайте, як зграя обходить перешкоди та слідує за лідером у реальному часі — тисячі агентів, прискорені WebGL.
Відкрити симуляцію →