BVH: ієрархія обмежувальних обʼємів для трасування променів та виявлення зіткнень
Рендеринг сцени з мільйонами трикутників через грубий перебір перетинів промінь–трикутник тривав би хвилини на кадр. BVH зводить це до мілісекунд — організовуючи геометрію в дерево вкладених обмежувальних коробок і пропускаючи цілі піддерева тієї ж миті, коли промінь не влучає в їхню коробку. Та сама структура даних забезпечує виявлення зіткнень у реальному часі як в ігрових рушіях, так і у фізично коректних трасувальниках шляху.
1. Проблема прискорення
Трасувальник шляху випускає мільйони променів на кадр. Кожен промінь має знайти найближчий перетин із геометрією сцени. Без прискорення перевірка одного променя проти N трикутників коштує O(N). За N = 10 мільйонів трикутників і 4 мільйони променів на кадр при 60 fps це 2,4 × 10¹⁵ перевірок трикутників на секунду — приблизно у 10⁶× понад можливості сучасного апаратного забезпечення.
Структури прискорення розвʼязують це, відсікаючи великі частини сцени до перевірок окремих трикутників. BVH знижує очікувану вартість променя з O(N) до O(log N) для рівномірно розподілених сцен — сцена з 10 мільйонів трикутників потребує ~23 перевірок обмежувальних коробок плюс жменьку перевірок трикутників на промінь, замість 10 мільйонів.
2. Обмежувальні коробки, вирівняні за осями
Кожен вузол у BVH містить AABB (обмежувальну коробку, вирівняну за осями) — найменшу коробку з гранями, паралельними координатним площинам, яка охоплює всі примітиви в піддереві цього вузла.
Перетин «промінь–AABB» (метод плит)
Перевірка променя проти AABB надзвичайно дешева — 6 ділень і кілька порівнянь — що робить її етапом швидкого відсіювання перед дорогими перевірками трикутників:
3. Побудова BVH: стратегії
BVH — це бінарне дерево. Кожен внутрішній вузол містить AABB і вказівники на два дочірні вузли. Листові вузли містять один або кілька примітивів. Ключове питання: як розбити множину примітивів на дві групи, щоб сформувати два дочірні вузли?
Медіанне розбиття за центроїдами
Найпростіший підхід: обчислити центроїди всіх примітивів у поточному вузлі. Розбити вздовж осі з найбільшим розкидом центроїдів, за медіанним значенням центроїда. Кожен примітив іде до того дочірнього вузла, чия обмежувальна коробка містить його центроїд.
Обʼєктне розбиття проти просторового
Обʼєктне розбиття (те, що робить BVH) призначає кожен примітив рівно одному вузлу. Просторове розбиття (kd-дерева) обрізає примітиви на площині розбиття — трикутник, що перетинає межу, потрапляє до обох дочірніх вузлів. Обʼєктне розбиття простіше й уникає дублювання примітивів; просторове може бути щільнішим для великих трикутників.
4. Евристика площі поверхні (SAH)
Евристика площі поверхні обирає розбиття, що мінімізує очікувану вартість влучання випадкового променя у вузол. Вона використовує геометричний факт, що ймовірність влучання випадкового променя у опуклий обʼєкт пропорційна його площі поверхні (для рівномірно розподілених променів в обмежувальній сфері).
SAH із кошиками (Вальд, 2007) — стандарт у промислових рендерерах. Вона наближає точний оптимум SAH (який потребує O(N log N) на рівень) майже з тією самою якістю, використовуючи лише K=32 кошики. Embree, Optix і практично всі промислові трасувальники шляху використовують SAH із кошиками або її варіанти.
Завершення на листку
Рекурсія зупиняється, коли вузол має ≤ N_leaf примітивів (зазвичай 1–4 трикутники). Менші листки покращують якість обходу (менше хибних спрацювань), але збільшують кількість вузлів і памʼять. Установлення N_leaf = 4 і використання 4-широкого SIMD для одночасної перевірки всіх 4 трикутників — поширена оптимізація у CPU-рендерерах (пакетне трасування).
5. Обхід «промінь–BVH»
Маючи промінь і корінь BVH, знайти найближчий перетин. Стандартний алгоритм використовує стек, щоб уникнути рекурсії (зручно для GPU):
Порядок «спершу ближчий дочірній» критичний для продуктивності: відвідуючи ближчий дочірній першим, tBest оновлюється до меншого значення раніше, що дозволяє агресивніше відсікати AABB дальших дочірніх («ранній вихід із тіснішим t»).
Пакетний та SIMD-обхід
CPU-рендерери перевіряють 4 або 8 променів паралельно («пакет променів») за допомогою SSE/AVX SIMD. Усі промені в пакеті спільно використовують той самий стек обходу, поки вони йдуть тим самим шляхом крізь BVH — «відсікання за пірамідою видимості» в кожному вузлі за спільною обмежувальною пірамідою пакета. Розбіжні пакети повертаються до індивідуального обходу. Це дає прискорення у 2–4× на когерентних променях (первинних променях із камери), але менший виграш на розсіяних вторинних променях.
6. BVH на GPU: LBVH з кодами Мортона
Побудова за SAH на CPU (згори донизу, послідовна) надто повільна для динамічних сцен, які потребують перебудови щокадру. Лінійна BVH (LBVH) будує дерево знизу вгору із сортування за кодами Мортона за ~5–20 мс на GPU — достатньо швидко для скелетної анімації чи систем частинок у реальному часі.
Якість LBVH нижча за SAH — дерева, впорядковані за Мортоном, можуть мати погані розбиття, коли примітиви скупчуються, — але будуються у 20–50× швидше. HLBVH (ієрархічна LBVH) використовує розбиття за SAH для верхніх рівнів дерева та LBVH для піддерев, поєднуючи швидкість побудови з доброю якістю обходу.
7. TLAS / BLAS у сучасних RT-API
DirectX Raytracing (DXR), Vulkan Ray Tracing та Metal RT використовують дворівневу структуру BVH, що відокремлює геометричні деталі від перетворень екземплярів:
У DXR BuildRaytracingAccelerationStructure() приймає
дескриптор D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS,
що задає списки геометрії (для BLAS) або дескриптори
екземплярів (для TLAS). Драйвер опрацьовує всі внутрішні деталі формату BVH,
сортування за Мортоном і SAH — застосунок лише надає
геометрію та екземпляри.
Оновлення проти перебудови
BLAS можна оновити (переобчислити без структурних змін) значно швидше, ніж перебудувати з нуля. Оновлення коректне, коли вершини зміщуються незначно (анімація), але якість погіршується зі зростанням руху. Евристика: використовувати оновлення, якщо зміщення вершин < 10% від розміру примітива; інакше перебудовувати. Переобчислення на RTX 4090: ~0,5 мс для 1М трикутників; перебудова: ~5 мс.
8. BVH для виявлення зіткнень
Ігрові рушії використовують дерева AABB (варіант BVH) для широкофазного виявлення зіткнень — швидкого пошуку пар обʼєктів, які можуть зіштовхуватися, перед запуском дорогих вузькофазних геометричних перевірок (GJK, EPA).
Динамічна BVH (DBVH)
В ігровій фізиці обʼєкти рухаються щокадру. Перебудовувати повну BVH щокадру марнотратно — натомість динамічна BVH використовує «товсті» AABB (розширені «полем» у кожному напрямку) і ліниве переобчислення:
-
Кожен рухомий обʼєкт розширює свій AABB на пропорційне до швидкості
поле:
fat_aabb = aabb.expand(v × Δt × 2) - Дерево перебудовується для обʼєкта лише тоді, коли його AABB виходить за межі збереженого товстого AABB.
- Перебудова використовує обертання дерева (подібні до обертань AVL) для підтримання збалансованості.
Bullet Physics і Box2D використовують динамічне дерево AABB (підтримуване інкрементно) для широкої фази. Вартість оновлення на один переміщений обʼєкт — O(log N) амортизовано, що значно краще за повні перебудови.
Вибір обмежувальних обʼємів, окрім AABB
- Сфери: найпростіша перевірка перетину, але погано підходять для тонких трикутників. Використовувалися в ранніх ігрових рушіях і для динамічних обʼєктів.
- OBB (орієнтована обмежувальна коробка): найтісніше прилягання, 16 перевірок розділювальних осей для OBB–OBB. Краща якість, але у 5–10× повільніша перевірка перетину.
- k-DOP (дискретний орієнтований політоп): опуклий політоп із k/2 парами паралельних граней у фіксованих орієнтаціях. 18-DOP (k=18) дає чудове прилягання для більшості моделей лише за помірної додаткової вартості перевірки. Використовується в системі зіткнень Unreal Engine.