回答:
より良いテクスチャパッキングアルゴリズムを思い付く1つの仕事に数ヶ月を費やしました。
私たちが始めたアルゴリズムは簡単でした。すべての入力項目を収集します。消費された総ピクセル数で並べ替えます。それらをスキャンラインの順序でテクスチャにレイアウトし、左上のピクセルから右上のピクセルまでをテストし、ラインを下に移動し、配置が成功するたびに左上のピクセルにリセットします。
幅をハードコードするか、このための別の発見的方法を考え出す必要があります。直角度を維持するために、アルゴリズムは128から始まり、幅よりも深くない結果になるまで128秒ずつ増加します。
それで、私たちはそのアルゴリズムを持っていて、それを改善することに決めました。気まぐれなヒューリスティックを試してみました-一緒に収まるオブジェクトを見つけようとして、必要なスペースパッキングプロパティのセットに対していくつかの重み付けを行い、回転と反転を行いました。すべての作業の後、文字通り3か月の作業の後、3%のスペースを節約しました。
うん。3%。
そして、圧縮ルーチンを実行した後、実際には大きくなりました(まだ説明できません)ので、すべてを捨てて古いアルゴリズムに戻りました。
アイテムを並べ替え、スキャンライン順にテクスチャにジャムします。アルゴリズムがあります。コーディングは簡単で、実行は高速です。驚くほどの量の作業がなければ、それほど良くなることはありません。あなたの会社が少なくとも 50人、そしておそらくそれ以上でなければ、その仕事は価値がありません。
副次的な注意事項として、私はこのアルゴリズム(固定幅512ピクセル)を、文字通りまったく同じアプリケーション(ftglesではなく、openglでレンダリングされたfreetypeグリフ)に実装しました。結果は次のとおりです。私はValveの距離フィールドベースのテキストレンダリングアルゴリズムを使用しているため、ぼやけて見えます。これは、グリフ間の余分なスペースも考慮しています。明らかに、多くの空きスペースが残っているわけではなく、オープンスポットに物を詰め込むのに良い仕事をします。
このすべてのコードはBSDライセンスであり、githubで入手できます。
アンドレアロディの博士論文は、二次元ビンパッキングと割り当て問題のためのアルゴリズムと題されています。
論文は、この問題のより困難な形態のいくつかを取り上げています。幸いなことに、テクスチャパッキングは最も簡単なバージョンです。彼が見つけた最高のアルゴリズムは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アトラスにすべてのテクスチャを収めることが可能かどうかを判断するのに役立ちます。
まだ興味がある人は、rectpack2Dライブラリを完全に書き直して、効率を改善しました。
これは、維持することによって動作しstd::vector
、アトラス内の空きスペースのをいくつかの初期の最大サイズ(特にGPU上、通常、最大許容テクスチャサイズ)で始まる、分割最初の実行可能な空きスペースとバックベクトルへの分割を節約します。
以前のようにツリー全体を保持するのではなく、ベクターを使用するだけでパフォーマンスが飛躍的に向上しました。
このライブラリはMITの管理下にあるので、便利だと思ったら嬉しいです!
テストは、Intel(R)Core(TM)i7-4770K CPU @ 3.50GHzで実施されました。バイナリは、-03スイッチを使用してclang 6.0.0で構築されました。
ランタイム:4ミリ秒
無駄なピクセル:15538(0.31%-125 x 125平方に相当)
出力(2116 x 2382):
色:(
黒は無駄なスペースです)
ランタイム:3.5-7 ms
無駄なピクセル:9288(1.23%-96 x 96平方に相当)
出力(866 x 871):
色:(
黒は無駄なスペースです)
ここで、優れたヒューリスティックアルゴリズムを見つけることができます。最近似たようなものを試していたとき、これは私が見たほとんどの実装の基本的な出発点として参照されていることがわかりました。
多数の通常の形状、類似のサイズのアイテム、または小さい画像と小さい画像の適切な組み合わせのいずれかで特にうまく機能します。良い結果を得るための最善のアドバイスは、入力を画像サイズでソートし、小さい画像が大きい画像の周囲のスペースに収まるように、大きいものから小さいものにパックすることを忘れないことです。このソートをどのように行うかは、目標によって異なります。高さ+薄さ/短さ+幅の画像(低めの領域がある)は実際にはパック内で後で配置するのが非常に難しいと考えたため、1次近似として領域ではなく境界を使用しました。これらの奇妙な形は注文の前に向かっています。
以下は、私のウェブサイトのイメージダンプディレクトリからのランダムなイメージセットでのパッカーの出力のサンプルvisulizationです:)。
四角の中の数字は、ツリーに含まれるブロックのIDなので、挿入の順序がわかります。最初のIDは「3」です。これは、最初のリーフノード(リーフのみに画像が含まれる)であり、その結果2つの親を持っているためです)。
Root[0]
/ \
Child[1] Child[2]
|
Leaf[3]
私が使用したものは、不規則なUVマップでもうまく機能し、UVパッチをビットマップマスクに変え、テクスチャ自体のマスクを維持し、UVパッチが適合する最初の位置を検索することです。いくつかの単純なヒューリスティック(高さ、幅、サイズなど)に従ってブロックを並べ、選択したヒューリスティックを最小化または最大化するためにブロックの回転を許可します。これにより、ブルートフォース用の管理可能な検索スペースが提供されます。
その後、いくつかのヒューリスティックを試行することを繰り返すことができ、および/または順序の選択にランダム係数を適用し、一定の制限時間がなくなるまで繰り返します。
このスキームを使用すると、小さなUVアイランドを、大きなUVアイランドによって作られたギャップに詰め込むことができます。さらに、単一のUVパッチ自体に残された穴の中に入れることもできます。
最近、特定のサイズの複数の画像ファイルにテクスチャをパックするPythonスクリプトをリリースしました。
ブログから引用:
「オンラインで見つけることができるパッカーは多数ありますが、複数のディレクトリで大量の画像を処理できるパッカーを見つけることは困難でした。したがって、アトラスパッカーが誕生しました。
そのまま、ベーススクリプトで小さなスクリプトが開始され、すべての.PNGがアトラスに読み込まれます。そのアトラスがいっぱいになると、新しいアトラスが作成されます。その後、新しいアトラスでスポットを見つける前に、以前のすべてのアトラスで残りの画像をフィッティングします。このようにして、各アトラスは可能な限り密集します。アトラスは、画像の元となるフォルダーに基づいて名前が付けられます。
アトラスのサイズ(65行目)、パックするイメージの形式(67行目)、ロードディレクトリ(10行目)、および保存ディレクトリ(13行目)は、Pythonの経験がなくても簡単に変更できます。小さな免責事項として、これは数日で一緒にホイップされ、特にエンジンで動作するようになりました。機能をリクエストし、独自のバリエーションでコメントし、エラーを報告することをお勧めしますが、スクリプトの変更は自由な時間に行われます。」
ここで完全なソースコードをチェックしてください:http : //www.retroaffect.com/blog/159/Image_Atlas_Packer/#b
グリフテクスチャのすべて(または大部分)がほぼ同じサイズであるため、フォントをパックするのは非常に簡単です。あなたに発生する最も簡単なことを行うと、それは最適に非常に近くなります。
非常に異なるサイズの画像をパックする場合、賢さはより重要になります。次に、ギャップなどに詰め込むことができます。それでも、前述のスキャンライン順序検索などの単純なアルゴリズムでは、非常に妥当な結果が得られます。
高度なアルゴはどれも魔法ではありません。単純なアルゴリズムよりも効率が50%高くなることはありません。また、膨大な数のテクスチャシートがない限り、一貫した利点は得られません。それは、より優れたアルゴリズムによってもたらされる小さな改善は、全体としてのみ見られるからです。
シンプルに進み、努力が報われる場所に進みましょう