Как ускорить отрисовку спрайтов без отказа от алгоритма художника — Game Design Radar
← Все посты

Как ускорить отрисовку спрайтов без отказа от алгоритма художника

16.12.2025
Как ускорить отрисовку спрайтов без отказа от алгоритма художника

Автор разбирает проблему оптимизации отрисовки 2D‑игры со спрайтами и мягкими (fuzzy) краями, где нельзя полагаться на z‑buffer, а нужен классический painter’s algorithm — рисование объектов сзади наперёд.

Исходная постановка

В игре порядка 500 сущностей. Каждая сущность — спрайт с двумя дополнительными эффектами: «подсветка снизу» (underglow) и «подсветка сверху» (overglow). Для корректного наложения эффектов (без «просвечивания» огней через другие объекты) каждый объект рисуется в три прохода: underglow → спрайт → overglow. Наивная реализация даёт 3 draw call на объект, то есть 1500 draw call только на базовый слой.

Проблема усугубляется тем, что поверх могут быть дополнительные слои и эффекты, ещё увеличивающие количество draw call.

Использование сетки мира

Мир разбит на сетку. Объекты, как правило, находятся в пределах одной ячейки и не «прыгают» далеко (из верхнего левого в нижний правый угол). Это позволяет делать допущения для оптимизации.

Идея: батчить отрисовку по ячейкам и типу слоя. Вместо того чтобы рисовать каждый объект полностью по очереди, можно, например, сначала нарисовать все underglow для «первого» объекта в каждой ячейке одним draw call, затем все спрайты, затем все overglow, а потом повторить цикл для «второго» объекта в каждой ячейке (второй Z‑слой внутри ячейки).

При 8 ячейках и 2 объектах в каждой, наивный метод даёт 48 draw call (8 ячеек × 2 объекта × 3 слоя), а батчинг по слоям и ячейкам — всего 6 (3 слоя × 2 «прохода» по объектам). Выигрыш существенный.

Ограничения и сложности

Реальный случай сложнее:

  • В одной ячейке может быть от 1 до 16 сущностей.
  • Некоторые объекты частично заходят в соседние ячейки, что создаёт риск неправильного порядка отрисовки (артефакты и «z‑fight» на уровне painter’s algorithm).
  • Нужно избежать тяжёлой сортировки каждый кадр.

Метаинформация и Z‑структура

Для каждого объекта автор использует два ключевых параметра:

  • Индекс ячейки сетки (grid square).
  • Z‑значение, попадающее в один из 16 Z‑диапазонов (bands) на весь мир.

Это позволяет гарантировать, что до 16 сущностей в ячейке могут быть корректно отрисованы в разных Z‑слоях (по сути, 16 отдельных draw call для них, если нужно).

Чтобы минимизировать сортировку в рантайме, список сущностей изначально формируется в уже отсортированном по Z виде. Пример структуры: A1, B1, C1, D1, E1, F1, G1, H1, A2, C2, F2, где чёрный индекс — Z‑уровень, а синий — ячейка сетки. Этот список становится «дефолтным» порядком painter’s algorithm.

Двухслойный рендер-пайплайн

Для генерализации решения автор предлагает двухуровневую архитектуру рендеринга:

  • Слой 1 (логический): игровой код просто эмитирует «сырые» draw call с метаданными (текстура, режим смешивания, параметры объекта и т.п.) в наивном порядке, без попытки оптимизации.
  • Слой 2 (оптимизатор): собирает все запросы слоя 1 за кадр, анализирует их, вычисляет bounding box каждого вызова, определяет пересечения и возможные батчи, и формирует оптимизированную последовательность draw call для DirectX с минимальным числом смен состояний и вызовов.

Далее оптимизированный пакет может отправляться в отдельный поток, который «стримит» команды в DirectX, пока основной поток подготавливает следующий кадр.

Плюс архитектуры — её универсальность: один и тот же механизм может обслуживать как статичные сущности, так и любые другие объекты и эффекты. Также можно оставить возможность отключать второй слой и рендерить «как есть» для сравнения производительности (например, по горячей клавише).

Выводы

  • Наивный painter’s algorithm с несколькими эффектами на спрайт быстро взрывает количество draw call.
  • Разбиение мира на сетку и батчинг по ячейкам и слоям даёт кратное снижение числа вызовов рендера.
  • Хранение сущностей в заранее отсортированном по Z списке уменьшает потребность в сортировке каждый кадр.
  • Двухслойный рендер-пайплайн (сырые команды → оптимизатор) позволяет централизованно решать задачи батчинга и порядка отрисовки.
  • Такой подход масштабируется и подходит для сложных 2D‑игр со спрайтами и мягкими краями, где z‑buffer использовать нельзя.
check_circle Факт-чекинг
Статья прошла проверку. Фактологических ошибок не выявили.