テクスチャパッキングアルゴリズム


52

優れたテクスチャパッキングアルゴリズムとは何ですか?技術的には、ビンパッキングNPハードなので、ヒューリスティックは私が本当に求めているものです。


私はあなたがUVマップを最適化するためにこれを使用していると仮定していましたが、私はアプリケーションが何であるか興味があります。
ジョナサンフィショフ

ftglesは、openglとfreetypeを使用してフォントをレンダリングするライブラリです。ただし、各グリフは独自のテクスチャに保存されます。それらを1つのテクスチャにまとめたいと思います。
-deft_code

回答:


58

より良いテクスチャパッキングアルゴリズムを思い付く1つの仕事に数ヶ月を費やしました。

私たちが始めたアルゴリズムは簡単でした。すべての入力項目を収集します。消費された総ピクセル数で並べ替えます。それらをスキャンラインの順序でテクスチャにレイアウトし、左上のピクセルから右上のピクセルまでをテストし、ラインを下に移動し、配置が成功するたびに左上のピクセルにリセットします。

幅をハードコードするか、このための別の発見的方法を考え出す必要があります。直角度を維持するために、アルゴリズムは128から始まり、幅よりも深くない結果になるまで128秒ずつ増加します。

それで、私たちはそのアルゴリズムを持っていて、それを改善することに決めました。気まぐれなヒューリスティックを試してみました-一緒に収まるオブジェクトを見つけようとして、必要なスペースパッキングプロパティのセットに対していくつかの重み付けを行い、回転と反転を行いました。すべての作業の後、文字通り3か月の作業の後、3%のスペースを節約しました。

うん。3%。

そして、圧縮ルーチンを実行した後、実際には大きくなりました(まだ説明できません)ので、すべてを捨てて古いアルゴリズムに戻りました。

アイテムを並べ替え、スキャンライン順にテクスチャにジャムします。アルゴリズムがあります。コーディングは簡単で、実行は高速です。驚くほどの量の作業がなければ、それほど良くなることはありません。あなたの会社が少なくとも 50人、そしておそらくそれ以上でなければ、その仕事は価値がありません。

代替テキスト

副次的な注意事項として、私はこのアルゴリズム(固定幅512ピクセル)を、文字通りまったく同じアプリケーション(ftglesではなく、openglでレンダリングされたfreetypeグリフ)に実装しました。結果は次のとおりです。私はValveの距離フィールドベースのテキストレンダリングアルゴリズムを使用しているため、ぼやけて見えます。これは、グリフ間の余分なスペースも考慮しています。明らかに、多くの空きスペースが残っているわけではなく、オープンスポットに物を詰め込むのに良い仕事をします。

このすべてのコードはBSDライセンスであり、githubで入手できます。


あなたのテクスチャを見て、「私たちのテクスチャパッカーはそれよりも少し良くなると確信しています」と思いました。そして、私はそれを調べて見たところ、しばらく前に壊して気づかなかったことに気付きました(動作した後、誰が出力テクスチャを見るのですか?)...投稿してくれてありがとう-見つかりませんでしたそれ以外の場合のバグ:)(バグを修正すると、非常に似ているように見えます-おそらく日陰の方が良いかもしれませんが、正確に言うのは難しいです。「良い」がおそらく最も安全な説明です)。
JasonD

@JasonD、より良い出力が得られるなら、あなたのアルゴリズムが何をするのか知りたい:)たとえそれがおおよそ同等の出力を異なる方法で得たとしても。
-ZorbaTHut

1
アルゴの説明+認められた失敗+ソースコードをありがとう。素晴らしい投稿。
Calvin1602

1
圧縮後にサイズが大きくなった理由は、おそらく圧縮アルゴリズムが原因です。圧縮は多くの場合、ハッシュとバイナリパターンの検出に依存するため、アルゴリズムが十分なパターンを識別できる場合、サイズの拡大を引き起こす可能性があるパターン全体が生成されます。これをテストしてファイルを何度も何度も圧縮し直すと、パターンが不足しているために最終的にはサイズが再び大きくなります。
ハンナ

1
ZorbaTHutのパッキングコード(font_baker.cpp)の最新バージョンの検索方法については、github.com
zorbathut

20

アンドレアロディの博士論文は、二次元ビンパッキングと割り当て問題のためのアルゴリズムと題されています。
論文は、この問題のより困難な形態のいくつかを取り上げています。幸いなことに、テクスチャパッキングは最も簡単なバージョンです。彼が見つけた最高のアルゴリズムはTouching Perimeterと呼ばれていました。

52ページから引用するには:

Touching Perimeter(TPRF)と呼ばれるアルゴリズムは、アイテムを非増加領域(非増加min {wj、hj}の値でタイを壊す)に従ってソートし、水平方向に配置することから始まります。次に、最適解値の下限Lが計算され、L個の空のビンが初期化されます。(前のセクションで定義された連続下限L0は、明らかに2BP | R | Fでも有効です。Dell'Amico、Martello、およびVigo [56]により、より良い境界が提案されています。)既存のビンに入れるか、新しいビンを初期化します。ビンに梱包された最初のアイテムは、常に左下隅に配置されます。後続の各アイテムは、いわゆる通常の位置に詰め込まれます(Christofides and Whitlock [41]を参照)。
ビンおよび梱包位置の選択は、スコアを評価することによって行われます。スコアは、ビンに既に接触しているアイテムの周囲およびすでに梱包されている他のアイテムの割合として定義されます。この戦略は、パックされたアイテムが小さな領域を「トラップ」しないパターンを支持します。これは、さらなる配置に使用するのが難しいかもしれません。候補の梱包位置ごとに、2つのアイテムの向き(両方が実行可能な場合)について、スコアが2回評価され、最高値が選択されます。最大の梱包面積を持つビンを選択することにより、スコアのタイが壊れます。全体的なアルゴリズムは次のとおりです。

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;

また、興味深いことに、この論文では、最適にパックされたテクスチャマップのサイズを決定するアルゴリズムについて説明しています。これは、1つの1024x1024アトラスにすべてのテクスチャを収めることが可能かどうかを判断するのに役立ちます。


このアルゴリズムは、テクスチャが長方形であることを前提としていますが、正しいですか?
user1767754

17

まだ興味がある人は、rectpack2Dライブラリを完全に書き直して、効率を改善しました。

これは、維持することによって動作しstd::vector、アトラス内の空きスペースのをいくつかの初期の最大サイズ(特にGPU上、通常、最大許容テクスチャサイズ)で始まる、分割最初の実行可能な空きスペースとバックベクトルへの分割を節約します。

以前のようにツリー全体を保持するのではなく、ベクターを使用するだけでパフォーマンスが飛躍的に向上しました。

手順はREADMEで詳しく説明されています

このライブラリはMITの管理下にあるので、便利だと思ったら嬉しいです!

結果の例:

テストは、Intel(R)Core(TM)i7-4770K CPU @ 3.50GHzで実施されました。バイナリは、-03スイッチを使用してclang 6.0.0で構築されました。

任意のゲームスプライト+日本のグリフ:合計3264件。

ランタイム:4ミリ秒
無駄なピクセル:15538(0.31%-125 x 125平方に相当)

出力(2116 x 2382):

3

色:(
黒は無駄なスペースです)

4

日本語のグリフ+ GUIスプライト:3122件。

ランタイム:3.5-7 ms
無駄なピクセル:9288(1.23%-96 x 96平方に相当)

出力(866 x 871):

5

色:(
黒は無駄なスペースです)

6


2
あなたのコードをダウンロードしました。構造体の定義を読むだけ:この怪物とは?!コードゴルフのように見えます。
akaltar

3
それでも、それは機能し、助けたので、ありがとう。私は失礼になりたくありませんでした。
akaltar

わからない、それは私自身のアルゴリズムO_Oのおかげよりもさらに速く、より良いパッカーですので、私はこの答えをskipeed理由
GameDeveloper

@akaltar私は想像できますが、私はその時間にまだ言語を学んでいました:)
Patryk Czachurski

実装が迅速で、良い結果が得られる
非常に

5

ここで、優れたヒューリスティックアルゴリズムを見つけることができます。最近似たようなものを試していたとき、これは私が見たほとんどの実装の基本的な出発点として参照されていることがわかりました。

多数の通常の形状、類似のサイズのアイテム、または小さい画像と小さい画像の適切な組み合わせのいずれかで特にうまく機能します。良い結果を得るための最善のアドバイスは、入力を画像サイズでソートし、小さい画像が大きい画像の周囲のスペースに収まるように、大きいものから小さいものにパックすることを忘れないことです。このソートをどのように行うかは、目標によって異なります。高さ+薄さ/短さ+幅の画像(低めの領域がある)は実際にはパック内で後で配置するのが非常に難しいと考えたため、1次近似として領域ではなく境界を使用しました。これらの奇妙な形は注文の前に向かっています。

以下は、私のウェブサイトのイメージダンプディレクトリからのランダムなイメージセットでのパッカーの出力のサンプルvisulizationです:)。 パッカー出力

四角の中の数字は、ツリーに含まれるブロックのIDなので、挿入の順序がわかります。最初のIDは「3」です。これは、最初のリーフノード(リーフのみに画像が含まれる)であり、その結果2つの親を持っているためです)。

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

3

個人的には、欲張りな最大のブロックに適合する最初のシステムを使用しています。最適ではありませんが、うまくいくトリックです。

適切な量​​のテクスチャブロックがある場合、問題自体がNPであっても、最適な順序を徹底的に検索できることに注意してください。


3

私が使用したものは、不規則なUVマップでもうまく機能し、UVパッチをビットマップマスクに変え、テクスチャ自体のマスクを維持し、UVパッチが適合する最初の位置を検索することです。いくつかの単純なヒューリスティック(高さ、幅、サイズなど)に従ってブロックを並べ、選択したヒューリスティックを最小化または最大化するためにブロックの回転を許可します。これにより、ブルートフォース用の管理可能な検索スペースが提供されます。

その後、いくつかのヒューリスティックを試行することを繰り返すことができ、および/または順序の選択にランダム係数を適用し、一定の制限時間がなくなるまで繰り返します。

このスキームを使用すると、小さなUVアイランドを、大きなUVアイランドによって作られたギャップに詰め込むことができます。さらに、単一のUVパッチ自体に残された穴の中に入れることもできます。


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を行の最も高い文字の高さだけ下に進め、別の行を埋めます

文字がなくなるか、別の行に収まらなくなるまで繰り返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.