仮想テクスチャリングは実際にどのように効率的ですか?


27

参考までに、私が言及しているのは、idTech 5のMegaTextureテクノロジーで最初に導入された(私が信じている)テクニックの「一般名」です。それがどのように機能するかについての概要を見るには、こちらのビデオをご覧ください。

私は最近、それに関連するいくつかの論文や出版物をざっと読んでいますが、どうすれば効率的になるのか理解できません。「グローバルテクスチャページ」スペースから仮想テクスチャ座標へのUV座標の絶え間ない再計算が必要ではないでしょうか。そして、それはジオメトリをバッチ処理するほとんどの試みを完全に抑制しないのでしょうか?任意のズームインを許可するにはどうすればよいですか?ある時点でポリゴンを再分割する必要はありませんか?

私にはわからないことがたくさんあり、このトピックに関する実際に簡単にアクセスできるリソースを見つけることができませんでした。

回答:


20

概要

Virtual Texturing(VT)、またはSparse Virtual Texturesの主な理由は、時々呼ばれるように、メモリの最適化のためです。事の要点は、レンダリングされたフレームに必要な実際のテクセル(ページ/タイルとして一般化)だけをビデオメモリに移動することです。したがって、ビデオメモリやメインメモリに収まるよりもはるかに多くのテクスチャデータをオフラインまたは低速のストレージ(HDD、光ディスク、クラウド)に保存できます。最新のオペレーティングシステムで使用される仮想メモリの概念を理解していれば、本質的には同じことです(名前は偶然ではありません)。

VTは、メッシュをレンダリングする前に各フレームを実行してから頂点データを再送信するという意味でUVを再計算する必要はありませんが、着信UVから間接ルックアップを実行するには、頂点シェーダーとフラグメントシェーダーでかなりの作業が必要です。ただし、適切な実装では、仮想テクスチャまたは従来のテクスチャを使用している場合、アプリケーションに対して完全に透過的でなければなりません。実際、ほとんどの場合、アプリケーションは、仮想と従来の両方のタイプのテクスチャリングを混合します。

バッチ処理は理論的には非常にうまく機能しますが、詳細については一度も検討していません。ジオメトリをグループ化するための通常の基準はテクスチャであり、VTを使用すると、シーン内のすべてのポリゴンが同じ「無限に大きい」テクスチャを共有できるため、理論的には1回の描画呼び出しで完全なシーン描画を実現できます。しかし、実際には、他の要因が作用して、これを非実用的にしています。

VTの問題

VTセットアップでは、ズームイン/ズームアウトと急激なカメラの動きを処理するのが最も困難です。静的なシーンには非常に魅力的に見えるかもしれませんが、物事が動き始めると、外部ストレージにストリーミングできるよりも多くのテクスチャページ/タイルが要求されます。非同期ファイルIOとスレッド化は役立ちますが、ゲームのようにリアルタイムシステムの場合は、高解像度のものが到着するまで、低解像度のタイルでいくつかのフレームをレンダリングする必要があります。 、テクスチャがぼやけます。ここに特効薬はなく、それがIMOテクニックの最大の問題です。

また、仮想テクスチャリングでは透明度を簡単に処理できないため、透明ポリゴンには従来のレンダリングパスが別途必要です。

全体として、VTはおもしろいですが、私は誰にも勧めません。それはうまく機能しますが、実装と最適化が困難です。さらに、私の好みに合わせて必要なコーナーケースとケース固有の調整が多すぎます。しかし、大規模なオープンワールドゲームやデータ視覚化アプリの場合、すべてのコンテンツを利用可能なハードウェアに収める唯一のアプローチかもしれません。多くの作業を行うと、idのRageの PS3およびXBOX360バージョンで見られるように、限られたハードウェアでもかなり効率的に実行することができます。

実装

