2. BDPT Note@CEDEC2015 2
という二つの方法がある。1 については GPU などを用いて高速化することができる。本セッションでは 2 について取り
上げ、パストレーシングよりも賢いアルゴリズムである BDPT について説明する。
BDPT の説明に移る前に、その基礎となるパストレーシングのアルゴリズム、インポータンスサンプリング (Importance
sampling)、マルチプルインポータンスサンプリング (Multiple importance sampling, MIS) について説明する。
1.2 Implementation #1
レンダリング方程式の説明は省略するが、パストレーシングの 1 フレーム (ステップ) の計算は List. 1 に示すように
実装できる。このコードではそれぞれのピクセル (i, j) に対して、ループを回し、まずカメラからのレイであるプライマ
リーレイを生成する。そしてそのレイとシーンとの交差を求める。レイが何にもヒットしていなければ次のピクセルの
処理に移り、ヒットしたものが発光している表面ならば、ピクセルにその色を書き込む (これを経路を有効なパスが見つ
かったという。)。発光していない表面ならば、次のレイを生成する。ここでは randomSample() という関数でランダム
な方向を全球上から求め、レイを生成する。そして行 7 に戻り、そのレイとシーンとの交差を求める処理からの処理を
最大のレイの深さ (maxDepth) になるまで繰り返す。
1 f o r ( i n t i =0; i<nx ; i++) f o r ( i n t j =0; j<ny ; j++)
2 {
3 Ray ray = genPrimaryRay ( i , j ) ;
4
5 f l o a t 4 c o e f f = 1. f ;
6 f l o a t 4& output = p i x e l [ i , j ] ;
7 f o r ( i n t depth =0; depth<maxDepth ; depth++)
8 {
9 Hit h i t = i n t e r s e c t ( ray ) ;
10 i f ( ! h i t . hasHit () ) break ;
11
12 i f ( h i t . isEmissive () )
13 {// I m p l i c i t Connection
14 output += c o e f f ∗ getEmission ( h i t ) ;
15 break ;
16 }
17
18 ray , f , pdf = randomSample ( h i t ) ;
19 c o e f f ∗= f ∗ dot ( h i t . m n , ray . m dir ) / pdf ;
20 }
21 }
Listing 1: パストレーシング実装 #1
この実装は簡単であるが、効率が悪くノイズがなかなか消えない。つまり 1 フレームのレンダリングで得られる結果
がノイジーである。これは主に List. 1 行 18 の randomSample() が原因である。randomSample() は全球上からランダ
ムに方向を求めた。しかし光を透過しない表面ではレイが入射した側と反対側には光は届かない。したがってレイが入
射した側の半球上からランダムに次のレイの方向を求めれば改善できる。
この実装を用いて様々な種類の BRDF を含むシーンをレンダリングすると、ある表面上はノイズがなかなか消えない
ことに気づく。マットな表面上ではそこそこ早くノイズが消えるが、光沢がある表面上ではノイズがなかなか消えない。
これは光沢がある表面での反射特性によるものである。様々な BRDF の反射特性が知られており、それを用いて次のレイ
の方向を求めると光沢のある表面でもより効率的にレンダリングができるようになる。Fig. 2 に様々な表面の例を示す。
1.3 Implementation #2 (BRDF Importance Sampling)
鏡面反射のような表面では半球上からランダムに方向を決めて次のレイの方向を求める方法では、有効な反射方向を
求めることが難しい。しかし鏡面反射する表面ではレイが反射する方向が入射方向に対して一意に決まる。よって鏡面
反射する表面ではその方向のみにレイを飛ばせばよい。
光沢のある表面は、鏡面反射する表面とマットな表面の間のような特性があり、反射する方向は、鏡面反射方向の周
りにコーンを作り、その中の方向に強く反射する。よってそのコーンの中からランダムな方向を求めれば効果的なレイ
の方向を求めることができる。このように BRDF の性質を用いて反射するレイの方向を求めることを BRDF を用いた
インポータンスサンプリングと呼ぶ (BRDF の分布を考慮したインポータンスサンプリング)。
疑似コードを List. 1 に示すが List. 2 との違いは行 18 だけであり、List. 2 では randomSample() の代わりに
brdfSample() を用いている。
3. BDPT Note@CEDEC2015 3
図 2: 様々な表面。
1 f o r ( i n t i =0; i<nx ; i++) f o r ( i n t j =0; j<ny ; j++)
2 {
3 Ray ray = genPrimaryRay ( i , j ) ;
4
5 f l o a t 4 c o e f f = 1. f ;
6 f l o a t 4& output = p i x e l [ i , j ] ;
7 f o r ( i n t depth =0; depth<maxDepth ; depth++)
8 {
9 Hit h i t = i n t e r s e c t ( ray ) ;
10 i f ( ! h i t . hasHit () ) break ;
11
12 i f ( h i t . isEmissive () )
13 {// I m p l i c i t Connection
14 output += c o e f f ∗ getEmission ( h i t ) ;
15 break ;
16 }
17
18 ray , f , pdf = brdfSample ( h i t ) ;
19 c o e f f ∗= f ∗ dot ( h i t . m n , ray . m dir ) / pdf ;
20 }
21 }
Listing 2: パストレーシング実装 #2
1.4 Implementation #3 (Light Importance Sampling)
この実装を用いて様々なシーンをレンダリングすると、マットな表面やそれに近い鈍い光沢のある表面を小さな光源で
照らしたときにノイズが長く残ることに気づく。この原因はマットな表面で半球上にランダムにレイを飛ばすと、レイ
はほとんどライトに当たらないからである。このような場合はランダムにレイを飛ばすより、ライトの形状がわかって
いるので、そのライトの方向に重点的にレイを飛ばしたほうが効率良くレンダリングを行うことができる。このような
レイのサンプリング方法をライトを用いたインポータンスサンプリングという (ライトの分布を考慮したインポータンス
サンプリング)。
Fig. 3 にあるシーンを Sec. 1.3 で説明した BRDF を用いたインポータンスサンプリングを用いてレンダリングした結
果と、ライトを用いたインポータンスサンプリングを用いてレンダリングした結果の比較を示す。16spp を用いてレンダ
リングした結果を見ると後者の方がマットな表面でのノイズが少ないことがわかる。
List. 3 にライトを用いたインポータンスサンプリングを行う疑似コードを示す。
1 f o r ( i n t i =0; i<nx ; i++) f o r ( i n t j =0; j<ny ; j++)
2 {
3 Ray ray = genPrimaryRay ( i , j ) ;
4
5 f l o a t 4 c o e f f = 1. f ;
6 f l o a t 4& output = p i x e l [ i , j ] ;
7 f o r ( i n t depth =0; depth<maxDepth ; depth++)
8 {
9 Hit h i t = i n t e r s e c t ( ray ) ;
10 i f ( ! h i t . hasHit () ) break ;
11 {// E x p l i c i t Connection
12 ray , pdf = lightSample ( h i t ) ;
13 Hit hit1 = i n t e r s e c t ( ray ) ;
4. BDPT Note@CEDEC2015 4
図 3: サンプリング方法の比較。
14 i f ( ! hit1 . hasHit () )
15 {
16 output+=c o e f f ∗ getEmission ( hit1 ) / pdf ;
17 }
18 }
19 ray , f , pdf = brdfSample ( h i t ) ;
20 c o e f f ∗= f ∗ dot ( h i t . m n , ray . m dir ) / pdf ;
21 }
22 }
Listing 3: パストレーシング実装 #3
List. 2 と List. 3 の二つの疑似コードを見比べると、List. 2 で有効な経路を作成していた行 18 が List. 3 のコードに
はないことがわかる。この行を List. 3 に追加してしまうと、レンダリング結果画像のピクセルの色が 2 倍になってしま
う。これはそれぞれのコードが有効な光の経路を違った方法で見つけているだけであるためである。List. 2 と List. 3 を
長い時間計算すると同じ画像が得られる。つまりこれは List. 2 で見つけた光の経路を List. 3 でも見つけられていると
いうことに他ならない。
しかしせっかく List. 2 で見つけている有効な光の経路を List. 3 では無視している。これは直感的に無駄なように思
え、実際無駄である。それでは両方のテクニックで見つけた光の経路を有効に活用するにはどうすれば良いだろうか。
1.5 Multiple Importance Sampling
先ほど両方のテクニックでレンダリング結果は Fig. 3 に示すように最終的に同じになり、両方の結果を足し合わせる
とピクセルの色が 2 倍になってしまうと述べた。それでは単純に両方の結果を足し合わせ、結果を 2 で割ってはどうだ
ろうか。この方法を用いると両方のテクニックで見つけた有効な光の経路を活用することができる。しかし同じ数のサ
ンプルを用いてレンダリングをした結果 (Fig. 3) を見るとライトを用いたインポータンスサンプリングの方が画像のノ
イズが少ないように見える。よって単に平均を取るよりも、ライトを用いたインポータンスサンプリングの結果により
高い重みをつけて足し合わせた方が良さそうである。重みの比はどの値が最適なのであろうか。
2 つのテクニックを用いてレンダリングした結果をもう少し詳しく見てみると、重みをどのようにデザインすればいい
かについて手がかりが得られる。Fig. 4 に光沢の鋭さを変えた物体をいくつか配置してレンダリングした結果を示す。こ
の図を見ると光沢が鈍い表面ではライトを用いたインポータンスサンプリングの方がノイズが少なく、光沢が鋭い表面
では BRDF を用いたインポータンスサンプリングの方がノイズが少ないということがわかる。つまり、レンダリングし
ている全領域でどちらのテクニックが優れているということはできず、表面の光沢の度合い、より一般化すると BRDF
によってそれぞれのテクニックにおける収束の速度が違うということがわかる。
それでは Fig. 5 のように重みをピクセルごとに変えてみてはどうだろうか。そうすることで 2 つのテクニックを最適
に組み合わせることができそうである。ここで気をつけたいのは、ピクセルにおいて重みは一定ではないが、それぞれ
のピクセルにおいて両方の重みの和を取ると 1 になるということである。このようにピクセルごとの重みを変えて結果
を足し合わせることをマルチプルインポータンスサンプリングと言う。
5. BDPT Note@CEDEC2015 5
図 4: 様々な表面でのサンプリング方法の比較。
図 5: マルチプルインポータンスサンプリングの可視化。
1.6 Implementation #4 (Multiple Importance Sampling)
ではそれぞれのピクセルの重みをどのようにデザインすれば良いだろうか。これを理解するにはなぜ別のテクニック
でレンダリングした結果が違ったものになるのかを理解することが必要になる。2 つのテクニックの違いは光の経路の最
後の線分を生成する方法が違うことに起因する。BRDF を用いたインポータンスサンプリングを用いると最後の線分は、
BRDF の反射特性を用いて反射するレイを生成する分布を作っているのに対し、ライトを用いたインポータンスサンプ
リングを用いると、ライトの形状を用いて反射するレイを生成する分布を作っている(少し難しくなってきましたね)。
つまり 1 つの線分だけ見てみるとその線分が生成される信値 (Probability density function をわかりやすく言い換えた)
が違い、それによって違う結果が得られているということである。一般的に言うと信値が高い方法で生成されたパスの
方がノイズが少ないことが多い。よってこのパスを生成する震度を用いて重みを決定する方法が一般的である。
マルチプルインポータンスサンプリングを用いたアルゴリズムを List. 4 に示す。行 15 と 24 においてそれぞれのテク
ニックの重みを計算している。
1 f o r ( i n t i =0; i<nx ; i++) f o r ( i n t j =0; j<ny ; j++)
2 {
3 Ray ray = genPrimaryRay ( i , j ) ;
4
5 f l o a t 4 c o e f f = 1. f ;
6 f l o a t 4& output = p i x e l [ i , j ] ;
7 f l o a t pdfb = 0. f ;
8 f o r ( i n t depth =0; depth<maxDepth ; depth++)
9 {
10 Hit h i t = i n t e r s e c t ( ray ) ;
11 i f ( ! h i t . hasHit () ) break ;
12 i f ( h i t . isEmissive () )
13 {// I m p l i c i t Connection
14 pd fl = lightPdf ( h i t ) ;
15 w = pdfb / ( pdfb + pdfl ) ;
16 output += c o e f f ∗ getEmission ( h i t ) ∗ w;
17 break ;
18 }
19 {// E x p l i c i t Connection
20 ray , pdfl = lightSample ( h i t ) ;
21 Hit hit1 = i n t e r s e c t ( ray ) ;
22 i f ( ! hit1 . hasHit () )
23 {
24 w = pdfl / ( pdfb + pdfl ) ;
6. BDPT Note@CEDEC2015 6
(a) (b) (c)
図 6: パストレーシングを用いて同じ条件下でレンダリングした画像の例。
図 7: パストレーシングを用いてレンダリングした画像の例。
25 output += c o e f f ∗ getEmission ( hit1 ) ∗ w / pdfl ;
26 }
27 }
28 ray , f , pdfb = brdfSample ( h i t ) ;
29 c o e f f ∗= f ∗ dot ( h i t . m n , nextRay . m dir ) / pdfb ;
30 }
31 }
Listing 4: パストレーシング実装 #4
2 Beyond Path Tracing
2.1 Path Tracing Good & Bad
ここで Fig. 6 に示すパストレーシングを用いてレンダリングした結果を 3 つ示す。これらのレンダリング結果は実は
同じ数のサンプルを用いて計算されたものである。それにもかかわらずこれらを見比べるとノイズの強さが違うことに
気づくだろう。Fig. 6(a) は少なく、Fig. 6(b) はノイズが少し増え、Fig. 6(c) はとてもノイズが多い。Fig. 6 からパス
トレーシングのシーンにおける得意、不得意がよく分かる。直接光からの寄与が大きい Fig. 6(a) のような Outdoor の
シーンでは得意であるが、Fig. 6(c) のような Indoor のシーンは不得意であることがわかる。関節光がシーンの大半を照
らしている Fig. 6(c) は長く計算を行わなければ、ノイズが気にならないレベルまで落ちない。これは何故かというと関
13. BDPT Note@CEDEC2015 13
Algorithm LCV-BDPT 1: Light Vertex Cache BDPT
// Preparation phase
pathLength = 0;
foreach path in 10k light paths do
while path not terminated do
trace path.ray;
if no hit then
return;
pathLength++;
path.ray = nextRay;
averageLength = pathLength / 10k;
LVCache = reserve |nLightPaths| · (averageLength + 1) · 1.1;
connections = max(1, ⌈ averageLength + 1 ⌉);
// Light ray tracing
vertexIndex = 0;
foreach path in light paths do
while path not terminated do
LVCache[vertexIndex] = path start point; // On a light source
trace path.ray;
if no hit then
return;
LVCache[vertexIndex++] = path.vertex;
path.ray = nextRay;
// Camera ray tracing
foreach path in camera paths do
while path not terminated do
trace path.ray;
if no hit then
return;
repeat
path connects to LVCache[random];
until connections;
path.ray = nextRay;
15. BDPT Note@CEDEC2015 15
Implementation LVC-BDPT 2: Light Vertex Cache BDPT implementation outline
// Light ray tracing
maxDepth = averageLength;
setupLightRay(); // Generate light rays
explicitConnectinoLightToCamera(); // Connect light source to camera
for pathLength = 1 to maxDepth do
castRay();
sampleBrdf();
if pathLength == maxDepth then
break;
explicitConnectionToCamera(); // Connect light subpath to camera
sampleSurfaceSecondary();
cacheLightVertex(); // Cache current vertex data
// Camera ray tracing
maxDepth = maxRecursino;
setupCameraRay();
for pathLength = 1 to maxDepth do
castRay();
sampleBrdf();
if pathLength == 1 then
initializeCameraPathMisWeightTerm();
else
calcCameraPathMisWeightTerm();
implicitConnection();
if pathLength == maxDepth then
break;
explicitConnectionToLight(); // Connect camera subpath to light source and light subpath
sampleSurfaceSecondary();
updateCameraPathMisWeightTerm();
19. BDPT Note@CEDEC2015 19
おわりに
今回はじめて CEDEC の講演の追加資料をまとめてみました。どうでしたか。コメントやアドバイス等ありましたら、
takahiroharada at gmail dot com までお願いします。
参考文献
[Antwerpen, 2011] Antwerpen, D. V. (2011). Recursive MIS Computation for Streaming BDPT on the GPU. PhD thesis.
[Bogolepov et al., 2013] Bogolepov, D., Ulyanov, D., Sopin, D., and Turlapov, V. (2013). GPU-Optimized Bi-Directional Path Tracing. In
WSCG 2013: poster proceedings: 21st International Conference in Central Europe on Computer Graphics, Visualization and Computer
Vision in co-operation with EUROGRAPHICS Association, pages 57–60. V´aclav Skala - UNION Agency.
[Davidoviˇc et al., 2014] Davidoviˇc, T., Kiv´anek, J., Haˇsan, M., and Slusallek, P. (2014). Progressive Light Transport Simulation on the
GPU. ACM Transactions on Graphics, 33(3):1–19.