以下に膨大なポリゴン数(3000万から3億ポリゴン)を持つシーンのレンダリングを最適化する方法を載せています。
-
可能な限りパックプリミティブを使うようにしてください(ジオメトリの上記を参照)。これがレンダリングを最適化するのに最も単純で より効果的 な方法です。ディスク上のジオメトリ(通常はAlembicファイル)のパックリファレンス、またはプロシージャルジオメトリシェーダを使えば、レンダラーは、メモリにジオメトリを読み込んで保持することなく、必要に応じてディスクからジオメトリをストリームし、処理が終われば破棄することができます。これによって、レンダリングが高速化されます。
-
もし全てのオブジェクトの境界ボックスが重複していなければ、各空間ボクセルがオブジェクトの一部の小さい領域で機能するので、空間的にジオメトリをうまくサブディビジョンすることができます。とはいえ、ポリゴンが重複したオブジェクトがたくさんあると、境界ボックスがテストするプリミティブの数を制限するため、サブディビジョンすることができません。 空間的にサブディビジョン構造を最適化すれば、光線がオブジェクトをより高速に移動します。光線は、オブジェクトを探す時の手間が少なるなるので、より効率的になります。
-
空間的なサブディビジョンと同様の考え方で、オブジェクトを空間的に分離させて、境界ボックスが重複しないようにします。
例えば、グリッド上に建っているビルを含んだシーンは通常は各ビルのオブジェクトは空間的に他のビルと距離を空けるのに対し、果物が入ったカゴのシーンでは、通常はオブジェクトがたくさん重複しています。もしシャドウレイをテーブルから果物カゴへ送った場合、境界ボックスが重複するので結局、果物すべてをテストしなければならなくなります。
すべてのオブジェクトを単一オブジェクトに結合したほうが、Mantraは単一オブジェクトのプリミティブに対してより良い最適化処理ができます。
また、ray-predicingを試すこともできます。オブジェクトをray-predicingに含めると、光線が発射される前にオブジェクトすべてが細分化されます。pre-dicingしたオブジェクトすべてがMantraによって自動的に単一オブジェクトに格納されます。これによってメモリを消費しますが、レイトレースが格段に速くなります。
-
ディスプレイスメント境界は、プリミティブを2つに分割しても、それに合わせて縮小しません。そのため、ディスプレイスメント境界は、プリミティブを重複してしまいます。つまり、各光線は、より多くの処理をしなければなりません。以下に、この問題を対処してレイトレースをより効率化する方法を記述します:
-
True Displacements と True Ray-Displacements をオフにします。これは、ディスプレイスメントをバンプマップとして扱います(つまり、新しくジオメトリを作成しません)。見た目を許容できれば、より高速になるでしょう。
-
大きなディスプレイスメントを使わない。ディスプレイスメント境界を0.1に設定して、ポリゴンのサイズが0.01単位であれば、重複する境界ボックスは100から1000になる可能性があります。ディスプレイスメント境界を小さいままにすれば、重複を最小限に抑えることができます。
-
ray-predicingを使います。ray-predicingをオンにすると、ディスプレイスメントシェーダすべてがレンダリング開始前に実行されます。これによって、Mantraはレンダリングが始まる時に、既に変位させるポリゴンの正確な位置を知っているため、最適なサブディビジョン構造を構築することができます。ただ残念なことに、このオプションを使うとメモリ使用量が膨らんでしまいます。その理由は、Dicingをすると通常は解像度に依存するからです(つまり画像が大きいほどメモリを使います)。
-
完全な境界ボックスを使います。predicingと同様で、これは空間的なサブディビジョンの構造を構築するときに、レンダリング開始前にディスプレイスメントシェーダを実行するので、効率的なレイトレースになります。とはいえ、それ以降は細分化したポリゴンを保持しません。メモリ使用量は改善されますが、ジオメトリはレンダリング実行時に再生成しなければなりません。
-
-
ディスプレイスメントシェーディングをする時、Mantraは何度もトレースする必要があるときにジオメトリをキャッシュ化します。というのも、Mantraは一度にジオメトリすべてをメモリに保持することはできないので、ジオメトリをキャッシュ化します。Mantraが一度にメモリに保持できる変位させるジオメトリのデフォルトサイズは約32MBです。ジオメトリキャッシュのサイズを大きくすると、Mantraは変位するジオメトリを再度細分化する処理の数が減ります。もしRAMがたくさんあるなら、ジオメトリすべてを当分の間だけ強制的にキャッシュ化するのでray-predicingを使ってください。そうしない場合は、キャッシュサイズをシステムに合うように変更してください。
-
光線が交差するオブジェクトを単純化します。これは、光線が交差する範囲を制限したり、レイトレースからプロキシジオメトリ(代用ジオメトリ)を使うことで可能です。
-
スコープ: 交差するオブジェクトの範囲を選択することで、Mantraは少し効率的な高レベルのサブディビジョン構造を構築することができます。そのため、光線が交差するオブジェクトの近くを飛ばないのであればMantraは迅速にその光線を取り払います。
-
最大光線距離: 時々、最大光線距離を使って、交差するオブジェクトの範囲を制限することができます。これは、VEX関数のパラメータであり、シェーダに記述する必要があります。アンビエントオクルージョンを実行する時、例えば、シェーディングしているサーフェスに比較的近いオブジェクトに対してチェックだけしたい場合があります。
-
プロキシジオメトリ(代用ジオメトリ): ファントムオブジェクトを使って、それをスコープと結び付ければ、オブジェクトを“フェイク”することができます。例えば、果物のカゴではなく、テクスチャマップを追加したカードをファントムオブジェクトにします。主要な光線は実際の果物カゴにずっと当たっていても、ワイングラスの屈折は単一ポリゴンのカードに当たります。これによって、より効率化されます。ファントムはオブジェクトレベルでのみサポートされています。
-
シェーディング品質の調整: ここでの考え方としては、レイトレーシング時に少しだけプリミティブを大きくしておけば、結果としてそれよりも小さいプリミティブにレイトレースが処理されるという考えです。シェーディング品質を制御するには、 Rays Shading Quality パラメータを調整します。この動作を微調整するには、ray-measurerを使うことができます。Z-Importanceを調整すれば、Z方向に多かれ少なかれ分割が行なわれます。分割が少ないほど、より効率的になります。
-
-
光線を高速にするのとは別で、レイトレースを高速化する唯一の方法は、送信する光線をより少なくすることです。品質の観点では、レイトレースがマップよりも好まれることがほとんどですが、シーンが非常に複雑になると、マップを使用する必要性が出てきます。
-
シャドウマップ: レイトレースシャドウの代わりにシャドウマップを使います。特にボリュームレンダリングで重要になってきます。各ボリュームは、シャドウマップを生成するときに各サンプルをN回評価されます。シャドウの評価を実行すると、それがテクスチャマップ検索となります(ボリュームの評価と比べると負担が大きい場合もあれば、そうでない場合もあります)。メイン画像内の各ピクセルは、レイトレースシャドウを実行する時にM回ボリュームを評価します。また、各ボリュームの評価には、N回(またはNの数分の1)シャドウを評価をします。つまり、M*Nの回数だけの処理されます。非ボリュームレンダリングでもシャドウマップはメリットがあります。なぜなら、通常は光線を送るのはマップ検索を実行するよりも重いからです。
-
反射マップ: 反射マップはシャドウマップに似ていますが、通常は環境照明だけに使います。なぜなら、局所的な照明と環境マップはお互い調和しないからです。
-
光線の跳ね返りの制限: 光線の跳ね返りを制限すると、あまりにも不自然になるので、光線の重みのパラメータを変更する必要があります。シェーダが正しく記述されていれば、期待した通りに光線が分散して、重み付け以上の効果があります。
-
環境マップ照明(HDRI照明): 独自シェーダを記述するよりも、環境光オブジェクトを使ってください。環境マップはテクスチャマップを解析して環境のサンプリングを最適化します。
-
-
シェーダが光線を送る時、光線は任意の場所から送ることができ、そして任意の場所に飛ぶことができます。つまり、Mantraは光線が将来ジオメトリと交差するのか予測できない事を意味しています。現在では、もし光線がプロシージャルに当たると、プロシージャルがレイトレースするようにフラグが立ち、そのジオメトリが保持されます。つまり、64ビットマシンでない限り3億ポリゴンをレイトレースすることができません。小さいシーンであるほどレンダリングが可能ですが、状況次第です。
-
Mantraは箱からいくつかのプロシージャルを取り出して、HDKによるカスタムプロシージャルジオメトリを記述することができます。プロシージャルで境界ボックスを指定しない限り、Mantraはジオメトリの大きさを知らないで、まるでIFDで指定されたかのようにプロシージャルを実行してしまうことに注意してください。これによって、すべての意図と目的を果たすために、プロシージャルのメリットを台無しにしてしまいます。
-
File Procedural(ジオメトリの遅延ロード): これは、必要に応じてディスクからジオメトリを読み込みます。ファイルの境界ボックスを指定しなければなりません。そうしないと、プロシージャルはIFD処理中にジオメトリを読み込んでしまいます。これではプロシージャルの目的を台無しにしてしまいます。
File Proceduralには2つのモードのオペレーションがあります。 Share Geometry のトグルをオンにすると、読み込むジオメトリは、このプロシージャルの他のインスタンス間で共有します。これは、重いジオメトリを何回もインスタンス化するときに役に立ちます。ジオメトリを何回も読み込むのとは違って、Mantraは一度だけジオメトリを読み込めば、あとはプロシージャルすべてに、そのジオメトリを共有するだけです。
とはいえ、これは、Mantraがレンダリング中にジオメトリを保持しておく必要があることを意味します。代わりの案( Share Geometry がオフの時)として、必要に応じてプロシージャルのジオメトリを読み込み、事後にジオメトリを解放する方法もあります。この場合、レンダリングのメモリ使用量が大きくなります。
-
Program Procedural: Mantraが外部のプログラムを実行してジオメトリを生成することができます。この方法のメリットは、プログラムがプロシージャルの視覚的な詳細レベルに基づいて別々に実行することができるということです。プログラムの文字列は、%LODをオブジェクトの詳細レベルに置き換えて読み込まれます。
スクリーン空間での詳細レベルに基づいて、別々のジオメトリを使う簡単なシェルスクリプトのプログラムで例えると、スクリーン空間の4ピクセルに詰め込まれた100万ポリゴンの宇宙船モデルがあるとしたら、このケースではより低いポリゴン数のモデルを使うことを考慮することができます。
-
-
キャッシュに保持するジオメトリを最小限にする方法:
-
帯単位でレンダリングして、その結果の画像を合成します:
Camera Cropチャンネルを使って、画像を帯単位でレンダリングすることができます。この帯は水平・垂直に切り替えることができます。これによってMantraがキャッシュに保持するジオメトリの量を最小限にすることができます。
ここで問題になるのが、走査線の最初のタイルをレンダリングする時、走査線よりも上にあるタイルがレンダリングされるまで、いくつかのプリミティブが保持されるということです。非常に大きなX方向の解像度に対して、長い時間重複したプリミティブが保持されます。画像を垂直の縞に分割することでメモリは水平方向と比べて長く保持されません。
-
バケットサイズの変更:
驚くことに、バケットサイズを大きくすると、結果としてメモリ使用量(メモリフットプリント)が小さくなります。バケットサイズが大きくなると、重複するプリミティブが少なくなるので、タイルの処理の後に解放できるメモリが多くなります。
-
画像をスクリーン空間の帯単位で分割する(帯単位のレンダリング)のとは別の方法として、空間的に画像を分割することも可能です。シーンのオブジェクトをサブセットに分ける(前景オブジェクトと背景オブジェクト)ことで、レンダリングでのメモリのパフォーマンスが良くなります。画像は合成することで最終画像になります。
-
ファントムチャンネルを使えば、オブジェクトをレイトレースで表示することができますが、カメラからは見えません。これは、実ジオメトリがレイトレースをオフにしている場合にプロキシジオメトリ(代用ジオメトリ)を使うことができます。
-
-
Depth Complexityという用語は、スクリーン空間の特定のポイントにおけるZ方向で積み重なっているプリミティブの数のことです。例えば、シーン内の単一ポリゴンのDepth Complexityは1です(ポリゴンがない箇所のピクセルは0)。ボックスはDepth Complexityが2です(正面サーフェスが1と背面サーフェスが1)。Depth Complexityが大きいほど、処理時間とメモリ使用量が大きくなります。
とはいえ、完全に前景プリミティブに隠されたオブジェクトはMantraで処理しないようにすることで処理量とメモリを最小限に抑えることができます。ただ、Mantraがプリミティブが隠れいているかどうか判断する唯一の方法は、そのプリミティブの境界ボックスを評価することです。
そのことから、もし2つの円がちょうど重なっている状況で考えてみると、人間目線では、後ろの円はもう処理する必要がないと思うでしょう。しかし、Mantraでは後ろの円に関しては、境界ボックスしか知らないです。前の円は完全に境界ボックスを隠せないので、後ろの円は処理されます。
ほとんどのケースでは、背景プリミティブは分割され、分割されたプリミティブのいくつかを破棄しますが、まだ追加処理をすることが可能です。
Depth Complexityは、プリミティブが透明なときに深刻な問題になる可能性があります。半透明のスプライトを積み重ねた状況を考えてみます。各スプライトは50%の不透明度で1000個のスプライトを積み重ねて単一のピクセルにします。合成をすると、2番目のスプライトの影響は0.5、3番目は0.25、4番目は0.125などになります。つまり、10枚目のスプライトのピクセルカラーへの影響は0.0001にしかなりません。不透明度の閾値(
vm_opacitythresh
)は、蓄積した不透明度が十分であると考慮される閾値を指定するのに使います。例えば、閾値を0.99に設定すれば、上記の例では、処理するスプライトの数を制限します。各タイルに対して、Mantraは、各ピクセルの各サンプルで、ある程度の情報を記録する必要があります。そのため、メモリに
depth complexity
、pixel samples
、bucket size
の関数が使われます。