Налаштування рівнів деталізації (LOD) для графіки

Наша компанія з розробки відеоігор веде незалежні проекти, спільно з клієнтом створює ігри та надає додаткові операційні послуги. Досвід нашої команди дозволяє нам охопити всі ігрові платформи та розробити приголомшливий продукт, що відповідає баченню клієнта та перевагам гравців.

Від імерсивних застосунків до ігрових світів і 3D-сцен

Наша виділена команда для VR/AR/MR-розробки, Unity-продакшну і 3D-моделювання та анімації — з власними кейсами і презентаціями.

Відвідати персоналізований сайт

Наші компетенції

Які етапи розробки гри?

Останні роботи

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    1421
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    954
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    577
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    637

Ми стикаємося з LOD-проблемами на кожному другому проєкті. Неправильно налаштований LOD Group в Unity дає зворотний ефект: LOD0 перемикається на LOD1 занадто рано, гравець бачить різкий pop, і це сприймається як баг. Або переходи налаштовані за Screen Relative Height без урахування реальної відстані — на ортографічній камері LOD взагалі не працює. Наше завдання — зробити LOD непомітним, але ефективним.

Суть у тому, що LOD — це система управління складністю сцени залежно від видимості об'єкта. Вона працює правильно лише коли враховані: тип камери, швидкість руху гравця, освітлення (відкидні тіні у LOD1 часто гірші, ніж у LOD0), і те, як рушій рахує відстань.

Як виникає LOD pop і що з ним робити?

LOD Pop — головна візуальна проблема. Відбувається, коли геометрія та/або текстури між рівнями відрізняються надто сильно. Класичний випадок: художник зробив LOD1 з на 60% меншою кількістю полігонів, але UV-розгортка поїхала, і нормал-карта не компенсує втрату форми. Перехід з 10 метрів — помітний неозброєним оком. Вирішується через правильний LOD-генератор (ми використовуємо Simplygon або Unity LOD Generator) зі збереженням UV-seam'ів і перевіркою normal projection.

Тіні не слідують за LOD-переходами. В Unity Shadow Caster Culling працює незалежно від LOD Group. Якщо у вас LOD2 — плоский billboard з 2 полігонами, а тінь все ще рахується від LOD0 mesh (тому що Force Shadow Casting = On), ви платите за рендер тіней від повної геометрії об'єкта, якого візуально немає. Це легко пропустити — Frame Debugger показує Shadow Pass з повним Draw Call-бюджетом. Ми гарантуємо усунення таких прихованих витрат.

HLOD (Hierarchical LOD) не налаштований для великих сцен. В open-world проєктах стандартний LOD Group не працює на відстані понад 500 метрів — об'єкти просто culled, але Scene не вивантажується. Unity HLOD (через HLOD Creator пакет) об'єднує далекі об'єкти в один mesh автоматично. Без цього у вас може бути 3000 Draw Calls від дерев на горизонті. Оцінимо ваш проєкт за 1–2 дні.

Чому LOD-політика повинна відрізнятися для різних типів об'єктів?

Під кожну категорію об'єктів (персонажі, будівлі, пропси, рослинність) встановлюємо окрему LOD-політику. Приклад з практики — мобільна RPG, відкритий світ 2×2 км. Дерева: LOD0 (500 полігонів) до 15 метрів, LOD1 (80 полігонів) до 50 метрів, LOD2 (billboard cross) до 150 метрів, Culled за 150 метрів. Будівлі: LOD0 до 30 метрів, LOD1 до 80 метрів, LOD2 до 200 метрів. Таке налаштування дало зниження Draw Calls з 680 до 210 в центрі міста.

Для Unreal Engine працюємо з Nanite там, де це застосовно — на статичних мешах з високим poly count. Але Nanite не замінює LOD для рухомих об'єктів і не працює з напівпрозорими матеріалами. HLOD в UE5 налаштовуємо через World Partition HLOD Layer. Згідно з документацією Unreal Engine, Nanite best practice вимагає вимикати LOD для Nanite-мешів.