OpenGL-ESを使用してiOSでVTをある程度動作させることができました。私の実装は「出荷可能」ではありませんが、リソースが必要であり、それがあれば、おそらく実装できるでしょう。ここソースコードを見ることができます。これは、各部分がどのように適合するかについてのより良いアイデアを得るのに役立つかもしれません。iOS Simで実行されているデモのビデオ次に示します。シミュレーターはシェーダーをエミュレートするのがひどいため、非常に遅れていますが、デバイス上ではスムーズに実行されます。

次の図は、私の実装におけるシステムの主要コンポーネントの概要を示しています。SeanのSVTデモ(下のリンク)とはかなり異なりますが、最初のGPU Proの書籍(リンク)にある「CUDAを使用した仮想テクスチャリングの加速」という論文で提示されたアーキテクチャに近いアーキテクチャです

仮想テクスチャリングシステム

  • Page Files仮想テクスチャは、前処理ステップとしてすでにタイル(AKAページ)にカットされているため、必要なときにいつでもディスクからビデオメモリに移動できます。ページファイルには、仮想ミップマップとも呼ばれるミップマップのセット全体も含まれます。

  • Page Cache ManagerPage TableとのPage Indirectionテクスチャのアプリケーション側の表現を保持します。オフラインストレージからメモリにページを移動するには費用がかかるため、既に利用可能なものをリロードしないようにキャッシュが必要です。このキャッシュは、非常に単純な最長未使用時間(LRU)キャッシュです。キャッシュは、データの独自のローカル表現を使用して物理テクスチャを最新の状態に保つコンポーネントでもあります。

  • Page Providerシーンの特定のビューのために必要なページを取得し、キャッシュにそれらを送信する非同期ジョブキューです。

  • Page Indirectionテクスチャは、着信UVをマッピングする仮想質感、各ページ/タイルのための一つの画素を持つテクスチャでPage Tableキャッシュテクスチャ実際のテクセルデータを持っています。このテクスチャは非常に大きくなる可能性があるため、RGBA 8:8:8:8やRGB 5:6:5などのコンパクトな形式を使用する必要があります。

しかし、ここではまだ重要な部分が欠落しています。それが、どのページをストレージからキャッシュに、したがってにロードする必要があるかを判断する方法Page Tableです。それがフィードバックパスPage Resolver入力の場所です。

フィードバックパスは、ビューの事前レンダリングであり、カスタムシェーダーと非常に低い解像度で、必要なページのIDをカラーフレームバッファーに書き込みます。上記の立方体と球体のカラフルなパッチワークは、RGBAカラーとしてエンコードされた実際のページインデックスです。この事前パスレンダリングはメインメモリに読み込まれ、によって処理されPage Resolverてページインデックスをデコードし、で新しいリクエストを実行しPage Providerます。

フィードバックの事前パス後、VTルックアップシェーダーを使用してシーンを通常どおりにレンダリングできます。ただし、新しいページリクエストが完了するのを待たないことに注意してください。同期ファイルIOでブロックするだけなので、ひどいことになります。リクエストは非同期であり、最終ビューがレンダリングされるまでに準備が整っている場合とできない場合があります。準備ができていて、甘いが、そうでなければ、常にキャッシュに低解像度ミップマップのロックされたページをフォールバックとして保持するため、使用するテクスチャデータがいくつかありますが、ぼやけてしまいます。

チェックアウトする価値のあるその他のリソース

VTはまだコンピュータグラフィックスのややホットなトピックであるため、多数の優れた資料が利用可能であり、さらに多くを見つけることができるはずです。この回答に追加できるものがあれば、お気軽にお問い合わせください。私はこのトピックについて少し錆びていますが、過去1年間あまり読んでいませんが、思い出がものを再訪するのは常に良いです:)


ねえ、素晴らしい答えをありがとう。私はこれが一般的に眉をひそめていることを知っていますが、私はさまざまな問題を抱えているので、ほとんどのものをざっと読んでいます-将来のトピックの直感的な概要を取得するために)-とにかく、可能であれば、プロセス自体の概要を示す擬似コードの例を投稿できますか?
ラマゲドン

