Shade3Dでのレイトレーシングについて

Shade3Dでは複数のレンダリング手法を選択できます。
それぞれ目的に合わせて選択することになりますが、目的に合わないのを選ぶとレンダリングに時間がかかったり目的の品質にならないということになる場合があります。
ここでは目的別にレンダリング手法のどれを選べばよいか、最適化はどうすればいいか、というレンダリングに特化した内容を書いていきます。

今回はもっとも基本となる「レイトレーシング」について説明します。

レンダリング手法の選択

イメージウィンドウのレンダリング設定を開き、「手法」ドロップダウンリストからレンダリング手法を選択できます。

全グレードで、以下を選択できます。

  • レイトレーシング(ドラフト)
  • レイトレーシング
  • パストレーシング
  • ワイヤーフレーム

Standard/Professional版では、以下が追加で選択できます。

  • トゥーンレンダラ

レイトレーシング」とは ?

Shade3Dのレンダリング手法は、
「ワイヤーフレーム」を除いては「レイトレーシング」という理論によりレンダリングが行われます。

レイトレーシングとは日本語では「光線追跡法」と書かれることもあります。
もっとも古典的なレイトレーシングは、
視点(カメラ)からスクリーンに対して光線(レイ)を飛ばし、物体にぶつかる場合にそのときのスクリーン上の最終的なピクセルの色を計算します。

上図の場合、球の衝突と地面への衝突の2レイ分の矢印を赤色で表現しています。
これがレイトレーシングの第一歩になります。

シェーディング

物体に衝突した場合はそのときの面に垂直な方向(法線)を求め、光源の位置を元に輝度計算を行います。

物体の表面材質の情報により表面の色を計算していく工程を「Shading : シェーディング」と呼びます。
上図の場合は、球上の衝突位置と光源の間に物体は存在しません。
この場合は影はできないです。

以下のように地面へのレイを飛ばした場合、この時の衝突位置と光源の間は球が存在します。

この場合は、この位置は影(Shadow : シャドウ)になります。

陰(Shade)と影(Shadow)の違い

日本語では、陰(Shade)と影(Shadow)は同じ「カゲ」になりますがこれは区別されます。
陰(Shade)は、他の物体にさえぎられることなく暗くなる表現を指します。
この陰は、衝突位置での法線ベクトル(面に垂直な方向)と、衝突位置と光源を結ぶ光線ベクトルが作る角度より計算されます。
当然ながら、面の真正面から光線が入った状態(角度差が0)が一番明るくなりますよね。
一方、影(Shadow)は他の物体にさえぎられて暗くなる表現を指します。

反射

衝突した物体が鏡面の反射をする場合、レイは衝突位置から反射します。
そして、さらに反射物体にぶつかった場合は再び反射することになります。

透過、屈折

衝突した物体が透過または屈折する場合、レイは衝突位置から屈折率に従って物体内部に進みます。
そして、レイは物体外部に出ます。
屈折率が1.0の場合はそのまま直進することになります。

このように、レイトレーシングの場合はスクリーンにレイを飛ばした後に
「影計算」「反射」「透過、屈折」それぞれに対して再度レイを飛ばしていくことになります。
これはレイを飛ばして衝突判定後に衝突位置を開始位置として次のレイを飛ばす、と順番に判断していきます。

スクリーンのピクセル数分レイを飛ばし、さらに衝突時の物体の材質により何回かレイをたどるのはすごく計算時間がかかります。
実際は1ピクセルに対して複数のレイを飛ばすことでアンチエイリアスを表現しますので、スクリーンのピクセル数以上の計算が発生することになります。

このようにレイトレーシングは膨大な計算が発生するため、
内部的には計算時間を短縮する最適化が行われています。

レイトレーシングでの最適化

空間分割

レイと物体の交差判定は、最終的には物体の面(三角形)として交差計算が行われます。
シーン全体の三角形がレイと交差するかループでチェックすると膨大な量になります。
例えば、以下のように6つの物体があるシーンでレイ(始点と向きを持つ)がどの物体と交差するか考えます。

上図の場合は総当たりだと、最大6回の交差計算が必要となります。
これだと物体が増えれば増えるほど計算時間がかかるのが分かりますね。

そこで、複数の物体が直方体(AABB(Axis Aligned Bounding Box)と呼びます)内に収まるようにグループ分けします。

この場合、シーン全体を囲むAABB(B1)から開始し、C1-C2に空間を分離。
C1はD1-D2に空間を分離、C2はE1-E2に空間を分離、と分けていきます。
以下のような二分木構造となりました。

