Алгоритм упаковки текстур


52

Що таке хороший алгоритм упаковки текстур? З технічної точки зору упаковка у сміттєві контейнери не є складною , тому евристика - це те, про що я дійсно хочу.


Я припускав, що ви використовуєте це для оптимізації карти uv, але мені цікаво, що це за програма.
Джонатан Фішофф

ftgles - це бібліотека, яка використовує opengl та freetype для візуалізації шрифтів. Однак кожен гліф зберігається у власній текстурі. Я хотів би упакувати їх в одну текстуру.
deft_code

Відповіді:


58

Я провів кілька місяців на одній роботі, придумавши кращий алгоритм упаковки текстур.

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

Вам або потрібно жорстко кодувати ширину або придумати для цього іншу евристику. Намагаючись зберегти квадратність, наш алгоритм розпочнеться з 128, а потім збільшиться на 128, поки не з’явиться результат, який був би не глибшим, ніж широким.

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

Так. 3%.

І після того, як ми провели нашу процедуру стиснення над нею, вона фактично вийшла більшою (що я досі не можу пояснити), тому ми викинули всю річ і повернулися до старого алгоритму.

Сортуйте елементи, варення за текстурою у порядку сканування. Там є ваш алгоритм. Це легко кодувати, швидко працювати, і ви не отримаєте набагато кращого без дивовижної кількості роботи. Ця робота просто не варта, якщо ваша компанія не має принаймні 50 людей і, мабуть, більше.

alt текст

І як бічне зауваження, я щойно реалізував цей алгоритм (фіксована ширина 512 пікселів) для абсолютно буквально того самого додатка, який ви робите (не ftgles, а оперантизовані вільні гліфи.) Ось результат. Це виглядає розмито, оскільки моє використовує алгоритм візуалізації тексту на основі Вальве , який також враховує додатковий простір між гліфами. Очевидно, що порожнього місця залишилось не так багато, і це добре справляється з набиванням речей на відкриті місця.

Весь код для цього має BSD-ліцензію та доступний у github .


Я подивився на вашу текстуру і подумав собі: "Я впевнений, що наш пакувальник текстур робить трохи краще за це". А потім я пішов і подивився на це, і зрозумів, що зламав його деякий час тому і не помітив (бо як тільки він працює, хто дивиться на вихідні текстури?) ... Тож дякую за публікацію - не знайшов би помилка інакше :) (як тільки я виправив помилку, вона виглядає дуже схожою - можливо, відтінок краще, але складно точно сказати. "як добре", мабуть, найбезпечніший опис).
JasonD

@JasonD, я хотів би знати, що робить ваш алгоритм, якщо він отримує кращі результати :) Навіть якщо він отримує приблизно рівномірний вихід по-іншому.
ZorbaTHut

1
Дякуємо за опис algo + допущена помилка + вихідний код. Чудовий пост.
Calvin1602

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

1
Про те , як знайти останню версію упаковки коди ZorbaTHut ( в font_baker.cpp), ви можете знайти тут: github.com/zorbathut/glorp/blob / ...
MEMS

20

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

Цитувати зі сторінки 52:

Алгоритм, званий дотиком периметра (TPRF), починається з сортування елементів за не збільшуючою площею (розрив зв'язків за збільшенням значень min {wj, hj}) та горизонтального їх орієнтування. Потім обчислюється нижня межа L на оптимальному значенні рішення, і L порожніх бункерів ініціалізується. (Безперервна нижня межа L0, визначена в попередньому розділі, очевидно, справедлива і для 2BP | R | F; кращі межі запропоновані Dell'Amico, Martello та Vigo [56].) Алгоритм пакує по одному елементу за один раз у існуючому контейнері або шляхом ініціалізації нового. Перший товар, упакований у відро, завжди розміщується в нижньому лівому куті. Кожен наступний предмет упаковується у так званому нормальному положенні (див. Крісто і Вітлок [41]), тобто
Вибір контейнера та положення упаковки проводиться шляхом оцінки балів, визначених як відсоток периметра товару, який торкається контейнера та інших предметів, які вже упаковані. Ця стратегія надає перевагу шаблонам, коли упаковані товари не «захоплюють» невеликі ділянки, які можуть бути важко використати для подальших місць розташування. Для кожної позиції упаковки кандидата оцінка оцінюється двічі, для двох орієнтацій на предмет (якщо обидва можливі), і вибирається найвище значення. Зв'язки балів розбиваються, вибираючи контейнер із максимальною упакованою площею. Загальний алгоритм такий.

touching_perimeter:
  sort the items by nonincreaseing w,h values, and horizontally orient them;
  comment: Phase 1;
  compute a lower bound L on the optimal solution value, and open L empty bins;
  comment: Phase 2;
  for j := 1 to n do
     score := 0;
     for each normal packing position in an open bin do
        let score1 and score2 be scores with tow orientations;
        score := max{score,score1,score2};
     end for;
     if score > 0 then
        pack item j in the bin, position and orientation corresponding to score;
     else
        open a new bin and horizontally pack item j into i;
     end if;
  end for;
end;

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


Цей алгоритм передбачає, що текстури мають прямокутну форму, чи правильно це?
користувач1767754

17

Якщо когось все ще цікавить, я повністю переписав бібліотеку rectpack2D, щоб вона була більш ефективною.

Це працює, зберігаючи std::vectorпорожні місця в атласі, починаючи з деякого початкового максимального розміру (як правило, максимально дозволеного розміру текстури для конкретного GPU), розділяючи перший життєздатний порожній простір і зберігаючи розбиття назад до вектора.

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