1
@Llamageddon、それは私がまだ手元に図を持っていたことだけが起こります;)私は擬似コードがそれを提供するのが少し難しいと思っています。しかし、拡張された答えがテクニックの一般的なアイデアを与えるのに役立つことを願っています。
glampert

3
最新のハードウェアのほとんどは、プログラム可能なページテーブルを公開し、リダイレクトテクスチャの必要性を排除していることに注意してください。これは、たとえばdirectx11 タイル化リソースまたはopengl スパーステクスチャ上に構築れるdirectx12 予約済みリソースを介して公開されます
MooseBoys

1
@Llamageddonでは、ページのピクセルが通常繰り返されるため、フィードバックの事前パスを低解像度で実行して、できるだけ多くのコンピューティングとメモリを節約できます(デモで大きな色の付いた正方形に気付くことができます)。最終的にそのような表示ページを見逃すかもしれませんが、システムは常にキャッシュ内で使用可能なVT全体の最低のミップマップを常に保持する必要があるため、視覚的に大きな影響はありません。リンクした2番目のペーパーには、付録のシェーダーの例がすべて含まれています。また、自分のプロジェクトのリポジトリを参照することもできます。これらは似ています。
glampert

1
@glampertああ、なるほど。それは理にかなっている。それでも、透明度を処理するためのオプションはたくさんあると思います。ページIDパスでは、ディザリング(膨大な数の透明レイヤーがない限り、ヒストグラムですべてのページが表示されます)、またはkバッファーアプローチを使用するか、オブジェクトがカメラ(フィードバックパスでレンダリングするのではなく)。
ネイサンリード

11

仮想テクスチャリングは、テクスチャアトラスの論理的な極端です。


テクスチャアトラスは、内部の個々のメッシュのテクスチャを含む単一の巨大なテクスチャです。

テクスチャアトラスの例

テクスチャを変更すると、GPU上で完全なパイプラインフラッシュが発生するため、テクスチャアトラスが一般的になりました。メッシュを作成するとき、UVは、テクスチャアトラス全体の正しい「部分」を表すように圧縮/シフトされます。

@ nathan-reedがコメントで言及したように、テクスチャアトラスの主な欠点の1つは、繰り返し、クランプ、境界線などのラップモードを失うことです。さらに、テクスチャの周囲に十分な境界線がない場合、誤ってフィルタリングを行うときに、隣接するテクスチャからのサンプル。これは出血アーティファクトにつながる可能性があります。

テクスチャアトラスには、サイズという大きな制限があります。グラフィックスAPIは、テクスチャの大きさに弱い制限を設けます。とはいえ、グラフィックスメモリは非常に大きいだけです。そのため、v-ramのサイズによって与えられるテクスチャサイズにも厳しい制限があります。仮想テクスチャは、仮想メモリから概念を借用することにより、この問題を解決します

仮想テクスチャは、ほとんどのシーンで、すべてのテクスチャのごく一部しか表示されないという事実を利用します。したがって、テクスチャのそのサブセットのみがvramにある必要があります。残りはメインRAMまたはディスク上にあります。

それを実装する方法はいくつかありますが、ショーンバレットがGDCトークで説明した実装について説明します。(視聴することを強くお勧めします)

仮想テクスチャ、物理テクスチャ、ルックアップテーブルの3つの主要な要素があります。

仮想テクスチャ

仮想テクスチャは、すべてに適合するのに十分なvramがあれば理論上のメガアトラスを表します。実際にはメモリ内のどこにも存在しません。物理テクスチャは、vramに実際にあるピクセルデータを表します。ルックアップテーブルは、2つの間のマッピングです。便宜上、3つの要素すべてを同じサイズのタイルまたはページに分割します。

ルックアップテーブルには、物理​​テクスチャ内のタイルの左上隅の場所が格納されます。それでは、仮想テクスチャ全体にUVを与えた場合、物理テクスチャに対応するUVをどのように取得するのでしょうか?

