← Назад к вопросам

Как уменьшить количество вызовов отрисовки?

2.0 Middle🔥 121 комментариев
#Оптимизация#Рендеринг и графика

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличный, фундаментальный вопрос об оптимизации рендеринга (Rendering Optimization) в Unity. Уменьшение количества вызовов отрисовки, или Draw Calls, — одна из главных задач для достижения высокой частоты кадров, особенно на мобильных платформах и в сложных сценах.

Draw Call — это команда графическому API (OpenGL, Vulkan, Direct3D) на отрисовку меша (сетки) с определенным материалом. Каждый вызов несет накладные расходы, и их объединение — ключ к производительности. Вот стратегии по уменьшению их количества, от самых эффективных к более ситуативным.

1. Объединение статической геометрии (Static Batching)

Unity может автоматически объединять статические объекты в один большой меш в момент сборки, что радикально снижает количество Draw Calls.

  • Как: Пометить объект как Static в инспекторе (хотя бы Batching Static).
  • Условия: Объекты должны использовать один и тот же материал (или материалы из одного атласа) и не двигаться.
  • Особенность: Увеличивает потребление памяти (VRAM), так как создается объединенная геометрия.

2. Динамическое объединение (Dynamic Batching)

Unity автоматически объединяет небольшие движущиеся меши в один Draw Call во время выполнения.

  • Как: Включено в Player Settings -> Other Settings.
  • Жесткие ограничения:
    *   Меш должен иметь **менее 300 вершин**.
    *   Использовать только один **1 компонент Skinned Mesh Renderer**.
    *   Использовать один и тот же материал.
    *   Не использовать масштабирование с отрицательным значением.
  • Применение: Идеально для мелких подвижных объектов, например, монеток или пуль.

3. Использование атласов текстур (Texture Atlasing)

Разные материалы, даже на одном шейдере, вызывают отдельные Draw Calls. Атлас — одна большая текстура, содержащая множество мелких. Это позволяет разным мешам использовать один материал.

  • Как: Создать атлас в графическом редакторе (Photoshop, Substance) или использовать Sprite Atlas для 2D или UV-развертку для 3D.
  • Результат: Объекты с разными визуалами, но одним материалом, могут батчиться.

4. Объединение через скрипты (Mesh.CombineMeshes)

Для сложных сценариев можно программно объединять меши.

// Пример упрощенного объединения мешей
public void CombineMeshes(GameObject parentObject)
{
    MeshFilter[] meshFilters = parentObject.GetComponentsInChildren<MeshFilter>();
    CombineInstance[] combine = new CombineInstance[meshFilters.Length];

    for (int i = 0; i < meshFilters.Length; i++)
    {
        combine[i].mesh = meshFilters[i].sharedMesh;
        combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
        meshFilters[i].gameObject.SetActive(false);
    }

    MeshFilter combinedFilter = parentObject.GetComponent<MeshFilter>();
    if (combinedFilter == null)
        combinedFilter = parentObject.AddComponent<MeshFilter>();

    combinedFilter.mesh = new Mesh();
    combinedFilter.mesh.CombineMeshes(combine);
    parentObject.GetComponent<MeshRenderer>().material = /* ваш общий материал */;
    parentObject.SetActive(true);
}

5. Оптимизация материалов и шейдеров

  • Уменьшение разнообразия материалов: Чем меньше уникальных материалов, тем лучше.
  • Использование GPU Instancing: Позволяет за один Draw Call отрисовать множество одинаковых объектов с разными данными (позиция, цвет). Включите галочку Enable GPU Instancing в материале.
    // Пример отправки свойств для Instancing через MaterialPropertyBlock
    MaterialPropertyBlock props = new MaterialPropertyBlock();
    props.SetColor("_Color", Color.red);
    myRenderer.SetPropertyBlock(props); // Не разрывает батчинг!
    
  • Избегайте Material.Set... в рантайме на общих материалах, это создает копию материала (Material Instance) и ломает батчинг. Используйте MaterialPropertyBlock.

6. Работа с освещением и тенями (Realtime Lights & Shadows)

Каждая реальная (realtime) точка света, влияющая на объект, может разделять (split) его на дополнительные Draw Calls. Используйте Baked Lighting (запекание) для статического освещения и Light Probes для динамических объектов. Заменяйте реальные тени на baked-тени или проекционные текстуры (cookie), где возможно.

7. Оптимизация UI (Canvas)

Каждый элемент UI Canvas генерирует свой собственный набор вызовов. Огромная проблема для производительности.

  • Объединяйте Canvas: Разделяйте статический и динамический интерфейс по разным Canvas.
  • Следите за перерисовкой: Элементы, меняющие вертексные данные (например, Text, часто меняющий текст), вызывают перестройку всего Canvas (Rebuild), на котором находятся. Изолируйте такие элементы.

Дополнительные инструменты анализа

  • Окно Frame Debugger (Window -> Analysis -> Frame Debugger) — ваш главный инструмент. Оно показывает каждый Draw Call в текущем кадре, его причину и затраты.
  • Профайлер Profiler (Window -> Analysis -> Profiler), раздел Rendering — показывает общую статистику по Draw Calls, SetPass Calls, количеству треугольников.

Практический чек-лист действий:

  1. Анализ: Включите Frame Debugger и найдите кадр с пиковым количеством вызовов.
  2. Статика: Все, что не движется, помечайте как Static.
  3. Материалы: Сведите их к минимуму, используйте атласы и GPU Instancing.
  4. Динамика: Для мелких объектов убедитесь, что работает Dynamic Batching, или переходите на GPU Instancing.
  5. Освещение: Запекайте, используйте Light Probes, минимизируйте реальные источники света.
  6. UI: Оптимизируйте иерархию Canvas.

Помните, что оптимизация — это баланс. Иногда агрессивный статический батчинг может увеличить использование памяти, а GPU Instancing не подойдет для сильно различающихся объектов. Всегда тестируйте изменения в целевых условиях.