Як оптимізувати гру з допомогою цих атласів

Як всім відомо, життя розробника мобільних ігор непроста. Він повинен знайти свій шлях на дуже вузькій доріжці. З одного боку — вимоги гейм-дизайнерів, впевнено прямують до нескінченності. Більше функціоналу, більше красивою графіки, більше ефектів, більше анімацій, більше звуків. А з іншого боку — обмежені ресурси мобільного пристрою. І раніше всього, як правило, закінчується оперативна пам'ять.

Наприклад, iPad 2 — всього у ньому 512 Мб RAM. Однак додатком доступно тільки приблизно 275 Мб. Коли займана додатком пам'ять буде наближатися до цієї межі, операційна система надішле так зване «Memory warning» — м'яко, але наполегливо запропонує звільнити пам'ять. І якщо ліміт все ж буде перевищено, операційна система зупинить додаток. Користувач буде думати, що ваша гра впала і побіжить писати гнівного листа в саппорт.



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

Текстури і атласи
Як всім знову ж відомо, малювати текстури безпосередньо з вихідних файлів — погана ідея. Замість цього їх упаковують в текстурні атласи.

Текстурний атлас — це велике зображення, отримане склеюванням маленьких текстур. Тим самим економиться пам'ять і, що ще більш важливо, зменшується кількість батчей при відображенні. Є два відео, наочно демонструють чому атлас — це добре, а окремі текстура — це погано: раз і два. Таким чином, завдання упаковки текстурного атласу зазвичай зводиться до наступного: взяти прямокутники різних розмірів (вихідні текстури) і максимально щільно укласти їх в один великий прямокутник, або, частіше, квадрат. Ми не на олімпіаді і ідеальна упаковка нам не потрібна, а написати алгоритм, щільно упаковывающий текстури за розумний час, зовсім не складно. Ми досить давно користуємося таким алгоритмом, який генерує нам текстурні атласи. Приблизно такі:



Як легко помітити, в ньому досить багато вільного місця і прямо-таки хочеться спакувати текстури щільніше. Причина неефективної упаковки в тому, що багато текстури містять досить великі прозорі області. Тому одного разу ми вирішили спробувати відмовитися від звичайної прямокутної упаковки і зробити те, що в результаті отримало назву «полігональний атлас». Для цього потрібно вирішити 2 завдання:

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

2. Утворені фігури складної форми (ті самі полігони) треба якомога щільніше упакувати в атлас. Тут був використаний один з варіантів алгоритму бджолиного рою, який, проводячи численні спроби з використанням генератора випадкових чисел, намагався знайти досить щільний варіант упаковки. Тут потрібен компроміс іншого роду — між якістю упаковки та що витрачається на упаковку часом.

Пройшовши досить тривалий шлях і зробивши спробу упаковки тестового атласу приблизно 100 000 раз, ми нарешті добилися результату:



Як видно, упаковка дійсно набагато щільніше. Залежно від текстур, виграш може становити до 1/4 їх площі. Можна також сформувати текстовий варіант атласу, в якому видно розбивку текстур на трикутники.



Крім самого атласу, генерується його опис, в якому вказані імена вихідних текстур і координати отриманих трикутників.

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

Є ще одна проблема, з якою ми зіткнулися при переході на нові атласи. Часто потрібно намалювати не текстуру цілком, а лише її частину. Наприклад, якщо треба зробити поступове поява об'єкта на екрані або який-небудь індикатор прогресу. При використанні звичайних атласів задача легко вирішується за допомогою корекції uv-координат. У разі ж полігональних атласів все стає складніше. Подивимося на прикладі. Синім на малюнку виділено частину текстури, яку треба намалювати:


Буде потрібно:

  • Виключити з відтворення трикутники, які не потрапляють в рисуемую частина текстури
  • Знайти перетину нової кордону текстури з вихідними трикутниками. Результат може вже не бути трикутником, і може знадобитися розділити його на 2 нових трикутника.
Рішення такої задачі дасть можливість непогано повправлятися в шкільному курсі геометрії. Крім того, такий алгоритм може працювати не дуже швидко.

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

Трохи технічних подробиць. Час пакування полігонального атласу сильно залежить від налаштування якості упаковки. Оскільки при будь-якій зміні набору текстур атласи треба перезбирати, це припадає робити досить часто. Тому зазвичай у нас включений режим «мінімальна щільність, максимальна швидкість». В такому режимі 8 атласів розміром 2048x2048 пакуються приблизно 5 хвилин. У загальних рисах процес упаковки виглядає так:

  • текстури розбиваються на трикутники;
  • текстури сортуються по спаданню висоти;
  • попередня упаковка текстур. Вибираються всі текстури по порядку і для кожної шукається вільне місце в атласі;
  • виробляється кілька спроб більш щільною перепакування текстур. Кількість спроб залежить від налаштувань якості;
  • якщо вдалося поліпшити первинну упаковку, то в атлас додаються додаткові текстури.
Як правило, вже попередня упаковка є досить щільною. При спробі підвищення якості, час пакування виростає дуже сильно — до декількох годин на 1 атлас — а виграш може становити 2-3 додатково упаковані текстури.

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

class Drawable {
virtual int Width() const;
virtual int Height() const;
віртуальний bool HitTest(int x, int y) const;
virtual void Draw(SpriteBatch* batch, const FPoint& position);
}

У неї вже були функції отримання розміру, перевірки непрозорості пікселя для обробки кліків і відтворення. Потрібно тільки створити ще один клас PolygonalSprite, який правильно реалізовував ці функції з урахуванням розбивки текстури на трикутники.

Також, щоб клієнтський код міг отримати список трикутників текстури і провести з ними якісь перетворення, інтерфейс була додана функція

virtual void GetGeometry(std::vector<QuadVert>& geometry) const;

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

Зараз полігональні атласи використовуються у багатьох наших проектах і можна вважати, що вони пройшли випробування часом. Наприклад, у нашому наступному free-to-play проекті Gardenscapes, світовий реліз якого відбудеться зовсім скоро, основна частина ігрового процесу відбувається в гравцеві належить саду.

Для нього намальовано просто величезна кількість різноманітних текстур, деякі з них використані в цій статті як приклади. Зараз всі ці текстури поміщаються у 8 полігональних атласів 2048x2048. А от якщо б ми пакували атласи звичайним способом, то їх вийшло б вже 11. Таким чином, ми отримуємо економію оперативної пам'яті, в залежності від застосовуваного графічного формату, від 6 до 48 МБ. А гейм-дизайнери можуть запропонувати на четверь гарненьких текстур більше!

Про автора: Сергій Шестаков, технічний директор компанії Playrix.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.