まず、物理テクスチャ内のページの場所を見つける必要があります。次に、ページ内のUVの位置を計算する必要があります。最後に、これら2つのオフセットを一緒に追加して、物理テクスチャ内のUVの位置を取得できます。

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


pageLocInPhysicalTexの計算

ルックアップテーブルを仮想テクスチャのタイル数と同じサイズにした場合、最近傍サンプリングでルックアップテーブルをサンプリングするだけで、物理テクスチャ内のページの左上隅の位置を取得できます。

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);


inPageLocationの計算

inPageLocationは、テクスチャ全体の左上ではなく、ページの左上を基準としたUV座標です。

これを計算する1つの方法は、ページの左上のUVを差し引いてから、ページのサイズにスケーリングすることです。ただし、これはかなりの数学です。代わりに、IEEE浮動小数点の表現方法を活用できます。IEEE浮動小数点は、基数2の一連の小数で数値の小数部分を格納します。

ここに画像の説明を入力してください

この例では、番号は次のとおりです。

number = 0 + (1/2) + (1/8) + (1/16) = 0.6875

次に、仮想テクスチャの簡易バージョンを見てみましょう。

シンプルな仮想テクスチャ

1/2ビットは、テクスチャの左半分にいるのか右にいるのかを示します。1/4ビットは、半分の4分の1を示しています。この例では、テクスチャは16に分割されているため、最初の2ビットはどのページにいるかを示しています。ビットはページ内の場所を示します。

exp2()でフロートをシフトし、fract()でそれらを取り除くことで、残りのビットを取得できます

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);

numTilesは、テクスチャの片側あたりのタイル数を示すint2です。この例では、これは(4、4)になります

それでは、緑色の点のinPageLocationを計算してみましょう。(x、y)=(0.6875、0.375)

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);

完了する前に最後にやることがあります。現在、inPageLocationは仮想テクスチャ「スペース」のUV座標です。ただし、物理的なテクスチャ「空間」にUV座標が必要です。これを行うには、仮想テクスチャサイズと物理テクスチャサイズの比率でinPageLocationをスケーリングする必要があります。

inPageLocation *= physicalTextureSize / virtualTextureSize;



したがって、完成した関数は次のとおりです。

float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
    float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);

    float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
    inPageLocation = fract(inPageLocation);
    inPageLocation *= physicalTexSize / virtualTexSize;

    return pageLocInPhysicalTex + inPageLocation;
}

私は、仮想テクチャリングについて言及しているのではなく、最もよく知られているのはidTech 5のMegaTextureテクノロジーです。こちらこちらもご覧ください。多くの最新エンジンのレンダリングパイプラインの概要、およびシャドウマップに同様のアプローチを使用するいくつかの論文で言及されているのを見ました。テクスチャアトラスと多くの共通点があります。はい、それらをある意味で使用しますが、テクスチャアトラスと混同することはありません。
ラマゲドン

ああ。リンクをありがとう。それらを質問に追加できますか。私はそれに応じて私の答えを更新します
RichieSams

3
IMO、単純なテクスチャアトラス(仮想テクスチャではない)の主な欠点は、繰り返しやクランプなどのラップモードを失うことです。また、浮動小数点の精度ではなく、フィルタリング/ミップマッピングにより出血が発生します。フロートの精度が通常の(非仮想)テクスチャの問題になっているのには驚かされます。16Kテクスチャ(現在のAPIで許可されている最大値)でさえ、フロートの精度を実際に低下させるほど大きくはありません。
ネイサンリード

@RichieSams Btw、別の質問に対しても、あなたの答えは良いものだと思います。Q&Aを投稿する必要があります。
ラマゲドン

うーん、これはかなりよく説明していますが、ミップレベルでどのように機能するのかは本当にわかりません。...私はそれを理解した上で、私の特定の問題を書き留めることがしたいが、それはちょっと私を見逃さ
Llamageddon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.