この場合、レイを飛ばしたときにB1のAABBとの衝突がない場合は、
どの形状とも衝突しないということになります。
B1衝突する場合は、C1とC2との衝突を行い、レイの交差順を計算(ここではC1-C2)。
C1の中でD1、D2と衝突するか判定。ここではどちらとも衝突しないのでスキップ。
C2の中でE1とE2のどちらと衝突するか判定。E1と衝突するのでその中の物体と衝突判定、のように走査します。
レイとAABBとの交差判定は、レイと三角形/球などの交差判定と比べてはるかに計算負荷が軽いため、
不要な計算は二分木で「刈っていく」ことができます。
上記の場合は、AABBとの交差判定を除くとE1内のAABBの2つの物体との交差判定を行う、という最適化ができたことになります。

Shade3Dの場合は、BVH(Bounding Volume Hierarchy)という空間分割方法を使用しています。
その他、kd-treeやBSP、八分木など空間分割方法は様々あります。
この空間分割の最適化が、レイトレーシングの速度を決める重要な要素となります。

計算の打ち切り

レイが反射や透過/屈折を経由して順番にシーンをたどる際に、
「レンダリング設定」の「その他」タブの「視線追跡レベル」の回数レイをたどってそれでも交差がある場合は計算を打ち切ります。

これも最適化の1つと言えます。

また、アンチエイリアス処理は1ピクセル内を複数回レイトレーシングします。
このとき、隣接するピクセルと色が近い場合は計算を打ち切ります。
色の濃淡が激しい物体のエッジ部はサンプリングがより多く行われますが、緩やかなグラデーションの箇所などは計算がスキップされます。

これにより速度アップすることになります。
Shade3Dでは複数のアンチエイリアシングの方法を選べるため、それによって最適化は変わります。

間接照明を使ったレンダリングの場合はまた別の最適化が存在します。
これらは追って説明していきます。

レンダリングで計算時間を短縮するには ?

Shade3Dを使うにあたって、レイトレーシングで計算時間が増加する要因は
主に以下があります。

  • シーン上の形状数(面数)が多い場合
  • 光源数が多い場合
  • 材質で反射、透過、屈折が多い場合

逆に言うと、上記を回避することでレンダリング速度が上がることになりますね。
レンダリングでの最適化は以下のようになるでしょうか。

  • シーンの形状数を減らす。
  • 自由曲面だと細かい面の分割になるので、必要性があまりない形状は分割を粗くする。
  • 視点から離れている形状は粗くする。木を板ポリゴンで表現するなど。
  • 不要な光源を削除。
  • 表面材質の反射/透過/屈折が不要な場合は使用しない。

今回は大雑把に列挙しただけですが、後々サンプルシーンで最適化については解説予定です。

ゲームエンジンだとリアルタイムだしきれい。違いは ?

ところで、Unityなどのゲームエンジンだとほぼ同一のレンダリング結果なのにリアルタイムに表示できたりしてます。
昨今はリアルタイムのほうのレベルが上がったので区別が難しい面があります。
リアルタイムのレンダリングでは「レイトレーシング」を使用していない、というのが大きな違いです。
「ラスタライズ」と言って、シーンの物体を構成する三角形をスクリーン座標に変換後、視点から見た奥行き(Z値と言います)を考慮して描いています。
下図は、1つの三角形を上から下に走査しつつ
水平の1ラインが三角形と交差する始点と終点を求めて描画していく(スキャンライン)様子です。

Z値を格納しているZバッファにより、隠面処理は自動で行われます。
これをGPUのハードウェアでいくつも並列して描画するため速いということになります。
透明体は別途奥から順番に並べ替えて描く、などします。
レイトレーシングよりも厳密な反射や透過/屈折表現をしてるわけではない、
そもそも三角形とレイとの交差判定自体をしていないので速いのです。
ただ、シェーディング計算もシェーダー(Shader)としてハードウェアで計算してしまったりしますので、
反射/透過/屈折がない場合はほぼレイトレーシングと違いは分からないです。
なお、リアルタイムでの影計算はシャドウマップを使用します。
レイトレーシングはシンプルな考え方なのですが、
ハードウェアのレンダラはいくつものテクニックを使って実装されています。

ということで、今回はレイトレーシングの基礎について説明しました。
次回は、間接照明を使った場合のレンダリングの考え方を説明予定です。

カテゴリー: レンダリング