Cross-фейдинг замість hard pop. Unity LOD Group підтримує Cross Fade mode — дітерінговий перехід між рівнями. Працює через Dither Fade шейдерне ключове слово. На мобільних платформах дітерінг дешевший, ніж здається — Adreno добре його паралелить. Вмикаємо для великих об'єктів на передньому плані, залишаємо instant-перехід для дрібного пропса вдалині.

Рослинність — окрема історія. SpeedTree LOD інтегрується в Unity через окремий pipeline. Головна пастка — billboard LOD SpeedTree рендериться через окремий BatchRendererGroup, і його потрібно профілювати окремо від основного Draw Call лічильника. Бачили проєкти, де 40% GPU time йшло на 2000 billboard-дерев, які здавалися «вже оптимізованими». Ми навчаємо команду таким нюансам в рамках підтримки.

Як налаштувати LOD за 5 кроків?

  1. Профілювання GPU: відкрийте Profiler → GPU Usage і Frame Debugger з фільтром по Draw Calls. Визначте, скільки об'єктів рендериться з надлишковою деталізацією.
  2. Аналіз Overdraw: в Scene View увімкніть Overdraw mode — знайдіть об'єкти з високим overdraw без LOD.
  3. Класифікація: розділіть об'єкти на категорії та для кожної встановіть кількість рівнів і порогові дистанції за таблицею нижче.
  4. Генерація LOD: використовуйте Simplygon або вбудований генератор. Для критичних мешів — ручне доопрацювання.
  5. Тестування на цільовій платформі: перевірте відсутність pop і приріст FPS.
Категорія об'єктів Рівні Пороги (м) Тіні
Персонажі LOD0, LOD1, LOD2 0–10, 10–25, 25–50 On до LOD1
Будівлі LOD0, LOD1, LOD2 0–30, 30–80, 80–200 On до LOD0
Пропси (дрібні) LOD0, LOD1 + Culled 0–5, 5–15 Off на LOD1
Рослинність (дерева) LOD0, LOD1, Billboard, Culled 0–15, 15–50, 50–150 Off на LOD2

Процес роботи над LOD

Аудит починається з Profiler → GPU Usage і Frame Debugger. Дивимося, скільки об'єктів рендериться за межами видимої деталізації. Після аудиту готуємо LOD-специфікацію: таблиця з категоріями об'єктів, кількістю рівнів, пороговими дистанціями, політикою тіней для кожного рівня. Це артефакт, який погоджуємо з командою художників.

Реалізація: або налаштування існуючих LOD Group, або створення LOD-асетів (якщо їх немає), або автоматична генерація з подальшою ручною правкою критичних об'єктів.

Після аудиту ми виявили 450 об'єктів без LOD. Створили LOD-асети з 2–3 рівнями, застосували cross-fade для будівель і instant для пропса. Результат: FPS зріс з 28 до 55 на Samsung Galaxy S10. Економія Draw Calls склала 40%.

Що входить в роботу