Процедура докладно описана в README .

Бібліотека знаходиться в MIT, тому я радий за вас, якщо ви вважаєте її корисною!

Приклад результатів:

Тести проводилися на процесорі Intel (R) Core (TM) i7-4770K процесора при 3,50 ГГц. Двійковий файл був побудований з кланетом 6.0.0, використовуючи перемикач -03.

Довільна гра-спрайт + японські гліфи: 3264 теми.

Час виконання: 4 мілісекунди
Витрачені пікселі: 15538 (0,31% - еквівалент площі 125 x 125)

Вихід (2116 х 2382):

3

За кольором:
(чорний - марна кількість місця)

4

Японські гліфи + деякі справки GUI: 3122 предметів.

Час виконання: 3,5 - 7 мс
Витрачені пікселі: 9288 (1,23% - еквівалент площі 96 х 96)

Вихід (866 x 871):

5

За кольором:
(чорний - марна кількість місця)

6


2
Я завантажив ваш код. Просто читаючи визначення структури: ЩО ЦЕ МОНСТРОСІТ ?! Це схоже на код гольфу.
акалтар

3
Все-таки це працює, і це допомогло, тож спасибі. Я не хотів бути грубим.
акалтар

Не впевнений, чому я пропустив цю відповідь, оскільки це навіть швидший і
корисніший пакувач,

@akaltar Я можу собі це уявити, я все-таки вивчав мову :)
Патрик Чачурський

Досить простий підхід, який швидко реалізується і дає хороші результати, дякую :)
FlintZA

5

Хороший евристичний алгоритм , можна знайти тут . Коли я нещодавно намагався щось подібне, я знайшов це посилання як основний вихідний пункт для більшості реалізованих нами реалізацій.

Особливо добре працює або з безліччю предметів правильної форми, подібного розміру, або з гарною сумішшю невеликих і менших великих зображень. Найкраща порада для досягнення хороших результатів - пам’ятайте, що сортуйте дані за розміром зображення, а потім упакуйте від найбільших до найменших, оскільки менші зображення запакуватимуться у просторі навколо великих зображень. Те, як ви це сортуєте, залежить від ваших цілей. Я використовував периметр, а не область як наближення 1-го порядку, оскільки я вважав, що високі + тонкі / короткі + широкі зображення (які мали б низьку площу) насправді дуже важко розмістити згодом у пачці, тому, використовуючи периметр, ви натискаєте ці дивні форми на передній частині замовлення.

Ось зразкова візуалізація виводу для мого пакера на випадковий набір зображень із мого каталогу дамп зображень веб-сайту :). Вихід пакета

Цифри в квадратах - це ідентифікатори блоків, що містять дерево, тому дають уявлення про порядок вставок. Перший - це ідентифікатор "3", оскільки це перший вузол листя (лише листя містять зображення) і, отже, має 2 батьків).

        Root[0]
       /      \
   Child[1]  Child[2]
      |
    Leaf[3]

3

Особисто я просто використовую жадібну систему найбільшого блоку, яка відповідає першим. Це не оптимально, але це робить трюк добре.

Зауважте, що якщо у вас є достатня кількість блоків текстур, ви можете вичерпно шукати найкраще замовлення, навіть якщо сама проблема є NP.


3

Щось я використав, що добре працює навіть для нерегулярних УФ-карт, - це перетворити УФ-патч в маску растрової карти та підтримувати маску для самої текстури, шукаючи перше положення, до якого укладається УФ-патч. Я замовляю блоки згідно з простими евристичними (висота, ширина, розмір, що завгодно), і я дозволяю обертанням блоків мінімізувати або максимізувати обрану евристику. Це дає керований простір пошуку для грубої сили.

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

За допомогою цієї схеми ви отримаєте невеликі ультрафіолетові острівці, упаковані в прогалини, зроблені великими, і навіть у отвори, залишені в самих одних УФ-патчах.


1

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

Цитується з нашого блогу:

"Хоча є численні пакувальники, які можна знайти в Інтернеті, наші труднощі полягали в пошуку будь-якого, який міг би обробляти велику кількість зображень у кількох каталогах. Таким чином, народився наш власний пакувальник атласу!

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

Ви можете легко змінити розмір атласу (рядок 65), формат зображень, які ви хочете упакувати (рядок 67), каталог завантажень (рядок 10) та каталог збереження (рядок 13) досить легко, не маючи досвіду роботи в Python. Як невелике застереження, це було збито за кілька днів, щоб спеціально працювати з нашим двигуном. Я закликаю вас вимагати функції, коментувати власні варіанти та повідомляти про будь-які помилки, але будь-які зміни в сценарії відбудуться у вільний час ".

Ви можете ознайомитися з повним вихідним кодом тут: http://www.retroaffect.com/blog/159/Image_Atlas_Packer/#b


1

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

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

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

Ідіть просто, і переходите до чогось, де ваші зусилля будуть краще винагороджені


0

Якщо це спеціально для текстур шрифту, то, ймовірно, ви робите щось неоптимальне, але приємне та просте:

Сортування символів за висотою, найвищий перший

Почніть з 0,0 Помістіть перший символ у поточних координатах, просуньте X, поставте наступний, повторіть, поки ми не зможемо помістити інший

Скиньте X до 0, просуньте Y вниз на висоту найвищого символу в рядку та заповніть інший рядок

Повторюйте, поки ми не залишимо символів або не зможемо помістити інший рядок.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.