レイヤ化の背景

このページでは、レイヤ化シェーダの背景についての説明をします。

レイヤ化の基本

レイヤ化は、非常に複雑で視覚的に魅力のあるマテリアルを作成するために、コンピュータ グラフィックスで一般的に使用されます。その目的は、マテリアルの混合や単純なコーティングを施したマテリアル(木材のセラックや濡れた石など)の場合もあれば、さらに複雑な組み合わせの場合もあります。たとえば、車の塗装に泥が飛び散った様子です。この場合、クリアコート、顔料、フレークなど複数のレイヤを重ねた塗装をベースに、その上から泥を散乱させたレイヤ、さらに水のレイヤを重ねて配置しています。

注意: ここでは、マテリアルの物理的に可能なレイヤ化について取り扱います。物理の法則に反するものを含め、さまざまな「ブレンド モード」がある「Photoshop 感覚」でのレイヤ化ではありません。このような手法は、テクスチャを重ね合わせて何らかのエフェクトを作り出す場合には有効ですが、マテリアルが物理的に混ざることを再現する場合には適していません。

製造要件の観点から、レイヤ化シェーダは以下の条件を満足させる必要があります。

mib_color_mix など単純な色を組み合わせるシェーダを使ってシェーダを重ね合わせると、効率に影響が出ないか心配になります。視覚的には当然のことながら目的の外観が得られますが、いくつかの問題もあります。

一番の問題は、複雑さが増すと、幾何級数的にパフォーマンスの効率が落ちることです。例を使って説明します。

レイヤ化ライブラリを使う意義が分かる例

このイメージは、単純数学によるカラー混合でレンダリングされています。このレンダリングには 6 分かかります。
このイメージは、単純数学によるカラー混合でレンダリングされています。
このレンダリングには 6 分かかります。
これはまったく同じシーンですが、レイヤー化シェーダを使用したものです。レンダリングが 58 秒で完了しました。
これはまったく同じシーンですが、レイヤー化シェーダを使用したものです。レンダリングが
58 秒で完了しました。

上のイメージは、複数のレイヤで構成されたマテリアルを含むシーンです。

レンダー時間の例

より難易度の高いシーンでは、この違いはさらに顕著になります。特に、反射する放射レイの数が幾何学的に増加する閉鎖空間では、速度がすぐに 1/10 や 1/100 にまで低下することがあります。

同じようなテスト用シーンで透明が多用されているものを使って計測すると、レイヤ化を使用した場合が 22 であるのに対し、使用しないと 15 もかかりました。


課題への対処

レイヤ化シェーダは、互いに連係して動作するよう設計された一連の C++ mental ray シェーダ、そしてシェーダ キット(コンポーネントを結合する専用シェーダが付属)、さらにさまざまなシェーディング コンポーネント モデルを提供する一連のコンポーネント シェーダで構成されています。この実装は、mia_material と、NVIDIA ARC のレンダリング製品全体に開発された技術の両方を応用したものです。具体的には、コンポーネント要素が、NVIDIA のマテリアル定義言語(MDL)で使用できるものに似ています。レイヤ数が事前定義されているわけではないため、より多くの要素コンポーネント(追加の光沢または散乱コンポーネントなど)を使って任意のシェーディング モデルを拡張することができます。

以下、課題を検証して、それらをレイヤ化ライブラリで対処する方法を示します。

重要度の伝播(レイ数の分解)

3 つのシェーダを 80%、20%、0% の割合で混合する場合を考えてみます。これらを単純な計算ノードに接続すると、3 つのシェーダそれぞれが完全に実行されます。つまり、サンプル レイが 50 あるとして、3 つのシェーダそれぞれが光沢反射を計算する場合、計算モードが 3 つすべてを実行するため、レンダリングで取り扱うレイの総数は 150 になります。

さらに、これらの光沢反射したレイが同様に混合された別のサーフェスに当たる場合、この二次的サーフェスの計算モードですべての入力シェーダが実行されます。さらに、ミキサに接続されている個別シェーダmia_material のように知的な判断ができ、より高いトレース深度では単一のレイのみ照射するという場合でも、これらのシェーダが 3 つミキサに入力されていれば 3 つのレイが照射されます。

この程度なら大したことではないと思うかもしれませんが、幾何級数的に増大するレイ ツリーでは、トレース深度の接点が 1 つ増えるとレイ ツリーが 3 倍になるため、あっという間に手に負えない規模になります。

このような処理は、代償は大きいわりに、多くの場合視覚的なメリットはほとんどありません。またこれは、統一サンプリング(それぞれの視線の分析に基づいて最も視覚的メリットが得られる部分にのみ時間を割く手法)の使用効果も妨げることになります。理想的には、この場合なら二次的レイ トレーシングを最小限にし、統一サンプリングのアダプティブ機能を使用することで品質を向上すべき部分に処理を集中させる必要があります。

問題は、コンポーネント シェーダがその重要度に応じて実行されてはいないことです。最初のシェーダは、見える部分が 80% であることを分かっていません。2 番目のシェーダもそれが 20% しかないことを知りません。3 番目にいたっては、その処理が結果にまったく影響しないため、そもそも呼び出される必要がなかったのです。これら 3 つのシェーダはすべて、あたかも自分だけがマテリアルの表現を任されていると信じているかのように、盲目的に実行させられています。