Deliverable Опис
LOD-специфікація Документ з порогами, рівнями та налаштуваннями тіней для кожної категорії
Налаштовані LOD Group Всі асети з правильною конфігурацією
Оптимізовані меші Згенеровані або вручну доопрацьовані LOD-копії
Рекомендації щодо баджену Інструкції для художників з підготовки вихідників
Підтримка при впровадженні Консультації на етапі інтеграції та тестування (1 місяць)
Масштаб задачі Орієнтовні терміни
Аудит LOD-налаштувань + звіт 1–3 дні
Налаштування LOD для однієї сцени (до 200 типів об'єктів) 1–2 тижні
Розробка LOD-політики + реалізація для open-world 3–6 тижнів
Інтеграція HLOD/Nanite в існуючий проєкт 2–4 тижні

Зв'яжіться з нами для консультації — ми підготуємо пропозицію під ваш проєкт. Наші інженери мають досвід налаштування LOD для AAA-проєктів та мобільних ігор. Пишіть, оцінимо задачу за 1–2 дні без зобов'язань.

Типові проблеми продуктивності ігор

Проект запускається на топових девайсах розробника без питань. На mid-range Android п'ятирічної давності — 20 fps і перегрів через 5 хвилин. На iPhone 11 — стабільні 60, але на iPhone XR — просідання у важких сценах. Ми стикаємося з цим кожен день. Оптимізація — не «потім», а архітектурне рішення з першого коміту. За 8 років роботи ми провели оптимізацію більш ніж для 50 ігор — від гіперказуалки до AAA на консолях. Гарантуємо: після нашого аудиту ви отримаєте не просто список проблем, а конкретний план з вимірними цілями та термінами. Замовте аудит продуктивності — оцінимо ваш проект за 3 дні і покажемо, як знизити draw calls на 40% без втрати якості.

Як профілювати продуктивність ігор?

Перш ніж оптимізувати — виміряти. Оптимізація без профілювання — вгадування.

Інструмент Призначення
Unity Profiler CPU/GPU час по системах, GC allocations, audio
Frame Debugger Інспекція кожного draw call у кадрі
Memory Profiler Знімок пам'яті, граф залежностей асетів
RenderDoc Глибокий аналіз GPU-стану, актуальний для PC/Console
Android GPU Inspector Профілювання GPU на реальному Android-пристрої
Xcode Instruments GPU + memory на iOS (Metal Performance HUD)
Snapdragon Profiler Qualcomm GPU — детальна статистика шейдерів

Профілюйте на цільовому залізі, а не в редакторі. Editor додає оверхед — цифри з Play Mode не репрезентативні. Розділяйте GPU та CPU bottleneck: на CPU багато draw calls або важка логіка, на GPU — складні шейдери або overdraw. Документація Unity: профілювання на пристрої — обов’язковий крок для мобільних ігор. Типовий час профілювання одного сценарію — 5–8 годин, включаючи збір метрик на 3–5 пристроях різних поколінь. Аналізуємо 10–15 ключових ігрових сценаріїв за 3 робочих дні.

Як зменшити кількість draw calls?

Draw call — команда CPU до GPU «намалюй це». Кожен виклик має overhead незалежно від складності геометрії. На мобільних 200–300 draw calls за кадр — межа. Мета: мінімізувати їх кількість, об’єднуючи геометрію з однаковим матеріалом.

Static Batching об’єднує нерухомі меші при збірці. Вимога: прапорець Static на об’єкті та однаковий матеріал. Ефективний для статичного оточення, але збільшує споживання пам’яті — об’єднаний меш зберігається окремо. Static Batching може знизити draw calls в 3 рази, але збільшує пам’ять на 20% порівняно з іншими методами. У сценах з тисячами статичних об’єктів слідкуйте за пам’яттю через Memory Profiler.

Dynamic Batching об’єднує меші в рантаймі з жорсткими обмеженнями: менше 900 вертексних атрибутів на меш, однаковий матеріал та масштаб. На практиці ефективний лише для дрібних об’єктів (партикли, UI). В URP за замовчуванням вимкнений — його витіснив SRP Batcher.

SRP Batcher — не класичний батчінг, а оптимізація CPU-overhead при підготовці draw calls. Замість того щоб кожен кадр заново завантажувати uniform-дані шейдера, SRP Batcher кешує їх у GPU-пам’яті та оновлює лише ті, що змінилися. Draw calls залишаються колишніми за кількістю, але кожен займає менше часу CPU — SRP Batcher дає приріст в 2–3 рази по CPU-часу рендеру. Вимога: шейдер має бути сумісний з SRP Batcher (декларувати per-object властивості в UnityPerDraw CBUFFER). Стандартні URP Lit/Unlit шейдери сумісні. Кастомні — перевіряємо в Inspector матеріалу: SRP Batcher compatible: Yes/No. Для ввімкнення переконайтеся, що в URP Asset опція SRP Batcher активна, а для кастомних шейдерів використовуйте макрос UNITY_INSTANCING_BUFFER і декларуйте per-object властивості в блоці CBUFFER_START(UnityPerDraw). Після ввімкнення в Profiler має знизитися час RenderLoop.Draw на CPU.

GPU Instancing — для множини копій одного меша з одним матеріалом (дерева, трава, NPC). Відправляє один draw call з масивом per-instance даних. Вмикається на матеріалі: Enable GPU Instancing. Обмеження: всі інстанси в одному batch повинні мати однаковий матеріал і меш. Graphics.DrawMeshInstanced / Graphics.DrawMeshInstancedIndirect — для процедурного рендерингу без GameObject overhead.

Вибір методу залежить від сценарію: Static Batching підходить для статичного оточення, SRP Batcher виграє в проектах з множиною унікальних матеріалів, GPU Instancing незамінний для масових об’єктів (ліс, натовп), Dynamic Batching — лише для дрібних та рідкісних випадків. На практиці ми комбінуємо все, починаючи з профілювання.

Метод Тип об'єктів Вплив на CPU Вплив на GPU Споживання RAM
Static Batching Статичні Помірне зниження Без змін Збільшується
Dynamic Batching Дрібні (≤900 вертексів) Зниження Без змін Без змін
SRP Batcher Будь-які (сумісні шейдери) Значне зниження Без змін Без змін
GPU Instancing Копії одного меша Мінімальне Значне зниження Незначно

Детальніше про технологію — на Wikipedia.

Чому оптимізація пам'яті критична для мобільних ігор?

Мобільні платформи — жорсткі обмеження по RAM. iOS вбиває додаток при перевищенні пам’яті без попередження (memory pressure kill). Android — аналогічно, але з onLowMemory callback. Цільові бюджети: iOS — <1 GB для сучасних пристроїв, <512 MB для підтримки iPhone 8/X; Android — <800 MB для широкої сумісності (ОС займає 400–600 MB). Типова економія після нашої оптимізації — 30–50% оперативної пам’яті при збереженні якості. Середня економія бюджету на етапі оптимізації — від $3,000 до $8,000 залежно від складності проекту, що підтверджується нашими кейсами.

Addressables та Asset Bundles: як не перевищити бюджет

Завантажувати все одразу при старті — неприйнятно для великих проектів. Addressables (надбудова над Asset Bundles) — система адресованого асинхронного завантаження асетів. Явне вивантаження: Addressables.ReleaseInstance / Addressables.Release. Addressables не вивантажують асети автоматично при знищенні об’єкта. Типова помилка: Addressables.InstantiateAsync у циклі без Release — пам’ять росте до крашу. Reference counting: асет вивантажується тільки коли всі його handles звільнені. Архітектурний патерн: сервіс/менеджер тримає handle завантаженого асета, звільняє при переході між сценами.

Groups та Bundle Strategy: групуємо асети за логікою завантаження. Наприклад, всі асети одного рівня — в один bundle, шарені (UI, шрифти) — в окрему групу з Prevent Updates. Така стратегія економить до 30% пам’яті.

Texture Memory: звідки береться 70% займаного об'єму

Текстури — основний споживач пам’яті. Аналіз через Memory Profiler: вкладка All Of MemoryTexture2D — одразу видно список найважчих текстур. На практиці ми знаходимо текстури з завищеним Max Size (4096 для мобільної іконки — типова помилка). Заходи: Mipmap для 3D-текстур (ввімкнути), для UI (вимкнути); Streaming Mipmaps для open world — завантажує mip-рівні по мірі наближення камери. Красовська проблема: текстури, на які посилаються невикористовувані Materials, залишаються в пам’яті — Memory Profiler покаже reference chain. Видаляйте зайві матеріали. Після заміни всіх текстур формату RGBA32 на ASTC 6×6 на Android економія досягає 60% без втрати якості.

GC Allocations: як усунути фризи в Hot Path

C# garbage collector в Unity — stop-the-world. Якщо за кадр алоційовано багато heap-пам’яті, GC-пауза викликає видимий фриз. Ціль: нульові алокації в hot path (Update, FixedUpdate, рендер). Типові джерела: string конкатенація в Update (замінюємо на StringBuilder), LINQ в hot path (ручні цикли з предалоційованими списками), GetComponent<T>() кожен кадр (кешуємо в Awake/Start), boxing value types при передачі в object параметри. Після профілювання за допомогою Unity Profiler ми знижуємо алокації в hot path на 95% — фризи зникають.

Як відсікти зайві 40% draw calls? LOD та Culling

LOD Group — перемикання на спрощену геометрію при віддаленні об’єкта від камери. Стандарт для 3D оточення: LOD0 (100%), LOD1 (30–50% трикутників), LOD2 (10–15%), Culled. Для мобільних поріг Culled ставимо агресивніше — менше малюємо за кадр. LOD знижує draw calls на 40–60% залежно від кількості об’єктів.

Occlusion Culling — Unity не рендерить об’єкти за стінами. Вимагає запечені occlusion дані. Для indoor сцен знижує draw calls на 20–40%.

Frustum Culling працює автоматично — об’єкти поза FOV камери не рендеряться. Але draw call на перевірку все одно відбувається. Для сцен з тисячами об’єктів — кастомний spatial partitioning (Quadtree, Octree). В одному з проектів впровадження оклюзійного калінгу та LOD знизило загальну кількість draw calls з 2800 до 450 на Android.

Оптимізація VR: як утримати 72 FPS на Quest 3

VR — окремий клас задач. Фреймрейт 72/90 Hz не можна порушувати, інакше motion sickness. Додатково до стандартних методів: Single Pass Instanced Rendering — рендер для обох очей за один прохід (знижує draw calls в 2 рази); Fixed Foveated Rendering (Quest) — зниження роздільної здатності на периферії; Late Latching (Quest 3) — оновлення позиції контролера максимально пізно перед рендером; Dynamic Resolution в URP/HDRP — автоматичне зниження роздільної здатності рендера при просадці fps. Для Quest профілюємо через OVR Metrics Tool — показує CPU/GPU time прямо в гарнітурі. Після застосування цих методів частота кадрів на Quest 2 стабілізується на 72 FPS навіть у сценах з 1.5 млн полігонів.

Які етапи оптимізації та терміни?

Ми пропонуємо комплексну послугу «під ключ»:

  1. Профілювання на цільових пристроях — збір метрик (FPS, draw calls, пам’ять, GC). Аналізуємо 10–15 ключових сценаріїв за 3–5 робочих днів.
  2. Звіт з пріоритетами — які проблеми критичні, які можна відкласти. Пріоритети виставляємо на основі впливу на ігровий досвід.
  3. Впровадження оптимізацій — батчінг, LOD, Addressables, шейдери, occlusion culling. Середній цикл впровадження — 2–4 тижні.
  4. Повторне профілювання — замір покращень. Типовий приріст FPS — 30–60% на мобільних пристроях.
  5. Документація — рекомендації по підтримці та подальшій розробці.
  6. Навчання команди — як не допустити регресу. Проводимо воркшопи з профілювання та оптимізації.

Завдяки запобіганню переробок на пізніх етапах замовник отримує значну економію. Отримайте консультацію — ми безкоштовно оцінимо ваш проект і покажемо потенціал оптимізації. Звертайтеся, щоб дізнатися точні терміни та вартість для вашого стеку.