ConeTracingについて

Cone Tracingについて少し考えてみたいと思います。VoxelのOctreeの処理については考慮しません。

参照

[Implementing Voxel Cone Tracing. Simon Yeung]
[Non-interleaved Deferred Shading of Interleaved Sample Patterns, Segovia06]
The Technology Behind the 3D Graphics and Games Course “Unreal Engine 4 Elemental demo”

Coneの角度の決め方

参照に挙げた、SimonさんのBlogの解説では、ジオメトリの法線方向を中心に6方向のConeTracingを行う方法を用いています。Coneの角度は60度だそうです。60度のConeは、3つ並べると、丁度180度となるため、法線方向に一つのConeを配置して、それを囲むように5方向のConeを配置することで、半球上の光をTracingするということのようです。

Coneの角度を決める際には、もっと汎用的な考え方もあると思います。
半球の表面積は2πなので、6回のConeTracingでこれを購うとすると、2π/6が一つのConeの受け持つべき面積です。言い換えると、半径の1/6の長さで球を切り取った部分(球冠)が、それに該当すると思います。これより求まるConeの角度は、acos(5/6)*2なので、67.1度となります。
これは、Coneの数で半球上の面積を割り、その面積から求めた角度なので、実際にこの角度のConeを半球状に、重複することなく配置することは不可能です。しかし、ConeTracingのConeは、概念的な存在で、Voxel空間を正確にConeの形状で切り取るわけではありません。したがって、このような考え方も十分通用すると思います。

Voxel内のSampling位置の決め方

次に、ConeTracingする際の、Voxel内のSampling位置の決め方を考えます。

まず、例として、Coneの角度が67.1度の場合を考えます。
Coneの角度が67.1度の場合は、0.5/tan(67.1/2)の位置で、Coneの直径が、ちょうど1になります。値は約0.75です。従って、Coneの直径が2になる場所は、1.5で、直径が4になるのは、3.0です。これ等の位置で、VoxelのそれぞれのLODをサンプリングすれば、Coneの形状に沿ったサンプリングになると思います。
この計算から明らかなように、Voxelの絶対的な大きさは影響せず、Coneの角度によってのみサンプリングの位置が決定します。この例のように、Cornの開度が大きい場合は、サンプリング回数は各LODにつき1回で、連続した空間をサンプリングすることができます。

次に、極端に細いConeの場合を考えます。
例として、角度が10度のConeを考えた場合、0.5/tan(10.0/2)の値は5.7になります。従ってceil(5.7)=6なので、LOD:0で6回サンプリングすれば良いわけです。5.7/6は0.95なので、LOD:0を0.95間隔で6回(0.95, 1.9, 2.85, 3.8, 4.75, 5.7)サンプリングします。
LOD:1は、距離5.7~11.4までをサンプリングします。ceil(5.7/2)=3、つまり3回サンプリングすれば良いわけです。5.7/3は1.9なので、LOD:1は,間隔1.9で3回(7.6, 9.5, 11.4)サンプリングを行います。以降の処理は同様で、目的の距離までサンプリングを行います。

結局のところ、想定したConeに則って、サンプリング位置とLODを決めれば良いだけで、上記も考え方の一つに過ぎません。Quad-Linear filteringを用いれば、各LODの中間の値もサンプリングすることができます。

Coneの角度と、サンプリング回数、サンプル距離、LODの関係

上記の通り、ConeTracingで周辺の情報をVoxelから集める場合、Coneの角度と、サンプリング回数、サンプル距離には密接な関係があります。迅速に遠くまでの情報を集めようとするときは、VoxelのLODを下げて、大きな間隔でサンプリングする必要があります。そのためには、Coneの開度は必然的に大きくなります。一方、精度の高い情報が必要な場合は、角度の小さなConeを用いないと、LODの急激な低下で、情報の精度が落ちてしまいます。
つまり、Coneの角度を決めてしまえば、情報の精度と、同一サンプリング回数で到達する距離は決定します。また、Coneの角度と必要到達距離を決めると、必要とするVoxelのLODのレベルが決定します。

Non-interleaved Deferred Shading of Interleaved Sample Patternsについて

隣接する場所で、同じ向きで、ConeTracingを行った場合、その結果は、おそらく似た値になることが予想されます。この隣接度合いが、Voxelのサイズに対して十分小さい場合は、その結果は、おそらく殆ど同じ値になると思います。これを、DiffuseのConeTracingに大胆に活用した例が、UE4のGIをはじめとした技法になります。
ここで使われる手法の元になるのが、Non-interleaved Deferred Shading of Interleaved Sample Patternsの論文で、簡単に言えば、Shadingの計算をインターリーブで行い、後でそれを合成するという方法です。これを応用し、ConeTracingのConeの向きをインターリーブして、結果を後で合成するようにします。
UE4の例では、G-Bufferを3×3ピクセルブロックに分割し、1-9のインデックスを割りあて、各インデックスで、それぞれ1方向にのみConeTracingを行います。Traceする9方向は、全球上にうまく分布するようにあらかじめ設定しておきます。ConeTracingが終わったら、各自の周辺ピクセルから、ConeTracingの結果を取得します。(UE4の例では周辺5×5ピクセルから情報を集めているそうです。)これによって、各ピクセルで、9方向のConeTracingの結果が手に入るわけです。
当然ながら、周辺のピクセルの深度値を見て、ピクセルが近いかどうかをチェックして使用する必要があります。また、周辺ピクセルの法線が未知なので、ジオメトリの法線を基準としたTracingは行えず、全球上をConeTracingする必要があります。加えて、この手法を用いる場合、Tracingの回数の自由度が少ないです。3×3を用いれば、Tracingの回数は9の倍数となり、4×4を用いれば16の倍数となります。しかし、全体でのConeTracingの回数は1/9もしくは1/16になります。
この手法では、Traceする方向とConeの角度が固定されるので、最適化も行いやすいと思います。たとえば、各ピクセルで、Voxelの各LODに逐次アクセスするのではなく、LOD:0に対して行うTracing処理をまとめて行い、その後、LOD:1以降のTracingへといった処理にすることも可能だと思われます。こうした場合の、Voxel(3Dテクスチャ)へのアクセスは局所化され、キャッシュのヒット率向上が望める場合もあると思われます。
ちなみに、ConeTracingのグリッドは必ずしも正方形である必要は無く、4×3のグリッドが用いられているケースもあるようです。このケースでは、12方向のTracingとなり、正20面体の頂点方向をそのままTracingの方向として使える利点がありそうです。

ところで(実はここからが本題)

ところで、全球上を9分割したConeTracingでは、具体的にどの方向にConeTracingをすればよいのでしょうか。
残念ながら、頂点数が9の正多面体は存在しないので、適当に求めることにします。具体的な求め方は、ベクトルの向きを反復的に修正して、各ベクトルがなるべく離れるように分布させるだけです。これをWebGLで作ってみました。Chromeを使って作りましたので、Chromeならたぶん動作すると思います。
wgtest
使い方は、ベクトルの本数を6~32の範囲で入力し、Magnitudeのスライダを適当に動かして、ベクトルの分布が適切になるのを待ちます。あとはShow Axis Valueのボタンを押せば、ベクトルが表示されます。マウスで適当にドラッグすれば回転させることができるので、好みの向きに合わせることができます。表示される角度の値は、最も隣接するベクトルとの成す角度の平均で、ConeTracingの際の角度の指標になると思います。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中