視覚的な重要度に適した処理にするには、最初のシェーダでレイの 80 % 程度を計算し、2 番目のシェーダで残りの 20 % を計算し、3 番目のシェーダはまったく実行しないのが望ましいといえます。

結合したシェーダのコンポーネントのすべての入力のタイプがシェーダであるため、要求があったときのみ実行することができます(この例での 0% に対してはまったく実行されない)。さらに、変数「state->importance」が呼び出しの前に調整され、呼び出されるシェーダでこの変数に基づいた選択ができる(光沢反射などの外観にはレイ数を減らす)ようになります。最後に、トレース深度が最初にレイが当たったときより低くなると、結合の比重で決まる確率に基づいて、コンポーネントの二次レイのうち 1 つのみをトレースします。これは、統一サンプリング モデルの利点を生かすための設計です。

前述のように、複雑なレイヤ化を使ったシーンのレンダリングにおいて、この設計により処理が 10 倍~100 倍速くなります。これほどの差が生まれる理由は、シーンが幾何学的にレイ数が増大する性質を持っていることと、目に見える部分にのみ時間を使って処理していることです。

ライト サンプルの再利用

レイと同様に、従来のライト ループで実現するライト サンプリングでも問題が起きる可能性があります。

それぞれがライト ループを含んでいるシェーダを 3 つ重ねると、ライト ループは 3 回発生します。ライト ループを伴う実行では、ライト サンプルごとにシャドウ レイのトレーシングが発生し、この処理にかかる時間がすぐに膨大になる可能性があります。ほとんどのシーンにおいて、結局はシャドウ レイの数が物を言います。これは、すべての種類のレイが(環境レイでさえ)、シャドウ レイのトレーシングを引き起こすからです。

この問題は、シェーダのレイヤ化に特化した API を使ってライト サンプルを用意することで解決できます。この方法は、ライト サンプルを保管しておき、実行指令が来たときにのみライトのサンプリングを再実行します。指令がないときは、最初にループを使って実行したときに保管しておいたライト サンプルを自動的に再利用します。こうすることで、シャドウ レイが何度もトレースされることなく、各コンポーネントでライト サンプルを反復適用し、値を再利用できます。

透明が多く含まれているシーンでは、速度の差が大きくなります(2 ~ 20 倍)。これは、各シャドウ レイが多数の小さなレイ セグメントに分解されてしまい、それだけパフォーマンスが低下するためです。

複数出力および LPE

単一出力のシェーダ コンポーネントを単純に組み合わせる方法は簡単ですが、より複雑な複数出力のコンポーネント シェーダからの出力が無視される可能性が高くなります。

別の数学計算ノードを介して二次出力を組み合わせることができる一方、シェーダによって出力のスタイルも異なるため、そもそも正規に混合できるのは特定の種類の出力のみです。シェーダのネットワークが複雑になると、実際のプロジェクトの維持も難しくなります。

複数の LPE パス

レイヤ化シェーダ専用の API では、各コンポーネント シェーダ内からさまざまなライト パス専用のフレームバッファへの出力になります。その使用に関する仕様は、NVIDIA のマテリアル定義言語(MDL)で導入されたライト パス エクスプレッション(LPE)が基になっています。指定したライト パスから集められた光は、ユーザのフレームバッファ内に蓄積している可能性があります。サポートされている特定のライト パスがどのユーザ フレームバッファに書き込まれる可能性があるかは、文字列オプションを使って特定できます。サポートされている LPE パスの詳細は、ここをクリックしてください。

出力はウェイトを付けた後に書き込まれます。これにより、パスは加算パスです。言い換えると、すべてのパスが足し合わせることで結合できます。他のシェーダ(mia_material など)には「raw」と「level」というスタイルの出力を持つものが多く、これらは単一シェーダの出力には有用であるものの、結合はできません。「raw」や「level」の値の多重化を合成に延期することは簡単ではありません。サンプルをピクセルにフィルタ化する行為は非可逆の数学的複雑さを招き、後工程での対処が難しくなるためです。

フレームバッファと透明度

フレームバッファ」でも説明していますが、フレームバッファの書き込みでは、透明度も処理する必要があります。

これを達成するために、フレームバッファに書き込むシェーダでは、mi_trace_transparent() の呼び出しから得られる値をブレンドする必要があります。しかし、mi_trace_transparent() への呼び出しがシェード ツリーのリーフ シェーダに存在する場合、これを適切な順序でコントロールすることはできません。シェード ツリーに mi_trace_transparent() への複数の異なる呼び出しが存在する可能性があるためです。

望ましいフレームバッファの書き込みは、「フレームバッファ」のページに説明されている方法です。これは mila_material で特殊な処理を行って計算されます。具体的には次のとおりです。

フォトン シェーダとシャドウ シェーダ

mental ray では、法線サーフェス シェーダに対する動作がフォトン シェーダとシャドウ シェーダとで異なります。このため、2 つのサーフェス シェーダの出力を単に線でつなぐだけでは、それは正規なフォトン シェーダではなく、無理に使用しても実用にはなりません。また、シャドウ シェーダとして機能するかどうかも不明です(運次第)。

レイヤ化シェーダでは、フォトンとシャドウのシェーダ関数を別々にして、コンポーネントの機能を簡素化しています。