-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.html
663 lines (572 loc) · 332 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
<!DOCTYPE html><html><head>
<title>bvh</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
<style>
/**
* prism.js Github theme based on GitHub's theme.
* @author Sam Clarke
*/
code[class*="language-"],
pre[class*="language-"] {
color: #333;
background: none;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.4;
-moz-tab-size: 8;
-o-tab-size: 8;
tab-size: 8;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: .8em;
overflow: auto;
/* border: 1px solid #ddd; */
border-radius: 3px;
/* background: #fff; */
background: #f5f5f5;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
background: #f5f5f5;
}
.token.comment,
.token.blockquote {
color: #969896;
}
.token.cdata {
color: #183691;
}
.token.doctype,
.token.punctuation,
.token.variable,
.token.macro.property {
color: #333;
}
.token.operator,
.token.important,
.token.keyword,
.token.rule,
.token.builtin {
color: #a71d5d;
}
.token.string,
.token.url,
.token.regex,
.token.attr-value {
color: #183691;
}
.token.property,
.token.number,
.token.boolean,
.token.entity,
.token.atrule,
.token.constant,
.token.symbol,
.token.command,
.token.code {
color: #0086b3;
}
.token.tag,
.token.selector,
.token.prolog {
color: #63a35c;
}
.token.function,
.token.namespace,
.token.pseudo-element,
.token.class,
.token.class-name,
.token.pseudo-class,
.token.id,
.token.url-reference .token.variable,
.token.attr-name {
color: #795da3;
}
.token.entity {
cursor: help;
}
.token.title,
.token.title .token.punctuation {
font-weight: bold;
color: #1d3e81;
}
.token.list {
color: #ed6a43;
}
.token.inserted {
background-color: #eaffea;
color: #55a532;
}
.token.deleted {
background-color: #ffecec;
color: #bd2c00;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
/* JSON */
.language-json .token.property {
color: #183691;
}
.language-markup .token.tag .token.punctuation {
color: #333;
}
/* CSS */
code.language-css,
.language-css .token.function {
color: #0086b3;
}
/* YAML */
.language-yaml .token.atrule {
color: #63a35c;
}
code.language-yaml {
color: #183691;
}
/* Ruby */
.language-ruby .token.function {
color: #333;
}
/* Markdown */
.language-markdown .token.url {
color: #795da3;
}
/* Makefile */
.language-makefile .token.symbol {
color: #795da3;
}
.language-makefile .token.variable {
color: #183691;
}
.language-makefile .token.builtin {
color: #0086b3;
}
/* Bash */
.language-bash .token.keyword {
color: #0086b3;
}html body{font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6;color:#333;background-color:#fff;overflow:initial;box-sizing:border-box;word-wrap:break-word}html body>:first-child{margin-top:0}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{line-height:1.2;margin-top:1em;margin-bottom:16px;color:#000}html body h1{font-size:2.25em;font-weight:300;padding-bottom:.3em}html body h2{font-size:1.75em;font-weight:400;padding-bottom:.3em}html body h3{font-size:1.5em;font-weight:500}html body h4{font-size:1.25em;font-weight:600}html body h5{font-size:1.1em;font-weight:600}html body h6{font-size:1em;font-weight:600}html body h1,html body h2,html body h3,html body h4,html body h5{font-weight:600}html body h5{font-size:1em}html body h6{color:#5c5c5c}html body strong{color:#000}html body del{color:#5c5c5c}html body a:not([href]){color:inherit;text-decoration:none}html body a{color:#08c;text-decoration:none}html body a:hover{color:#00a3f5;text-decoration:none}html body img{max-width:100%}html body>p{margin-top:0;margin-bottom:16px;word-wrap:break-word}html body>ul,html body>ol{margin-bottom:16px}html body ul,html body ol{padding-left:2em}html body ul.no-list,html body ol.no-list{padding:0;list-style-type:none}html body ul ul,html body ul ol,html body ol ol,html body ol ul{margin-top:0;margin-bottom:0}html body li{margin-bottom:0}html body li.task-list-item{list-style:none}html body li>p{margin-top:0;margin-bottom:0}html body .task-list-item-checkbox{margin:0 .2em .25em -1.8em;vertical-align:middle}html body .task-list-item-checkbox:hover{cursor:pointer}html body blockquote{margin:16px 0;font-size:inherit;padding:0 15px;color:#5c5c5c;border-left:4px solid #d6d6d6}html body blockquote>:first-child{margin-top:0}html body blockquote>:last-child{margin-bottom:0}html body hr{height:4px;margin:32px 0;background-color:#d6d6d6;border:0 none}html body table{margin:10px 0 15px 0;border-collapse:collapse;border-spacing:0;display:block;width:100%;overflow:auto;word-break:normal;word-break:keep-all}html body table th{font-weight:bold;color:#000}html body table td,html body table th{border:1px solid #d6d6d6;padding:6px 13px}html body dl{padding:0}html body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:bold}html body dl dd{padding:0 16px;margin-bottom:16px}html body code{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:.85em !important;color:#000;background-color:#f0f0f0;border-radius:3px;padding:.2em 0}html body code::before,html body code::after{letter-spacing:-0.2em;content:"\00a0"}html body pre>code{padding:0;margin:0;font-size:.85em !important;word-break:normal;white-space:pre;background:transparent;border:0}html body .highlight{margin-bottom:16px}html body .highlight pre,html body pre{padding:1em;overflow:auto;font-size:.85em !important;line-height:1.45;border:#d6d6d6;border-radius:3px}html body .highlight pre{margin-bottom:0;word-break:normal}html body pre code,html body pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}html body pre code:before,html body pre tt:before,html body pre code:after,html body pre tt:after{content:normal}html body p,html body blockquote,html body ul,html body ol,html body dl,html body pre{margin-top:0;margin-bottom:16px}html body kbd{color:#000;border:1px solid #d6d6d6;border-bottom:2px solid #c7c7c7;padding:2px 4px;background-color:#f0f0f0;border-radius:3px}@media print{html body{background-color:#fff}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{color:#000;page-break-after:avoid}html body blockquote{color:#5c5c5c}html body pre{page-break-inside:avoid}html body table{display:table}html body img{display:block;max-width:100%;max-height:100%}html body pre,html body code{word-wrap:break-word;white-space:pre}}.markdown-preview{width:100%;height:100%;box-sizing:border-box}.markdown-preview .pagebreak,.markdown-preview .newpage{page-break-before:always}.markdown-preview pre.line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}.markdown-preview pre.line-numbers>code{position:relative}.markdown-preview pre.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:1em;font-size:100%;left:0;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.markdown-preview pre.line-numbers .line-numbers-rows>span{pointer-events:none;display:block;counter-increment:linenumber}.markdown-preview pre.line-numbers .line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}.markdown-preview .mathjax-exps .MathJax_Display{text-align:center !important}.markdown-preview:not([for="preview"]) .code-chunk .btn-group{display:none}.markdown-preview:not([for="preview"]) .code-chunk .status{display:none}.markdown-preview:not([for="preview"]) .code-chunk .output-div{margin-bottom:16px}.scrollbar-style::-webkit-scrollbar{width:8px}.scrollbar-style::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}.scrollbar-style::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,0.66);border:4px solid rgba(150,150,150,0.66);background-clip:content-box}html body[for="html-export"]:not([data-presentation-mode]){position:relative;width:100%;height:100%;top:0;left:0;margin:0;padding:0;overflow:auto}html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{position:relative;top:0}@media screen and (min-width:914px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{padding:2em calc(50% - 457px)}}@media screen and (max-width:914px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{font-size:14px !important;padding:1em}}@media print{html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn{display:none}}html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn{position:fixed;bottom:8px;left:8px;font-size:28px;cursor:pointer;color:inherit;z-index:99;width:32px;text-align:center;opacity:.4}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] #sidebar-toc-btn{opacity:1}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc{position:fixed;top:0;left:0;width:300px;height:100%;padding:32px 0 48px 0;font-size:14px;box-shadow:0 0 4px rgba(150,150,150,0.33);box-sizing:border-box;overflow:auto;background-color:inherit}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar{width:8px}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,0.66);border:4px solid rgba(150,150,150,0.66);background-clip:content-box}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc a{text-decoration:none}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul{padding:0 1.6em;margin-top:.8em}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc li{margin-bottom:.8em}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul{list-style-type:none}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{left:300px;width:calc(100% - 300px);padding:2em calc(50% - 457px - 150px);margin:0;box-sizing:border-box}@media screen and (max-width:1274px){html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{width:100%}}html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .markdown-preview{left:50%;transform:translateX(-50%)}html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .md-sidebar-toc{display:none}
/* Please visit the URL below for more information: */
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
</style>
</head>
<body for="html-export">
<div class="mume markdown-preview ">
<h1 class="mume-header" id="bvh%E3%81%AE%E3%81%AF%E3%81%AA%E3%81%97">BVHのはなし</h1>
<p><a href="https://twitter.com/ShinjiOgaki">Shinji Ogaki</a></p>
<p>これは<a href="https://qiita.com/advent-calendar/2018/raytracing">レイトレ Advent Calendar 2018</a>の記事です。</p>
<p>最終更新日 24/Dec/2018</p>
<h2 class="mume-header" id="%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</h2>
<p>今後レイ・トレーシングは専用のハードウェアで行われていくようになるかもしれませんが、使用される基礎的なデータ構造を理解する人・研究をする人が減ってしまうのは残念なので、現在もっとも広く利用されているデータ構造であるBVHについてまとめてみることにしました。大まかなトレンドをつかんでいただければ幸いです。文献の数に圧倒されるかもしれませんが、Light Transportほど込み入った数学が必要ないので勉強は始めやすいのではないかと思います。</p>
<h2 class="mume-header" id="bvh%E3%81%A8%E3%81%AF">BVHとは?</h2>
<p>BVHはBounding Volume Hierarchyの略で、レイ・トレーシングの交差判定や物理シミュレーションの衝突判定の高速化で利用される階層的なデータ構造です。交差判定や当たり判定をするときにシーン内のオブジェクトを総当たりでチェックするわけにはいきませんから、階層的な構造でオブジェクトを扱い、レイが当たらないものをまとめて棄却する、という使い方をします。もちろんフォトンなどを保持するのにも使ってもいいですし、後ほど取り上げますが、最近ではメッシュ・ライトによる照明計算などにも使用されます。</p>
<h2 class="mume-header" id="%E3%82%B3%E3%82%B9%E3%83%88%E9%96%A2%E6%95%B0-cost-functions">コスト関数 (Cost Functions)</h2>
<p>品質のよいBVHを構築するのはなにかしら品質をはかる尺度が必要になりますが、まずはそこからはじめましょう。</p>
<h3 class="mume-header" id="surface-area-heuristic-sah">Surface Area Heuristic (SAH)</h3>
<p><a href="https://authors.library.caltech.edu/79167/">Automatic creation of object hierarchies for ray tracing</a></p>
<p><a href="http://graphicsinterface.org/wp-content/uploads/gi1989-22.pdf">Heuristics for ray tracing using space subdivision</a></p>
<p>この記事にたどり着いた人の多くはSAHという用語を聞いたことがあるのではないかと思います。Surface Area Heuristicの略で、レイ・トレーシング用の品質の良いBVHを構築する際の基準として広く用いられています。</p>
<p>SAHはレイの分布がシーン内で完全にランダムであると仮定したときの交差判定にかかる平均的なコストを表したもので、これが小さいほど品質のよいBVHであるといえます。具体的にどう定義されるかを見てみましょう</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mrow><mi>l</mi><mi>e</mi><mi>a</mi><mi>f</mi></mrow></msub><mo>(</mo><mi>X</mi><mo>)</mo><mo>=</mo><mi mathvariant="normal">∣</mi><mi>X</mi><mi mathvariant="normal">∣</mi><mo>⋅</mo><msub><mi>C</mi><mi>I</mi></msub><mspace linebreak="newline"></mspace><msub><mi>C</mi><mrow><mi>s</mi><mi>p</mi><mi>l</mi><mi>i</mi><mi>t</mi></mrow></msub><mo>(</mo><mi>X</mi><mo separator="true">,</mo><msub><mi>X</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>X</mi><mn>1</mn></msub><mo>)</mo><mo>=</mo><msub><mi>C</mi><mi>T</mi></msub><mo>+</mo><mi>P</mi><mo>(</mo><msub><mi>X</mi><mn>0</mn></msub><mi mathvariant="normal">∣</mi><mi>X</mi><mo>)</mo><mo>⋅</mo><msub><mi>C</mi><mrow><mi>l</mi><mi>e</mi><mi>a</mi><mi>f</mi></mrow></msub><mo>(</mo><msub><mi>X</mi><mn>0</mn></msub><mo>)</mo><mo>+</mo><mi>P</mi><mo>(</mo><msub><mi>X</mi><mn>1</mn></msub><mi mathvariant="normal">∣</mi><mi>X</mi><mo>)</mo><mo>⋅</mo><msub><mi>C</mi><mrow><mi>l</mi><mi>e</mi><mi>a</mi><mi>f</mi></mrow></msub><mo>(</mo><msub><mi>X</mi><mn>1</mn></msub><mo>)</mo></mrow><annotation encoding="application/x-tex">C_{leaf}(X)=|X| \cdot C_I \\ C_{split}(X, X_0, X_1)=C_T + P(X_0 | X) \cdot C_{leaf}(X_0) + P(X_1 | X) \cdot C_{leaf}(X_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">e</span><span class="mord mathit mtight">a</span><span class="mord mathit mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.07847em;">I</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">s</span><span class="mord mathit mtight">p</span><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">i</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">e</span><span class="mord mathit mtight">a</span><span class="mord mathit mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">e</span><span class="mord mathit mtight">a</span><span class="mord mathit mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>I</mi></msub></mrow><annotation encoding="application/x-tex">C_I</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.07847em;">I</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>はプリミティブとの交差判定のコスト</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi mathvariant="normal">∣</mi><mi>X</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">|X|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mord">∣</span></span></span></span>はリーフノード<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>X</mi></mrow><annotation encoding="application/x-tex">X</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.07847em;">X</span></span></span></span>に含まれるプリミティブの数</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mrow><mi>l</mi><mi>e</mi><mi>a</mi><mi>f</mi></mrow></msub></mrow><annotation encoding="application/x-tex">C_{leaf}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">e</span><span class="mord mathit mtight">a</span><span class="mord mathit mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>はノードに含まれるプリミティブ全てと交差判定を行うコスト</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>T</mi></msub></mrow><annotation encoding="application/x-tex">C_T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>はノードをたどるコスト(メモリ・アクセスとバウンディング・ボックスとの交差判定のコスト)</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mrow><mi>s</mi><mi>p</mi><mi>l</mi><mi>i</mi><mi>t</mi></mrow></msub></mrow><annotation encoding="application/x-tex">C_{split}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">s</span><span class="mord mathit mtight">p</span><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">i</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>はインナー・ノード(リーフ以外のノード)のコスト</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>P</mi><mo>(</mo><mi>Y</mi><mi mathvariant="normal">∣</mi><mi>X</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">P(Y|X)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.22222em;">Y</span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span></span></span></span>は条件付確率で、ノード<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>X</mi></mrow><annotation encoding="application/x-tex">X</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.07847em;">X</span></span></span></span>のバウンディング・ボックスにレイが当たり、さらに子ノード<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>Y</mi></mrow><annotation encoding="application/x-tex">Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.22222em;">Y</span></span></span></span>のバウンディング・ボックスにも当たる確率</li>
</ul>
<p>さて、レイの分布はシーン内で完全にランダムであると仮定したので、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>X</mi></mrow><annotation encoding="application/x-tex">X</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.07847em;">X</span></span></span></span>の表面積<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>A</mi><mo>(</mo><mi>X</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">A(X)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span></span></span></span>とすると、条件付確率が<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>P</mi><mo>(</mo><mi>Y</mi><mi mathvariant="normal">∣</mi><mi>X</mi><mo>)</mo><mo>=</mo><mfrac><mrow><mi>A</mi><mo>(</mo><mi>Y</mi><mo>)</mo></mrow><mrow><mi>A</mi><mo>(</mo><mi>X</mi><mo>)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">P(Y|X)=\frac{A(Y)}{A(X)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.22222em;">Y</span><span class="mord">∣</span><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.53em;vertical-align:-0.52em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.01em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">A</span><span class="mopen mtight">(</span><span class="mord mathit mtight" style="margin-right:0.07847em;">X</span><span class="mclose mtight">)</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.485em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">A</span><span class="mopen mtight">(</span><span class="mord mathit mtight" style="margin-right:0.22222em;">Y</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.52em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>と面積比のみで決められます。そうすると先の再帰的に定義されたコストは次のように展開できます。</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>C</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>r</mi><mi>o</mi><mi>o</mi><mi>t</mi></mrow></msub><mo>)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mi>A</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>r</mi><mi>o</mi><mi>o</mi><mi>t</mi></mrow></msub><mo>)</mo></mrow></mfrac><mrow><mo fence="true">(</mo><msub><mi>C</mi><mi>T</mi></msub><munder><mo>∑</mo><msub><mi>N</mi><mi>S</mi></msub></munder><mrow><mi>A</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>s</mi><mi>p</mi><mi>l</mi><mi>i</mi><mi>t</mi></mrow></msub><mo>)</mo></mrow><mo>+</mo><msub><mi>C</mi><mi>I</mi></msub><munder><mo>∑</mo><msub><mi>N</mi><mi>L</mi></msub></munder><mrow><mi>A</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>l</mi><mi>e</mi><mi>a</mi><mi>f</mi></mrow></msub><mo>)</mo></mrow><mo fence="true">)</mo></mrow></mrow><annotation encoding="application/x-tex">C(X_{root})=\frac{1}{A(X_{root})} \left( C_T \sum_{N_S}{A(X_{split})} + C_I \sum_{N_L}{A(X_{leaf})} \right)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2805559999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.02778em;">r</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:3.144641em;vertical-align:-1.394641em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.32144em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2805559999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.02778em;">r</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">(</span></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.050005em;"><span style="top:-1.855664em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.3567071428571427em;margin-left:-0.10903em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathit mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.14329285714285717em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.0500049999999996em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.394641em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">s</span><span class="mord mathit mtight">p</span><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">i</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.07847em;">I</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.050005em;"><span style="top:-1.855664em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.3567071428571427em;margin-left:-0.10903em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathit mtight">L</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.14329285714285717em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.0500049999999996em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.394641em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">e</span><span class="mord mathit mtight">a</span><span class="mord mathit mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size4">)</span></span></span></span></span></span></span></p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>X</mi><mrow><mi>r</mi><mi>o</mi><mi>o</mi><mi>t</mi></mrow></msub></mrow><annotation encoding="application/x-tex">X_{root}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2805559999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.02778em;">r</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>はルートノード</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>N</mi><mi>S</mi></msub></mrow><annotation encoding="application/x-tex">N_S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.10903em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>はBVH内に含まれるインナー・ノードの数</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>N</mi><mi>L</mi></msub></mrow><annotation encoding="application/x-tex">N_L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.10903em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight">L</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>はBVH内に含まれるリーフ・ノードの数</li>
</ul>
<p>ここで、リーフにはたった一つのプリミティブが含まれることにします。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>K</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">K_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>と<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>K</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">K_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>を定数とすると、先の式は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>C</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>r</mi><mi>o</mi><mi>o</mi><mi>t</mi></mrow></msub><mo>)</mo><mo>=</mo><msub><mi>K</mi><mn>1</mn></msub><msub><mo>∑</mo><msub><mi>N</mi><mi>S</mi></msub></msub><mrow><mi>A</mi><mo>(</mo><msub><mi>X</mi><mrow><mi>s</mi><mi>p</mi><mi>l</mi><mi>i</mi><mi>t</mi></mrow></msub><mo>)</mo></mrow><mo>+</mo><msub><mi>K</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">C(X_{root}) = K_1 \sum_{N_S}{A(X_{split})} + K_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2805559999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.02778em;">r</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">o</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.150015em;vertical-align:-0.400015em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop"><span class="mop op-symbol small-op" style="position:relative;top:-0.0000050000000000050004em;">∑</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.17862099999999992em;"><span style="top:-2.4002900000000005em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.3567071428571427em;margin-left:-0.10903em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathit mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.14329285714285717em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.400015em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord"><span class="mord mathit" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">s</span><span class="mord mathit mtight">p</span><span class="mord mathit mtight" style="margin-right:0.01968em;">l</span><span class="mord mathit mtight">i</span><span class="mord mathit mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>と書き換えることが出来ます。2つの定数はどのようにBVHを構築したとしても変えることが出来ません(実際はOpacityなどが適用されたりするのでプリミティブであっても正確なコストの予測は難しくなります)。すなわち、SAHを最小化することは、全てのインナー・ノードのバウンディング・ボックスの表面積の和を最小化することと等価になります。</p>
<p>分かりにくいと思われた方は、使われている全てのバウンディング・ボックスの表面積の和が小さければ小さいほど良いと覚えておけばよいでしょう。BVHの品質が良い・悪いというのは、SAHが小さい・大きいを意味しています。</p>
<p>後ほど取り上げますが、T-SAHなど時間軸に拡張したものも提案されています。</p>
<h3 class="mume-header" id="end-point-overlap-epo">End Point Overlap (EPO)</h3>
<p>SAHは最もよく使われますが、実際のレイ・トレーシングのパフォーマンスはSAHによって完全に表すことが出来ません。理由はいろいろですが、一つの理由としてノードどうしの重なりが挙げられます。</p>
<p><a href="https://research.nvidia.com/publication/quality-metrics-bounding-volume-hierarchies">On Quality Metrics of Bounding Volume Hierarchies</a></p>
<p>レイの始点あるいは終点が複数のノード内にあった場合には、それらすべてを訪れなければなりません。レイと交差する一番近いプリミティブは大抵、最も近いノード内に含まれますが、そうでない場合もあるので、確認作業が必要となります。</p>
<p>このノードの重なりによる追加分のコストをEPOで表します。</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>E</mi><mi>P</mi><mi>O</mi><mo>=</mo><munder><mo>∑</mo><mrow><mi>n</mi><mo>∈</mo><mi>N</mi></mrow></munder><mrow><mi>C</mi><mo>(</mo><mi>n</mi><mo>)</mo><mfrac><mrow><mi>A</mi><mo>(</mo><mo>(</mo><mi>S</mi><mo>∖</mo><mi>Q</mi><mo>(</mo><mi>n</mi><mo>)</mo><mo>)</mo><mo>∩</mo><mi>n</mi><mo>)</mo></mrow><mrow><mi>A</mi><mo>(</mo><mi>S</mi><mo>)</mo></mrow></mfrac></mrow></mrow><annotation encoding="application/x-tex">EPO= \sum_{n \in N}{C(n) \frac{A((S \setminus Q(n)) \cap n)}{A(S)}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.05764em;">E</span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mord mathit" style="margin-right:0.02778em;">O</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.7487060000000003em;vertical-align:-1.321706em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.050005em;"><span style="top:-1.8556639999999998em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight">n</span><span class="mrel mtight">∈</span><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span></span></span></span><span style="top:-3.0500049999999996em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.321706em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="mopen">(</span><span class="mord mathit">n</span><span class="mclose">)</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.05764em;">S</span><span class="mclose">)</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">A</span><span class="mopen">(</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∖</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathit">Q</span><span class="mopen">(</span><span class="mord mathit">n</span><span class="mclose">)</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∩</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathit">n</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></span></p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.05764em;">S</span></span></span></span>はシーン中の全ジオメトリ</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>Q</mi><mo>(</mo><mi>n</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">Q(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit">Q</span><span class="mopen">(</span><span class="mord mathit">n</span><span class="mclose">)</span></span></span></span>はノード<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathit">n</span></span></span></span>のサブツリーに属するジオメトリ</li>
<li><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mo>(</mo><mi>S</mi><mo>∖</mo><mi>Q</mi><mo>(</mo><mi>n</mi><mo>)</mo><mo>)</mo><mo>∩</mo><mi>n</mi></mrow><annotation encoding="application/x-tex">(S \setminus Q(n)) \cap n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∖</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit">Q</span><span class="mopen">(</span><span class="mord mathit">n</span><span class="mclose">)</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∩</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathit">n</span></span></span></span>は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathit">n</span></span></span></span>のサブツリーには属さないが、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathit">n</span></span></span></span>のバウンディング・ボックス内にあるジオメトリ</li>
</ul>
<p>EPOを加味したコストはSAHとの線形和<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mo>(</mo><mn>1</mn><mo>−</mo><mi>α</mi><mo>)</mo><mi>S</mi><mi>A</mi><mi>H</mi><mo>+</mo><mi>α</mi><mi>E</mi><mi>P</mi><mi>O</mi><mo separator="true">,</mo><mtext> </mtext><mn>0</mn><mo>≤</mo><mi>α</mi><mo>≤</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">(1-\alpha) SAH + \alpha EPO,\ 0 \leq \alpha \leq 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.0037em;">α</span><span class="mclose">)</span><span class="mord mathit" style="margin-right:0.05764em;">S</span><span class="mord mathit">A</span><span class="mord mathit" style="margin-right:0.08125em;">H</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathit" style="margin-right:0.0037em;">α</span><span class="mord mathit" style="margin-right:0.05764em;">E</span><span class="mord mathit" style="margin-right:0.13889em;">P</span><span class="mord mathit" style="margin-right:0.02778em;">O</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mspace"> </span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.7719400000000001em;vertical-align:-0.13597em;"></span><span class="mord mathit" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>となりますが、残念ながら<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.0037em;">α</span></span></span></span>はシーンに大きく依存します。また構築や最適化においてこれらのEPOを正確に見積もるのはコスト的に現実的ではないため、依然としてSAHが使われます。</p>
<h3 class="mume-header" id="surface-area-orientation-heuristic-saoh">Surface Area Orientation Heuristic (SAOH)</h3>
<p>メニー・ライトは最近ホットな話題です。メッシュ・ライトによる照明計算を高速化するためにもBVHが使用されます。</p>
<p><a href="https://www.highperformancegraphics.org/wp-content/uploads/2018/Papers-Session1/HPG2018_ImportanceSamplingManyLights.pdf">Importance Sampling of Many Lights With Adaptive Tree Splitting</a> <a href="https://www.highperformancegraphics.org/wp-content/uploads/2018/Papers-Session1/HPG2018_ImportanceSamplingManyLights.pdf">(Slides)</a></p>
<p>この論文ではSAOHと呼ばれる尺度を導入して、照明計算に特化したBVHを構築する方法が述べられています。名前のとおり、面の向きを考慮に入れてオブジェクトをグループに分けます。同じ向きに向いているポリゴンをまとめたほうが照明計算には都合がよさそうというのは直感的に分かりますね。</p>
<p>あるポリゴンが選択される確率(PDF)はBVHをトラバースしながら求めます。またMultiple Importance Samplingでレイが当たったポリゴンのPDFを求める必要があるため、BVHのノードに空きがあれば親のノードへの参照をストアしておくとよいでしょう。Refittingの並列化や最適化にも使えて便利です。</p>
<p>実際はBVHの深さと同じ長さのビット配列がリーフにストアされていれば親ノードへの参照は必要はありません。興味がある人は考えてみてください。</p>
<h2 class="mume-header" id="%E6%A7%8B%E7%AF%89-construction">構築 (Construction)</h2>
<p>構築方法はおおまかにトップダウン型とボトムアップ型に分かれます。</p>
<h3 class="mume-header" id="%E3%83%88%E3%83%83%E3%83%97%E3%83%80%E3%82%A6%E3%83%B3%E5%9E%8B-divisive">トップダウン型 (Divisive)</h3>
<p>現在の主流のトップダウン型では、与えられたプリミティブ全てをまず2つのグループに分け、それぞれのグループをさらに2つに分ける、という手順で構築します。この分割はグループに含まれるプリミティブの数が決められた数以下になるまで再帰的に行われます。</p>
<p>オブジェクトをグループに分ける方法は主に2通りあります。一つはObject Splittingと呼ばれるもので、もう一つはSpatial Splittingと呼ばれるものです。双方に長所と短所があります。</p>
<h4 class="mume-header" id="%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E5%88%86%E5%89%B2-object-splitting">オブジェクト分割 (Object Splitting)</h4>
<p>こちらは重複を許さずにオブジェクトをグループに分ける方法です。プリミティブは必ず右か左の子ノードどちらかに属します。また、重複を許さないためBVHで使用されるノードの数が事前に予測できメモリの管理が簡単です。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.10903em;">N</span></span></span></span>個のリーフがあった場合、ノードは最大でも<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">N-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathit" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>個となります。また、モーション・ブラーの実装が楽になるというメリットがあります。</p>
<p>グループに分ける際はSAHが最も小さくなるような分け方をしますが、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.10903em;">N</span></span></span></span>個のプリミティブを2つのグループに分ける組み合わせをすべて試すのは現実的ではありません。一般的には次にあげる2つの方法が使われます。</p>
<ul>
<li>
<p><strong>Sweep</strong><br>
全てのプリミティブの中心座標を分割したい軸方向にソートし、全てを走査して、コストが最小となる分割位置を探します。</p>
</li>
<li>
<p><strong>Binning</strong><br>
近似方法ですがSweepで分割した場合と遜色ないBVHを高速に作ることが出来ます。バウンディングボックスを分割したい軸方向に、例えば8や16といった限られた数に分割し、各領域(これをビンと呼びます)に入るプリミティブを数え、表面積を考慮し、どこで分割するかを決めます。プリミティブがどのビンに入るか判定する際にはプリミティブのバウンディング・ボックスの中心座標を使います。</p>
</li>
</ul>
<p>トップダウン型で構築を行う場合のTask Queueのサンプル・コードは<a href="https://github.com/shinjiogaki/bvh/blob/master/taskqueue.cpp">こちら</a>に置いてあります。</p>
<h4 class="mume-header" id="%E7%A9%BA%E9%96%93%E5%88%86%E5%89%B2-spatial-splitting">空間分割 (Spatial Splitting)</h4>
<p>こちらは重複を許してグループ分けするもので、大きなポリゴンなどを分割して(切断すると考えてよいでしょう)ぴったりとしたバウンディング・ボックスを作ることができるため、交差判定は非常に高速になります。切断されたプリミティブは、2つの子ノード両方に含まれることになるのでBVHのノード数は事前に予測することはできません。kd-treeも同様に空間を切断するので、レイ・トレーシングの速度は変わらないのではと思うかもしれませんが、SplitありのBVHは、何もない空間を一気にスキップすることができるという点で有利です。</p>
<p><a href="https://www.nvidia.com/docs/IO/77714/sbvh.pdf">Spatial Splits in Bounding Volume Hierarchies</a></p>
<p><a href="http://rapt.technology/data/pssbvh.pdf">Parallel Spatial Splits in Bounding Volume Hierarchies</a></p>
<p>Spatial Splitsを用いたBVHの構築を並列で高速に行う方法です。複数スレッドの管理が鍵となります。ルートに近いノードでパーティションを行うときは、複数のスレッドが協力して行います。ある程度細かくパーティションがなされたあとは、一つのスレッドがサブツリーの構築を行います。他にもSIMDを用いた高速化などが記述されています。</p>
<p><a href="https://jo.dreggn.org/home/2018_manuka.pdf">Manuka: A batch-shading architecture for spectral path tracing in movie production</a></p>
<p><a href="https://patents.google.com/patent/US20140362074A1/en">Splitting bounding volumes of primitives (US20140362074A1)</a></p>
<p>空間分割を用いると、モーション・ブラーの取り扱いと構築は共に複雑になります。入力されるオブジェクトが均等に細かく分割されている場合(映画のアセットなどはディスプレイスメント・マップやサブディビジョン・サーフェスによってメッシュが細かく分割されていることが多い)はメリットがほとんどありません。</p>
<h3 class="mume-header" id="%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E5%9E%8B-agglomerative">ボトムアップ型 (Agglomerative)</h3>
<p>トップダウン型と異なり、プリミティブのペアを作り、さらに出来上がったペアのペアを作る、といったアプローチでBVHを構築します。</p>
<p><a href="https://www.cs.cornell.edu/~kb/publications/IRT08.pdf">Fast Agglomerative Clustering for Rendering</a></p>
<div align="center">
<img src="figures/agglomerative.png" width="512">
</div>
<p>初めてボトムアップ型の構築を導入した論文ですが、品質の良いBVHが作れる反面、ペアを見つけるのに別のデータ構造を用いているため、実際の使い勝手は良くありません。ただし手法自体は有益で、後述するツリーレットの最適化手法でも使用されます。</p>
<p><a href="http://graphics.cs.cmu.edu/projects/aac/">Efficient BVH Construction via Approximate Agglomerative Clustering</a> <a href="http://www.highperformancegraphics.org/wp-content/uploads/Gu-AAC.pdf">(Slides)</a></p>
<div align="center">
<img src="figures/aac_bad.png" width="512">
</div>
<p>Agglomerative Clusteringは図のように細かく分割されたモデルの場合、品質の悪いBVHを生成します。これはバウンディング・ボックスの重なりが大きくなるためで、解決するにはEPOといった別の評価関数を取り入れる必要があります。Manukaのように全てのオブジェクトを細かく分割するタイプの実装では、そのままAgglomerative Clusteringを使うのはお勧めしません。</p>
<p><a href="https://dcgi.felk.cvut.cz/projects/ploc/ploc-tvcg.pdf">Parallel Locally-Ordered Clustering for Bounding Volume Hierarchy Construction</a></p>
<p>構築の並列性が高くないというAACの問題を克服しています。アルゴリズムは非常に簡単で、まず<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.10903em;">N</span></span></span></span>個のプリミティブが与えられたとき、各プリミティブにつき1つのクラスタ、つまり全部で<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.10903em;">N</span></span></span></span>個のクラスタを用意します。次にこれらをモートン・コードでソートします。ソートは最初に一度だけ行われます。</p>
<div align="center">
<img src="figures/nn_pairs.png" width="512">
</div>
<p>つづいて、それぞれのクラスタに対して、自身のインデックス<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mo>−</mo><mi>r</mi></mrow><annotation encoding="application/x-tex">-r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord">−</span><span class="mord mathit" style="margin-right:0.02778em;">r</span></span></span></span>から<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mo>+</mo><mi>r</mi></mrow><annotation encoding="application/x-tex">+r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord">+</span><span class="mord mathit" style="margin-right:0.02778em;">r</span></span></span></span>の範囲から最も近いクラスタを探します。全てのクラスタについて最も近いクラスタが見つかったら、最近傍ペアを作ります(図の緑のクラスタ)。最近傍ペアというのはクラスタ<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">C_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>と<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">C_j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>があった時に、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">C_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>が<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">C_j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>の最近傍かつ<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">C_j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>が<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">C_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>の最近傍であるようなペアのことをいいます。</p>
<p>次に見つかったペアをマージします。ペアが見つからなかった場合(図の赤のクラスタ)はそのクラスタはそのまま残します。この処理を繰り返し、クラスタが一つになるまで繰り返して、BVHの構築が終了します。これらの処理はダブル・バッファを用いて並列で行うことが出来ます。</p>
<p>個人的には実装しないと思っていたので、きちんと読んでいませんでしたが、分かり易く書かれた良い論文です。アルゴリズムが必ず収束する(各反復で必ずペアが見つかる)のを証明している部分がありますが、簡単なグラフ理論を使ってきれいに書かれています。</p>
<p>論文中ではATRBVHより高速にレンダリングできるシーンが多いものの、Happy Buddhaではやはり遅くなると報告されています。</p>
<p><code>PLOCの論文ではイントロダクションでインサーションも一般的な構築方法の一つだと書いてありますが、個人的にはインサーションは最適化手法に分類するのがふさわしいと思うので最適化のReinsertionのところで紹介します。</code></p>
<h3 class="mume-header" id="%E3%83%AA%E3%83%8B%E3%82%A2bvh-lbvh">リニアBVH (LBVH)</h3>
<p>LBVHは少し分類に迷ったので、独立したカテゴリとすることにします。</p>
<p><a href="http://graphics.snu.ac.kr/class/graphics2011/references/2007_lauterbach.pdf">Fast BVH Construction on GPUs</a></p>
<div align="center">
<img src="figures/morton.png" width="512">
<p>image from <a href="https://devblogs.nvidia.com/thinking-parallel-part-iii-tree-construction-gpu/">https://devblogs.nvidia.com/thinking-parallel-part-iii-tree-construction-gpu/</a></p>
</div>
<p>この論文はLBVHを導入した論文として知られていますが、実は内容は2部構成で、前半がモートン・コードといった空間充填曲線を利用する高速な構築方法の提案、後半がBinningを使ったSAHを最小化する構築の並列化についての解説となっています。</p>
<p>前半部分ですが、シーンをグリッドに細かく分割し(明示的に行うのではなく、モートン・コードなどの空間充填曲線により得られるコードにより陰に行われます)、各オブジェクトが所属するグリッドのIDを空間充填曲線によって求めます。その値が近いもの同士がペアとなります。高速ですが、品質はあまりよくありません。特に局所的にポリゴン数の多いオブジェクトがあるケース(いわゆるteapot in stadium)を苦手とします。</p>
<p>構築はトップダウンかつ逐次型で、まずプリミティブをコードでソートし、ルート・ノードを用意します。そして最上位ビットが変わる部分を見つけ、子ノードを2つ生成します。1つは最上位ビットが0であるプリミティブ用、もう1つは最上位ビットが1であるプリミティブ用です。つづいて、それぞれの子ノードに属するプリミティブに対し同様の処理を行うのですが、次は上から2番目のビットの値が変わる場所(なければ3番目のビット)を見つけ、それぞれのノードにさらに子ノードを2つ追加します。子ノードに属すプリミティブが1つになればそれはリーフであり、そのグループについての処理は終了します。</p>
<p>後半のSAHをつかったBVHの構築はいたって普通で、Binningを使ったものとなっています。一般的に、ルート・ノード付近での並列性を高めるのは難しいことから、ハイブリッドな構築手法も提案しています。ルートに近い部分のみLBVHで構築し、残りはBinningを使うというものです。並列性を上げることは出来ますが、ルートに近い部分のノードは一般的に表面積が大きいため、出来上がったツリーの品質についてはあまり期待できません。</p>
<p><a href="https://research.nvidia.com/publication/hlbvh-hierarchical-lbvh-construction-real-time-ray-tracing">HLBVH: Hierarchical LBVH Construction for Real-Time Ray Tracing</a></p>
<p>LBVHの構築はメモリを多く消費してしまうといった欠点がありましたが、上位レベルのツリーと下位レベルのサブツリーとの2段階でBVHを構築することで解決しています。</p>
<p><a href="https://research.nvidia.com/publication/simpler-and-faster-hlbvh-work-queues">Simpler and Faster HLBVH with Work Queues</a></p>
<p>タスク・キューを用い上記のHLBVHの構築方法を簡潔、かつ高速にしています。</p>
<p><a href="https://research.nvidia.com/publication/maximizing-parallelism-construction-bvhs-octrees-and-k-d-trees">Maximizing Parallelism in the Construction of BVHs, Octrees, and k-d Trees</a></p>
<div align="center">
<img src="figures/maximize.png" width="512">
</div>
<p>先に挙げた2つの論文では、メモリ消費、並列性などLBVHの構築方法の欠点がいくらか解決されたものの、依然としてノードの処理は完全に並列ではありませんでした。この論文では全ノードを並列に扱う方法が提案されており、また、応用範囲は広くOctreeなどにも適用できます。</p>
<p>構築されるBVHは上図のようなノード(ピンク)とリーフ(グリーン)のレイアウトとなります。アルゴリズムは単純で、それぞれのノードについて独立に、「ノードがカバーする範囲(図中の水平なバー、文献によってはPivotsと書いているものもある)」と「Split(つまり分割位置)」を求めます。BVHの構築にはSplit(言い換えれば子ノードへの参照)だけ分かればよいのですが、ノードがカバーする範囲はSplitを見つけるために使われます。</p>
<p>分かり易くするため、まず、ノード3に注目してみましょう。対応するリーフのコードは<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>00101</mn></mrow><annotation encoding="application/x-tex">00101</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span><span class="mord">0</span><span class="mord">1</span><span class="mord">0</span><span class="mord">1</span></span></span></span>、右隣のノード4に対応するコードは<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>10011</mn></mrow><annotation encoding="application/x-tex">10011</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span><span class="mord">0</span><span class="mord">0</span><span class="mord">1</span><span class="mord">1</span></span></span></span>、左隣のノード2に対応するコードは<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>00100</mn></mrow><annotation encoding="application/x-tex">00100</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span><span class="mord">0</span><span class="mord">1</span><span class="mord">0</span><span class="mord">0</span></span></span></span>となっています。</p>
<p>ここで<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mi>i</mi><mo separator="true">,</mo><mi>j</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">\delta(i,j)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathit">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mclose">)</span></span></span></span>を「ノード<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.65952em;vertical-align:0em;"></span><span class="mord mathit">i</span></span></span></span>と<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.85396em;vertical-align:-0.19444em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span></span></span></span>のコードの共通な上位ビットの数」としましょう。赤い手書き文字で書かれた数字です。最上位ビットが異なるので、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>4</mn><mo>)</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\delta(3,4)=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">4</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span>、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>2</mn><mo>)</mo><mo>=</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">\delta(3,2)=4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">2</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">4</span></span></span></span>であるので、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>4</mn><mo>)</mo><mo>−</mo><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>2</mn><mo>)</mo><mo>=</mo><mo>−</mo><mn>4</mn><mo><</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\delta(3,4)-\delta(3,2)=-4<0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">4</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">2</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">−</span><span class="mord">4</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span>となります。この値がマイナスであった場合は、リーフ3はこのノードの先頭(左端)ではなく、末尾(右端)であることが分かります。逆に、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>0</mn><mo>≤</mo><mi>δ</mi><mo>(</mo><mi>i</mi><mo separator="true">,</mo><mi>i</mi><mo>+</mo><mn>1</mn><mo>)</mo><mo>−</mo><mi>δ</mi><mo>(</mo><mi>i</mi><mo separator="true">,</mo><mi>i</mi><mo>−</mo><mn>1</mn><mo>)</mo></mrow><annotation encoding="application/x-tex">0 \leq \delta(i,i+1)-\delta(i,i-1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.78041em;vertical-align:-0.13597em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathit">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathit">i</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathit">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathit">i</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span>の時はこのノードがカバーする範囲の先頭となります。ノード5を見ればこの値は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>4</mn><mo>−</mo><mn>1</mn><mo>=</mo><mn>3</mn></mrow><annotation encoding="application/x-tex">4-1=3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">4</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">3</span></span></span></span>となっています。</p>
<p>同様にコードを調べ、逆の端点、つまり対象ノードが末尾であった場合は先頭を、先頭であった場合は末尾を探します。ノード3は末尾であるので、左方向に探索を行います。探し方は特殊で、まず進む方向と逆の右隣を見て、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>4</mn><mo>)</mo></mrow><annotation encoding="application/x-tex">\delta(3,4)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">4</span><span class="mclose">)</span></span></span></span>を求めます。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span>ですね。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>0</mn><mo><</mo><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mi>j</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">0<\delta(3,j)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68354em;vertical-align:-0.0391em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mclose">)</span></span></span></span>である間、左へ進んでいき、結果<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>j</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">j=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.85396em;vertical-align:-0.19444em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span>となります。ノード5の場合は先頭であるので、右へ進んでいきます。同様にまず逆の左隣をみて<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>5</mn><mo separator="true">,</mo><mn>4</mn><mo>)</mo></mrow><annotation encoding="application/x-tex">\delta(5,4)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">5</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">4</span><span class="mclose">)</span></span></span></span>を求めます。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>ですね。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>1</mn><mo><</mo><mi>δ</mi><mo>(</mo><mn>5</mn><mo separator="true">,</mo><mi>j</mi><mo>)</mo></mrow><annotation encoding="application/x-tex">1<\delta(5,j)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68354em;vertical-align:-0.0391em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">5</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mclose">)</span></span></span></span>である間、右へ進みます。結果<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>j</mi><mo>=</mo><mn>7</mn></mrow><annotation encoding="application/x-tex">j=7</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.85396em;vertical-align:-0.19444em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">7</span></span></span></span>となります</p>
<p>つづいてSplitを探します。ノード3の場合は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>3</mn><mo separator="true">,</mo><mn>0</mn><mo>)</mo><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">\delta(3,0)=2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">3</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">0</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span>なので<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span></span></span></span>が<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>2</mn></mrow><annotation encoding="application/x-tex">2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span>からそれより大きな値へ変化するところつまり2と1の間がSplitとなります。ノード5の場合は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>δ</mi><mo>(</mo><mn>5</mn><mo separator="true">,</mo><mn>7</mn><mo>)</mo><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">\delta(5,7)=2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathit" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord">5</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">7</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span>で、同様に<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>2</mn></mrow><annotation encoding="application/x-tex">2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span>から大きな値に変化する場所つまり6と7の間がSplitとなります。探索には二分探索などが使えます。</p>
<p>以上のように、各ノードが完全に独立して子ノードへの参照を求めることができるという素晴らしい手法です。</p>
<p><a href="https://devblogs.nvidia.com/thinking-parallel-part-iii-tree-construction-gpu/">Thinking Parallel, Part III</a></p>
<p>著者によるブログでも解説されています。</p>
<p><a href="http://dcgi.felk.cvut.cz/projects/emc/">Extended Morton Codes for High Performance Bounding Volume Hierarchy Construction</a></p>
<p>LBVHが苦手とするteapot in stadium問題に対する対策を含め、いくつかの改善策が書かれています。</p>
<p>モートン・コードはX(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>x</mi><mn>3</mn></msub><msub><mi>x</mi><mn>2</mn></msub><msub><mi>x</mi><mn>1</mn></msub><msub><mi>x</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_3 x_2 x_1 x_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>)、Y(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>y</mi><mn>3</mn></msub><msub><mi>y</mi><mn>2</mn></msub><msub><mi>y</mi><mn>1</mn></msub><msub><mi>y</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">y_3 y_2 y_1 y_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>)、Z(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>z</mi><mn>3</mn></msub><msub><mi>z</mi><mn>2</mn></msub><msub><mi>z</mi><mn>1</mn></msub><msub><mi>z</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">z_3 z_2 z_1 z_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>)の正規化した各座標のビットを<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>x</mi><mn>3</mn></msub><msub><mi>y</mi><mn>3</mn></msub><msub><mi>z</mi><mn>3</mn></msub><msub><mi>x</mi><mn>2</mn></msub><msub><mi>y</mi><mn>2</mn></msub><msub><mi>z</mi><mn>2</mn></msub><msub><mi>x</mi><mn>1</mn></msub><msub><mi>y</mi><mn>1</mn></msub><msub><mi>z</mi><mn>1</mn></msub><msub><mi>x</mi><mn>0</mn></msub><msub><mi>y</mi><mn>0</mn></msub><msub><mi>z</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_3 y_3 z_3 x_2 y_2 z_2 x_1 y_1 z_1 x_0 y_0 z_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>のように順番にならべたものです。(簡単のため4ビットで各座標を表現しています。)</p>
<div align="center">
<img src="figures/emc.png" width="320">
</div>
<p>BVH構築時にプリミティブのサイズを考慮するため、その中心座標だけではなく、バウンディング・ボックスの対角線の長さS(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>s</mi><mn>3</mn></msub><msub><mi>s</mi><mn>2</mn></msub><msub><mi>s</mi><mn>1</mn></msub><msub><mi>s</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">s_3 s_2 s_1 s_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>)もモートン・コードに組み込み、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>x</mi><mn>3</mn></msub><msub><mi>y</mi><mn>3</mn></msub><msub><mi>z</mi><mn>3</mn></msub><msub><mi>s</mi><mn>3</mn></msub><msub><mi>x</mi><mn>2</mn></msub><msub><mi>y</mi><mn>2</mn></msub><msub><mi>z</mi><mn>2</mn></msub><msub><mi>s</mi><mn>2</mn></msub><msub><mi>x</mi><mn>1</mn></msub><msub><mi>y</mi><mn>1</mn></msub><msub><mi>z</mi><mn>1</mn></msub><msub><mi>s</mi><mn>1</mn></msub><msub><mi>x</mi><mn>0</mn></msub><msub><mi>y</mi><mn>0</mn></msub><msub><mi>z</mi><mn>0</mn></msub><msub><mi>s</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_3 y_3 z_3 s_3 x_2 y_2 z_2 s_2 x_1 y_1 z_1 s_1 x_0 y_0 z_0 s_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathit">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>のように拡張しています。</p>
<p>大きなプリミティブはルートからリーフ・ノードまでのパス上にあるすべてのノードのサイズを大きくしてしまうため、小さなプリミティブと大きなプリミティブは中心座標が近くても、別のグループに分けたほうが、交差判定の効率が上がります。ただし、グループ分けを行うのがルートに近すぎても、リーフに近すぎても、ノードの重なりによって、逆にBVHの品質が下がってしまいます。この論文では、1となったサイズ・ビットが示す長さがバウンディング・ボックスのサイズより大きい場合、別のグループに分けることとしています。</p>
<p>また、サイズのビットは必ずしもインターリーブにする必要はなく、数も可変にして、<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>x</mi><mi>y</mi><mi>z</mi><mi>x</mi><mi>y</mi><mi>z</mi><mi>s</mi><mi>x</mi><mi>y</mi><mi>z</mi><mi>x</mi><mi>y</mi><mi>z</mi><mi>s</mi></mrow><annotation encoding="application/x-tex">xyzxyzsxyzxyzs</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">s</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">s</span></span></span></span>のように座標へより多くのビットを割り当てることができます。また<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>y</mi><mi>x</mi><mi>y</mi><mi>x</mi><mi>z</mi><mi>s</mi><mi>y</mi><mi>x</mi><mi>y</mi><mi>x</mi><mi>z</mi><mi>s</mi></mrow><annotation encoding="application/x-tex">yxyxzsyxyxzs</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">s</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">s</span></span></span></span>のように長軸を優先したり、シーンの広がりによってビット数を可変に割り当てたりすることで単純なモートン・コードの使用に比べBVHの大幅な品質改善を達成しています。</p>
<p>提案手法はモートン・コードを利用する構築方法全てで利用可能です。</p>
<h3 class="mume-header" id="%E7%9B%86%E6%A0%BD-bonsai">盆栽 (Bonsai)</h3>
<p>ミニ・ツリーという小さなツリーを大量に並列で構築し、それらをさらにまとめて1つのBVHを構築する方法もあります。</p>
<p><a href="http://jcgt.org/published/0004/03/02/paper.pdf">Bonsai: Rapid Bounding Volume Hierarchy Generation using Mini Trees</a></p>
<div align="center">
<img src="figures/bonsai.png" width="512">
</div>
<p>ミニ・ツリーをたくさん構築して、さらに、個々のミニ・ツリーをリーフとみなし、BVHを構築しますが、そのままでは質のいいBVHが作れるとは限りません。ミニ・ツリーのルートのバウンディングボックスが大きい場合は、刈込み(Pruning)を行います。刈込みとは、ルートに近いノードをばらし、ミニ・ツリーをさらにいくつかのミニ・ツリーに分ける操作で、それらから再び一つのBVHを構築します。これが盆栽(Bonsai)と名づけられた所以です。</p>
<p>侮るなかれ。この刈込みは様々なプリミティブに対してBVHを構築し、それらを1つのBVHにまとめる時の有力な手段となりますし、同じような考え方はRe-braidingの論文でも用いられています。</p>
<h2 class="mume-header" id="%E5%88%86%E5%89%B2%E7%B5%B1%E6%B2%BB%E6%B3%95-dacrt">分割統治法 (DACRT)</h2>
<p>構築方法について述べましたので、構築しない方法についても触れたいと思います。日本語にすると分割統治法(Divide and Conquer Ray Tracing, DACRT)と呼ばれるアプローチで入力データを小さなグループに分割し、そのグループ内で総当たりの交差判定を行います。構築とトラバースを同時に行い、構築したBVHを保存しないのが特徴で、動的なシーンのレンダリングに適しています。</p>
<p>プリミティブの陰的なデータ構造にはBVH、Octree、kd-treeなど任意なものを用いることが出来て、それらの長所と短所をそのまま継承します。またレイの分割にも4次元のLight Fieldを用いることができるなど、自由度は大変高いです。また、グループ分けをイン・プレイスで行った場合、事前に消費メモリのサイズが分かるため、ハードウェアの実装に適しています。</p>
<p>プリミティブやレイのデータは大きくなりがちなので、グループ分けでそれらを移動させるのは非効率的です。ですので、それらのインデックスの配列を作り、インデックスだけを移動させるという方法をとります。したがって、1つのプリミティブとレイにつきそれぞれ、4バイト(int型の場合)の追加データが必要となります。</p>
<p>学術的には大変面白い手法ですが、プロダクション・レンダラでの採用が進まないのには理由があります。この方法ではパフォーマンスを上げるため、あるいは構築分のコストを償却するため同時に多くのレイ扱いますが、プロダクション・レンダラでは1つのレイに非常に多くの情報(レイ微分、QMCなどサンプルに関連する情報、反射回数などなど)が付随します。よって、生存期間が重なるレイが増えると、メモリの消費が膨大になってしまうというジレンマに陥ります。また、並列化されたアルゴリズムも提案されていますが、スレッド数に比例させて高速化するのも難しいようです。また、パス・トレーシングはWavefront方式に切り替える必要があります。</p>
<p>このアプローチのメリットはダイナミックなシーンが簡単に扱えることですが、複数種類のプリミティブを同時に容易に扱えるのも利点です。プリミティブの種類ごとにBVHを構築しストアしておく必要がありません。</p>
<p>DACRTをテーマにした論文にはどれも簡単に見つけられる(あるいは改善できる)欠点があり、突き詰めたものが存在しないという印象を受けます。逆に、論文を書くには大変良いテーマなのではないでしょうか。もちろん、プロダクションで使えるレベルに昇華させるのは全く別の問題です。</p>
<div align="center">
<img src="figures/naive.png" width="512">
</div>
<p>アルゴリズムは上のコードのようにシンプルです。まず、シーンを含むバウンディング・ボックス、プリミティブのリスト、それからレイのリストを入力し、プリミティブを何らかの基準によって2つのグループに分けます。一番単純な方法は、長軸でバウンディング・ボックスを2つに切断し、そのどちらかに含まれるかによって分類するものです。レイも同様に、左の子ノードにヒットするもの、右の子ノードにヒットするもの、といった具合に分類します。グループに分ける処理は一般的にフィルタリングと呼ばれます。フィルタリングを終えると、2つの子ノード、2つのプリミティブのリスト、2つのレイのリストができるので、それらに対しさらに同様の処理を行い、レイのリストのサイズとプリミティブのリストのサイズが共にある程度小さくなったら、総当たりの交差判定を行います。</p>
<p>フィルタリングは陰に使用するデータ構造やアルゴリズムにより異なり、右のノードにヒットするもの、左のノードにヒットするもの、それから両方のノードにヒットするものといった具合に2つ以上のグループに分けることもあります。</p>
<p><a href="https://dl.acm.org/citation.cfm?id=2019636">Naive ray-tracing: A divide-and-conquer approach</a></p>
<p>DACRTを提案した論文です。2009年にmental imagesよる特許で同様の内容が記述されていたようです。</p>
<p>高速化手法としてConic Packet(円錐を使ったバンドルによるDACRT)が用いられています。これはレイの始点が同じである場合、1次レイやシャドウ・レイの計算に用いられています。</p>
<p>分割はイン・プレイスであると書かれているので、陰に使われるデータ構造はObject SplittingのBVHだと思っていたのですが、実はkd-treeが用いられています。イン・プレイスで実装するため、フィルタリングは、左のノードと交差するものとそうでないものを分けるパス、右のノードと交差するものとそうでないものを分けるパスの2回行われており、ここが欠点と考えられます。分割は長軸で行います。その位置は、10000以上プリミティブがある場合、間引いたプリミティブで近似したSAHをもとに決定され、10000より少ない場合は単純に真ん中とします。</p>
<p><code>kd-treeはトラバースが高速であると考えられますが、kd-treeでフィルタリングを2回行うのと、Object Splittingでフィルタリングを1度だけ行うのとどちらが実際に高速か検証する必要がありそうです。</code></p>
<p>残念ながら、並列化については詳細がいっさい書かれていませんが、実は2011年のi7、4コア8スレッドのCPUを用いたパス・トレーシングで4.6Mrays/sec(Sponza、7回の拡散反射)となかなか素晴らしい結果を達成しています。この数値は1スレッドの時の4.2倍であり、線形に近い高速化が達成できています。後ほど2本、並列化を扱った論文を取り上げますが、この論文の実装のほうがより優れていたように思えます。内容もしっかりしており、DACRTに興味がある方は、まずこの論文を読むとよいと思います。</p>
<p><a href="http://www.wakayama-u.ac.jp/~iwasaki/project/dacrt/hpg2013final.pdf">Efficient Divide-And-Conquer Ray Tracing using Ray Sampling</a></p>
<div align="center">
<img src="figures/efficient_dacrt.png" width="512">
</div>
<p>この論文では陰的なデータ構造にBVHを利用しており、プリミティブは、コスト関数にSAHを用いて、Binningを用いて2つのグループに分けられます。ただし、コストの計算では単純に親ノードと子ノードの面積比を用いず、無作為に選ばれた一部のレイ(Representative Rays)を用いて、レイが子ノードにあたる確率を近似しています。こうすることで、実際のシーン中のレイの分布が考慮されるため、交差判定の高速化が期待できます。レイに相関がある場合効果が大きそうです。</p>
<p>プリミティブをグループ分けした後、レイを「左のノードに当たるもの」と「右のノードに当たるもの」の2つのグループに分けるフィルタリングの処理を行います。フィルタリング後、トラバースを行いますが、サンプルされたレイの過半数が左のノードに当たる場合は左のノードを先に、過半数が右のノードに当たる場合は右のノードを先に、トラバースします。</p>
<p>理にかなったように見えますが、実はここがこの論文の欠点となっています。例えばレイのバッチ・サイズが100本であった場合、51本が右、49本が左のノードが近いといった状況であれば、右のノードを先にトラバース(再帰的にDACRT)するわけですが、本来左のノードを先にトラバースすべきレイは、右のノードを先にたどらざるを得なくなります。したがって、本来テストしなくてよかったプリミティブとの交差判定を行う必要が出てきます。民主主義的にトラバースの順番を決めているので、処理は全てのレイについて最適ではありません。ただし、これは数行のコードの追加で修正できますので、それほど大きな問題ではありません。また、レイがどちらか一方のノードのみとしか交差しないという状況であれば、この問題は無視できます。</p>
<div align="center">
<img src="figures/skip_filtering.png" width="512">
</div>
<p>Representative Raysに基づいた確率計算の他に、この論文には、レイのフィルタリングをスキップするというユニークなアイデアが導入されています。レイのフィルタリングはアクティブなレイの数を減らすのに役立ちますが、レイの原点や向き、ノードの位置によってはフィルタリングしてもアクティブなレイがほとんど減らないことがあります。そういった場合はフィルタリング自体をスキップしたほうが効率的です。</p>
<p>また、残念ながらこの方法は並列化が考慮されていません。</p>
<p><a href="https://www.cs.dartmouth.edu/~sriravic/papers/pdacrt.pdf">Parallel Divide and Conquer Ray Tracing</a></p>
<p>この論文では、DACRTを並列化することにのみ焦点を当てています。ルート・ノードとプリミティブとレイのリストから処理を始めるのは共通ですが、出来上がった子ノードを再帰による深さ優先の処理ではなく、幅優先で並列に処理していきます。</p>
<div align="center">
<img src="figures/pdacrt_split.png" width="512">
</div>
<p>それぞれのノードにおいて、バウンディング・ボックスを2つに分割したのち、プリミティブ(ここではTriangle)を3つのグループに分けます。Node0にのみ含まれるもの、Node1にのみ含まれるもの、両方をまたぐもの、の3つです。レイは4つにグループ分けされます。Node0あるいはNode1にのみヒットするもの、両方にヒットするもの、それから、どちらにもヒットしないものです。</p>
<div align="center">
<img src="figures/pdacrt_sort.png" width="512">
</div>
<p>次にそれぞれのリストをグループによってソートします。どちらにもヒットしなかったレイは、除去されます。深さを一つ進めるには、レイとプリミティブのリストを作り直します。上の図の例でいうと、Triangle Idsは1,2,6~24までと3,4,5~25までのリストの2つをくっつけたものが新たに生成され、現在のリストは破棄されます。レイのリストも同様に1~9までのリストと2~25までのリストの2つをくっつけたものが新たに生成されますが、レイのリストの場合は、最近傍点を見つけたり、子ノードにヒットしないなどで、一般的にサイズが小さくなっていくのでメモリ消費の問題は深刻ではありません。</p>
<p>実は、陰に利用されるデータ構造には、BVHではなくkd-treeが用いられています。kd-treeを用いた理由は高速化のためであると思われます。しかし、kd-treeを用いたが故、プリミティブのグループ分けがObject SplittingのBVHとは異なり、イン・プレイスで出来なくなってしまい、深いレベルにいくにつれてプリミティブの参照が重複し増えていきます。論文中でもこれは欠点として触れられており、DACRTは本来省メモリが売りであるにも関わらず、子ノードをまたぐプリミティブの参照によってメモリの消費が膨大になってしまうというジレンマに陥っています。</p>
<div align="center">
<img src="figures/terminals.png" width="512">
</div>
<p>さて、先述の通り、レイのグループとプリミティブのグループが十分に小さくなったら総当たりの交差判定を行うのですが、この処理も並列化されています。閾値サイズ以下になったレイとプリミティブのグループ(あるいは、ターミナル・ノードに所属するといいます)は事前に確保されたバッファへとコピーされます(上図)。バッファがいっぱいになれば、各ノード内においてレイとプリミティブの交差判定を行います。すべてのノードは並列に処理されます。それぞれのレイは最近傍点までの距離とヒットしたオブジェクトのIDを持ちますが、それらはmin_tバッファ、hit_idバッファとしてグローバルに確保された配列にストアされます。バッファがいっぱいになるまでこの処理は行われないので、それまでは最近傍点までの距離が更新されず、無駄な交差判定が行われることになります。</p>
<p>また、幅優先で処理する代償として、front-to-backトラバーサルをあきらめています。右、あるいは左のノードがレイの原点から近い、といった情報は一切使用せず、基本的にはどちらもチェックするという非常に効率の悪いアルゴリズムが用いられています。</p>
<p>構築の手法の解説で触れたように、ルート・ノード近くは並列性を高めるのが難しく、また非常に効率の悪いトラバースを行うので、GPUを用いたにもかかわらず、大した高速化が出来ていません。並列化しただけという内容の論文ですが、逆に並列化の難しさを伝えるという役割を果たしているように思います。</p>
<p><a href="https://ieeexplore.ieee.org/document/6915284">Improving Divide-and-Conquer Ray-Tracing Using a Parallel Approach</a></p>
<p>こちらも並列化をテーマにした論文ですが、Parallel DACRTと比べていくつか工夫がされています。</p>
<div align="center">
<img src="figures/transfer.png" width="512">
</div>
<p>この論文ではレイとプリミティブをスレッド数に応じて複数のグループに分け、各スレッドが1つのレイのグループと1つのプリミティブのグループの交差判定を行います。もちろんこれだけでは各レイが最近傍点を見つけることができませんので、レイのグループは別のプリミティブのグループと交差判定を行う必要があります。もっとも単純なケースでは、上図のようにそれぞれ2つのグループがある場合で、レイのグループを単純にスワップさせればよいことがわかります。</p>
<div align="center">
<img src="figures/transfer_multi.png" width="512">
</div>
<p>スレッド数が増えるにつれ、レイとプリミティブのグループの数は増えていきます。ですのでノードが2つだけであればレイのグループをスワップさせればよかったのですが、複数ある場合は上図のようにレイのグループをまだ交差判定を行っていないプリミティブのグループのほうへ移していく必要があります。この移す順番と開始点はレイの始点と方向に依存します。当然ながら近いプリミティブのグループから処理したほうが効率が良いわけですが、処理開始時点での最適なレイとプリミティブのグループのペアを何かしらのコスト関数を最小化するように決めるのはなかなか難しいのではないでしょうか。</p>
<p>面白い方法ですが、パフォーマンスはスレッド数に比例しておらず、改善の余地があることを示唆しています。</p>
<h2 class="mume-header" id="%E6%9C%80%E9%81%A9%E5%8C%96-optimization">最適化 (Optimization)</h2>
<p>ここから最適化の話に入ります。最適化手法にはBVHのトポロジーを変更するもの、またノードの並びを変更するもの、メモリの節約を行うものなど、様々なものがあります。</p>
<p>BVHの品質を上げる最適化は、SAHのみを考慮するもの、それからRepresentative Raysと呼ばれるレイを投げ、シーン内での実際のレイの分布を考慮に入れるものの2つに分けることが出来ます。後者のタイプではどのレイが、どのノードに当たったか、どのリーフに当たったか、といった記録を取る必要があるので追加のコストが必要になりますが、レンダリングするシーンに最適な結果を導き出すことができます。コンパイラのProfile Guided Optimizationと似たコンセプトです。Representative Raysは実際のレンダリングに使われるレイの一部(数パーセント)であるためオーバーヘッドは小さく、また、それによって得られた結果は無駄にはなりません。</p>
<p>トポロジーを変更する最適化は、静的なシーン用のBVH構築直後、または、動的なシーンでBVHの品質が下がっていくのを防ぐ為にプログレッシブに行います。</p>
<h3 class="mume-header" id="wide-bvh-n-ary-bvh">Wide BVH (N-ary BVH)</h3>
<p>現在主に使われているIntelやAMDのCPUはSIMDといわれる4から16の演算を並列に実行するユニットが備わっていますので、CPUの実装ではWide BVHが使われることがほとんどです。SSE、AVX、AVX-512といったユニットを聞いたことがあるでしょうか?Wide BVHはそれらを活用し、複数のバウンディング・ボックスとレイの交差判定を一度に行えるようにするため提案されました。各ノードは複数の子ノードを持つことになり、これがWide BVHと呼ばれる所以です。通常実装にはSOA(Structure of Arrays)を使います。複数のバウンディング・ボックスのデータが隣接するのでメモリのアクセスの点で有利です。1つのノード自体は大きくなりますが、ノードの数自体が減るので、メモリ消費の観点からは64ビット消費するポインタではなく32ビットのインデックスで管理したほうが良いでしょう。</p>
<p><code>少し話がそれますが、これに限らずポインタの使用はお勧めしません。integerのインデックスでよい場合はインデックスを使うように心がけましょう。インデックスが妥当かどうかは値が-1(あるいは最大値)かどうかでチェックします。ポインタでもnullかどうかで判別できるから同じだと思うかもしれませんが、それは早とちりです。インデックスの利点は配列のサイズが分かっていた場合、値の上限についてもチェックできるという点で、バグが発見しやすくなります。個人的にはメモリ節約よりもこれによる恩恵のほうが大きいと感じています。私が作っているレンダラはインデックスに切り替えてからクラッシュ・バグと戦うことがほぼなくなりました。自分の経験では、製品でもクラッシュするバグはだいたいポインタがらみです。</code></p>
<p><a href="https://dspace.jaist.ac.jp/dspace/bitstream/10119/3530/2/091paper.pdf">並列演算に適したバウンディングボリューム階層によるレイトレーシングの高速化</a></p>
<p>QBVHと呼ばれているものです。SSEを用いており、BVHは4分木となります。QBVHを解説した英語の文献は、次にあげるものが最初で、現在はそちらが広く知れ渡っています。英語で書くことの大切さを思い知らされます。</p>
<p><a href="https://www.uni-ulm.de/fileadmin/website_uni_ulm/iui.inst.100/institut/Papers/QBVH.pdf">Shallow Bounding Volume Hierarchies for Fast SIMD Ray Tracing of Incoherent Rays</a></p>
<p>QBVHについては日本語のを読んだからこちらは読まなくてもよいと思うかもしれませんが、この論文ではさらにShadow Rayの高速化について触れていますので一読の価値ありです。基本的には、あるノードからトラバースを開始してヒットしなければ親ノードに戻る、ということを行いますが、親へ戻ってトラバースするには、親ノードへの参照が必要になること、またSIMDの幅がNであった場合に、そのうちの一つは既に計算済みですから、使用率が100%ではなく<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mfrac><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow><mi>N</mi></mfrac></mrow><annotation encoding="application/x-tex">\frac{N-1}{N}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.217331em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.872331em;"><span style="top:-2.6550000000000002em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathit mtight" style="margin-right:0.10903em;">N</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>になってしまうという欠点があります。</p>
<p>レイの始点と終点がいつも特定のノードに収まる場合には、ルート・ノードからトラバースせず、特定のノード以下(サブツリー)だけ対象にすればよいので、高速化が可能です。この場合は親ノードへの参照も必要ありませんし、SIMDの使用率も落ちません。また、始点が含まれるルート以外のノードからトラバースを開始して、何もヒットしなかったら改めてルートからルートからたどる、ということも可能です。こういったテクニックの使いどころは、近年はやりのランダム・ウォークを使ったサブサーフェス・スキャッタリングや(MFPによりある程度範囲が絞れます)、探索半径が限定できるラウンド・コーナーや曲率を求めるシェーダーなどが良い例ではないでしょうか。</p>
<p><a href="https://voxelium.wordpress.com/2013/08/14/faster-incoherent-ray-traversal-using-8-wide-avx-instructions/">Faster Incoherent Ray Traversal Using 8-Wide AVX Instructions</a></p>
<p>こちらはAVXを利用して8-ary BVHにしたもの。幅が広がるほどレイが当たったノードのソートに時間がかかるようになりますが、この論文では当たった数によって、ソートのコード・パスを変えることで高速化できると書かれています。</p>
<p><a href="http://graphics.cs.ucdavis.edu/~hamann/FuetterlingLojewskiPfreundtHamannEbertHPG2017PaperFinal06222017.pdf">Accelerated Single Ray Tracing for Wide Vector Units</a></p>
<p>これはAVX-512を利用してさらに16-ary BVHとしたもの。幅が増えただけと思うかもしれませんが、先も述べたようにWide BVHではレイが交差したノードのソートがボトルネックになります。この論文ではQBVHの論文のようにソート無しで訪れる順序を決める方法が書かれているので目を通しておくとよいでしょう。</p>
<p>この記事の執筆時点ではAVX-512を搭載したCPUはそれほど多く出回っていませんが、使用する場合動作クロックを落とすことがあるようで、どんどんSIMDの幅が広がっていくかといえば、残念ながらそうでもないようです。しかし、ディープ・ラーニングの普及は、幅の広いSIMDの普及に追い風となるのではないでしょうか。</p>
<h3 class="mume-header" id="wide-bvh-on-gpu">Wide BVH on GPU</h3>
<p>最近の研究ではGPUでもWide BVHの使用は効果があることが報告されています。</p>
<p><a href="https://users.aalto.fi/~laines9/publications/ylitie2017hpg_paper.pdf">Efficient Incoherent Ray Traversal on GPUs Through Compressed Wide BVHs</a></p>
<p>これはWide BVHをGPUで使用したものです。圧縮したノードを利用することでメモリ帯域の圧迫を避け、高速化を達成しています。</p>
<p><a href="https://github.com/lispbub/simd-ray-traversal">CPU-style SIMD ray traversal on GPUs</a></p>
<p>こちらはCPUと同じようにWideBVHをGPUで使った場合に高速化できることが示されています。</p>
<h3 class="mume-header" id="%E7%B8%AE%E7%B4%84-contractioncollapsing">縮約 (Contraction/Collapsing)</h3>
<div align="center">
<img src="figures/contraction.png" width="512">
</div>
<p>Contractionとは2分木型のBinary BVHを、N分木型のWide BVHへと変換する処理のことをいいます。Collapseという用語を使う場合もあります。プロダクション・レンダラではこの処理を明示的に行うことはなく、直接Wide BVHを構築します。理由は単純で、Binary BVHとWide BVHの両方を保持するメモリが無駄だからです。ただし、構築の過程でContractionの考え方は必要になります。実は奥が深く、やり方次第で出来上がったWide BVHの品質が大きく左右されます。最近では、先述のようにWide BVHの利用がGPUでも効果的であると認められてきているので、覚えておくとよいでしょう。</p>
<p><a href="https://paginas.fe.up.pt/~ei06035/paper/adaptive-collapsing-bvh-for-raytracing.pdf">Adaptive Collapsing on Bounding Volume Hierarchies for Ray-Tracing</a></p>
<p>最適なコントラクションを動的計画法によって求めます。実装がやや面倒になってしまうのが難点です。</p>
<p><a href="https://www.cs.cmu.edu/~ygu1/paper/PG15/conference.pdf">Ray Specialized Contraction on Bounding Volume Hierarchies</a></p>
<p>この方法はBinary BVHをビルドした後、Representative Raysを投げ(実際のレンダリングで使用されるレイの数パーセント程度)、レンダリングを行います。ここではBinary BVHを使用しますが、その際、レイがどのノードをどれだけ訪れたかを記録しておきます。</p>
<p>そのあとコントラクションを行う際には、Representative Raysが多く訪れたノードを優先して展開します。こうすることでSAHではなく実際のシーンにおけるレイの分布に最適化されたWide BVHを作ることができ、それを用いて非常に効率の良いレンダリングができるようになります。個人的には、目からうろこでした。私のレンダラで試したところSponzaでは20%強の高速化が得られました。</p>
<p><a href="https://github.com/shinjiogaki/reports/blob/master/report.pdf">Fragmentation-Aware BVH Contraction</a></p>
<p>Wide BVHの構築の際に、子ノードの数がBVHの幅より小さくなることがあります。一般的には子ノードの数が幅と等しくなるようコスト関数を修正して(例えばオブジェクトの数が4や8の倍数のときボーナスを加えるなど)隙間をなくすようにします。それでも空きのある、リーフだけを含むノードによってメモリが無駄に消費されてしまうことがあります。</p>
<p>このレポートでは、空きスペースのあるノードのペアを見つけて、マージするという簡単な解決策を提案しています。例えばOBVHを利用しているときには、3つ空きがあるノードと5つ空きがあるノードをマージすることによって、無駄なスペースを完全に無くすことが出来ます。パフォーマンスの低下はほぼありません。適当に付けたので、ふさわしい名前かどうかは分かりません。</p>
<h3 class="mume-header" id="%E5%86%8D%E3%83%95%E3%82%A3%E3%83%83%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0-refitting">再フィッティング (Refitting)</h3>
<p>動いているものを扱うときに、BVHのノードのバウンディング・ボックスが中にあるオブジェクトをきちんと内包していなければ交差判定の結果がおかしくなったり、またルーズにフィットしていると処理に余計な時間がかかります。バウンディング・ボックスをフィットさせる処理自体は非常に簡単ですが、並列化で完全にリニアにスケールさせるのは思いのほか難しいものです。</p>
<p><a href="https://research.nvidia.com/publication/maximizing-parallelism-construction-bvhs-octrees-and-k-d-trees">Maximizing Parallelism in the Construction of BVHs, Octrees, and k-d Trees</a></p>
<p>atomicmin/maxが使える環境であれば、各ノードに親ノードへの参照を持たせることで比較的簡単に並列化できます。</p>
<h3 class="mume-header" id="%E9%83%A8%E5%88%86%E5%86%8D%E6%A7%8B%E7%AF%89-restructuring">部分再構築 (Restructuring)</h3>
<p>BVHからツリーレットを切りだしてきて、ツリーレットの末端ノードを一旦リーフとみなし、それらに対してなにかしらの構築のアルゴリズムを適用し、ツリーレットを作り直す操作をRestructuringと呼びます。</p>
<p>既に紹介したLBVHは非常に高速にBVHを構築することができますが、品質自体はあまりよくありません。ですので、この再構築による最適化と組み合わせて品質を上げるといったことが行われます。</p>
<p><a href="https://research.nvidia.com/publication/fast-parallel-construction-high-quality-bounding-volume-hierarchies">Fast Parallel Construction of High-Quality Bounding Volume Hierarchies</a></p>
<p><a href="https://patents.google.com/patent/US20140365532A1/en">(US20140365532A1)</a></p>
<div align="center">
<img src="figures/trbvh.png" width="512">
</div>
<p>ツリーレットの再構築することで品質を改善します。高速なLVBHで構築したBVHなどと組み合わせて使用されます。</p>
<p><a href="http://repositorio.unicamp.br/bitstream/REPOSIP/275656/1/Domingues_LeonardoRodrigo_M.pdf">GPU Optimization of Bounding Volume Hierarchies for Ray Tracing</a></p>
<p><a href="https://patents.google.com/patent/US9817919B2/en">(US9817919B2)</a></p>
<div align="center">
<img src="figures/distance_matrix.png" width="512">
</div>
<p>TRBVHでツリーレットの再構築を行う部分にAgglomerative Clusteringを利用したものです。TRBVHよりほんの少し品質が落ちるものの、高速に実行することができます。AACと同様にDistance Matrixを使って最適なノード(ツリーレットにおけるリーフ)の組み合わせを探すという考え方さえ押さえておけばよいでしょう。論文中には、限られたメモリのサイズで効率よくDistance Matrixを更新する方法が書かれています。当然ながら、Agglomerative Clusteringの欠点を引き継ぐので、細かくテッセレーションされたオブジェクトに適用し続けても、交差判定のパフォーマンスは逆に下がってしまうことがあります。</p>
<h3 class="mume-header" id="%E5%9B%9E%E8%BB%A2%E3%81%A8%E5%86%8D%E6%8C%BF%E5%85%A5-rotationreinsertion">回転と再挿入 (Rotation/Reinsertion)</h3>
<p><a href="https://www.cs.utah.edu/~aek/research/tree.pdf">Tree Rotations for Improving Bounding Volume Hierarchies</a></p>
<p><a href="http://www.cs.utah.edu/~thiago/papers/rotations.pdf">Fast, Effective BVH Updates for Animated Scenes</a></p>
<div align="center">
<img src="figures/rotation.gif" height="128">
<p>(image from: <a href="https://en.wikipedia.org/wiki/Tree_rotation">https://en.wikipedia.org/wiki/Tree_rotation</a>)</p>
</div>
<p>トポロジーを変更するもっとも簡単手法はRotationで、これを一般化したものはReinsertionと呼ばれます。</p>
<p>SAHを下げるようにRotationを行い続けても実際のレイ・トレーシングのパフォーマンスが上がらないことが多いです。これは、先に挙げたEPOからもわかるようにSAH自体がふさわしい尺度でないこと、またこの操作が実際のシーンにおけるレイの分布を考慮していないことなどが原因として考えられます。ツリーがいたずらに深くなるだけといったことも起こります。</p>
<p><a href="http://dcgi.felk.cvut.cz/publications/2013/bittner-cgf-fiobvh">Fast Insertion-Based Optimization of Bounding Volume Hierarchies</a></p>
<p>Reinsertionを導入した最初の論文です。個人的にはRestructuringよりも可能性を感じる方法です。Reinsertionは現状SAHを最も小さくすることができる手法として知られていますが、処理が本質的にはシーケンシャルであるため、最適化にかかる時間がボトルネックとなります。</p>
<div align="center">
<img src="figures/insertion2.png" width="320">
</div>
<p>この手法は、一つずつノードを取り除き、その2つの子ノードをコストを最も小さくする場所に移すことで徐々にSAHを小さくしていきます。1つのノードを除いてそれだけをほかの場所に移せばいいのでは?と疑問に思いましたが、2つの子ノードを別の場所に移したほうが少し良い結果が得られたとの記述があります。</p>
<p>削除するノードの選択はランダムでも構いませんが、パフォーマンスを上げるには何かしらの基準が必要となります。具体的には、以下の3つ(node inefficiency measure)をもとにノードを優先的に選択して処理を行うことで収束を早めることができます。</p>
<ul>
<li>表面積が大きい</li>
<li>表面積が子ノードの平均表面積に比べて大きい</li>
<li>極端に小さな表面積の子ノードを持つ</li>
</ul>
<p>子ノードの挿入位置は分枝限定法(branch and bound)によって最もSAHが小さくなる場所が選ばれます。</p>
<p><a href="https://dl.acm.org/citation.cfm?id=2902855">Incremental BVH construction for ray tracing</a></p>
<div align="center">
<img src="figures/insertion.png" width="512">
</div>
<p>構築方法ですが、説明のつながりからここで紹介します。プリミティブを一つずつ追加し、徐々にBVHを構築していく方法です。単純に追加していくだけであれば構築されるBVHがアンバランスなものになってしまいますが、まとまった数のノードのInsertionとBVHの最適化を交互に行うことで、それを防いでいます。最適化には<a href="http://dcgi.felk.cvut.cz/publications/2013/bittner-cgf-fiobvh">Fast Insertion-Based Optimization of Bounding Volume Hierarchies</a>の手法が用いられます。</p>
<p>サーバーからクライアントにジオメトリ・データが送られ、クライアント側でインクリメンタルにBVHを構築するアプリケーションが例に挙げられていますが、サーバー側でオブジェクトごとにBVHを構築し、クライアント側で後述のRe-braidingを使ったほうが実装が簡単になるのではないでしょうか。</p>
<p><a href="https://dcgi.felk.cvut.cz/projects/prbvh/prbvh_eg.pdf">Parallel Reinsertion for Bounding Volume Hierarchy Optimization</a></p>
<div align="center">
<img src="figures/teaser.svg" width="512">
</div>
<p>Insertionを並列化することで処理速度と結果のBVHの品質両方を改善する方法です。BVHのトポロジーを変化させるので、並列化する場合にスレッドが衝突しないよう操作中のノードまたはその子や親などロックする必要があります。複数のスレッドが同じ場所に挿入しようとした場合は、最もSAHを小さくするものが優先権を得ます。</p>
<p>論文ではロックの仕方が2つ提案されており、一つはConservative、もうひとつはAggressiveと呼ばれています。Conservativeでは上の図であるようなReinsertionの操作でたどるパス上にあるノードをすべてロックします。Aggressiveではノードの挿入によって実際に変更を受けるノードだけをロックします。また直観に反しますが、Agressiveではシーケンシャルに最適化を行った場合よりも、結果のBVHのSAHをより小さくすることが出来ます。考えてみれば当然なのですが、これは、一つの子孫にあたるノードを取り除いただけでは大して表面積が小さくならなかったノードも、複数を同時に取り除けば表面積が非常に小さくなる場合があり、シーケンシャルの場合よりも最適化のための探索領域が広がるためです。</p>
<p>この論文をしっかり理解すれば先に挙げた2本の論文は特に読む必要はないでしょう。</p>
<h3 class="mume-header" id="%E4%B8%A6%E3%81%B9%E6%9B%BF%E3%81%88%E3%81%A8%E3%83%88%E3%83%A9%E3%83%90%E3%83%BC%E3%82%B9%E9%A0%86%E5%BA%8F-reorderingtraversal-order">並べ替えとトラバース順序 (Reordering/Traversal Order)</h3>
<p>RestructuringとReinsertionはBVHのトポロジーを変えてしまうものですが、Traversal OrderとReorderingはBVHのトポロジーを変えずに処理速度を向上させます。これらは基本的には同じものですが、強いて違いを挙げるならば、Traversal Orderは、どのノードをどういう順で訪れるか、ランタイムでなんらかの計算を行い決定します。Reorderingは構築後一度だけ行えば、トラバース時にオーバーヘッドが発生しない、というのがメリットです。</p>
<p>通常レイ・トレーシングでは一番近いオブジェクトを見つけることが目的であり、Closest-Hitと呼ばれるテストを行います。しかし、影の計算や遮蔽の計算、コリジョン・ディテクションでは何かに当たるということさえ分かれば処理を完了することができるので、交差判定・当たり判定の処理は簡略化することができます。こういったテストはAny-Hitと呼ばれ、子ノードのReorderingによる高速化が可能です。</p>
<p><a href="http://gamma.cs.unc.edu/SATO/SATO_files/sato_preprint.pdf">SATO: Surface Area Traversal Order for Shadow Ray Tracing</a></p>
<p>これはツリーの最適化ではなくトラバースの最適化に関するものです。表面積の大きいノードが影を作ることが多いであろうという前提をもとに、トラバーサルの順番を決めます。もちろんシーン中のライトの位置などが考慮されていないため、常に効果があるわけではなく、状況によっては遅くなることがあります。</p>
<p><a href="http://graphics.cs.cmu.edu/projects/srdh/feltman12_srdh.pdf">SRDH: Specializing BVH Construction and Traversal Order Using Representative Shadow Ray Sets</a></p>
<div align="center">
<img src="figures/srdh.png" width="512">
</div>
<p>より良い方法は、この論文のようにRepresentative Raysを投げシーンに応じた最適化を行う方法です。</p>
<p><a href="http://jcgt.org/published/0005/02/02/">An N-ary BVH Child Node Sorting Technique for Occlusion Tests</a></p>
<p>SRDHはBinary BVHにのみにしか利用できず、Shadow Ray専用のBVHと通常のBVHを保持するのでメモリを余計に消費します。私が考えた方法では、トラバーサルのコスト関数をWide BVHへと一般化し、最適化をノードの並べ替えのみと限定することで、メモリの消費を抑えます。もう少し分かりやすいタイトルにすべきであったと反省しています。</p>
<h3 class="mume-header" id="re-braiding">Re-braiding</h3>
<p><a href="https://embree.github.io/papers/2017-HPG-openmerge.pdf">Improved Two-Level BVHs using Partial Re-Braiding</a></p>
<div align="center">
<img src="figures/rebraiding.png" width="320">
</div>
<p>構築時間を短縮したり、アニメーションを簡単にサポートするためにTwo-Level BVHとよばれる2階層のBVHが使われます。これは、同じオブジェクトに所属するプリミティブ、あるいは何かしらの意味を持つプリミティブの集合ごとにBVHを構築し、それらのルートをリーフとみなして、さらに上位の階層のBVHを構築する方法です。</p>
<p>一部のオブジェクトが動く場合などを効率よく扱うことが出来ますが、オブジェクト同士、インスタンス同士がオーバーラップした場合はレイ・トレーシングのパフォーマンスが著しく低下するのは想像に難くないでしょう。</p>
<p>この論文ではRe-braidingと呼ばれるテクニックを用いて、わずかな追加のメモリと構築時間でオブジェクトが重なった場合のパフォーマンスの低下を防ぎます。基本的にはBonsaiで提案された刈込と同様の処理を行います。</p>
<pre class="language-text">struct Bref
{
BVNodeReference ref;
AABB bounds;
unsigned int objectID;
unsigned int numPrims;
};
</pre>
<p>Re-braidingではBRefと呼ばれるオブジェクトごとのBVHを参照するデータ構造を使用します。まず、各オブジェクトを参照するBRefを用意します。次に、各Brefが参照しているノードのバウンディング・ボックスがある閾値より大きい場合は、そのBRefを削除し、参照していたノードを刈り込んで、その子ノードを参照するBrefを2つ(Binary BVHの場合は2つ、Wide BVHであれば4や8となります)新たに追加します。この処理BRefが事前に決めた数に達するまで、あるいは刈り込むノードがなくなるまで続けます。最後に、出来上がったBRefをリーフとしてBinningを使い上位レベルのBVHを構築します。</p>
<p>オブジェクトごとのBVHはBinningやSweepで高品質なものを作り、それらには手を加えないところがポイントです。またBRefには、参照しているサブツリーに含まれるプリミティブの数をストアしておきます。そうすることで、上位のBVHを作る際、適切にサブツリーのコストを考慮に入れることが出来ます。複数種類のインスタンスがある場合、それぞれの交差判定にかかるコストは当然違いますから、それらをリーフとして扱う場合には注意が必要です。</p>
<p><code>インスタンスはプロダクションのレンダラでは必須の機能ですが、それを扱った論文はそれほど多くありません。Sony Pictures ImageworksのArnoldではシングル・レベルのインスタンシングのみをサポートしています(2018年の執筆時点)。当然マルチレベル・インスタンシングのほうが汎用性が高く、より省メモリで複雑なモデルを扱えますが、セルフ・ヒットを避けるコードや、インスタンスごとに色を変えるシェーダではひと工夫必要になります。</code></p>
<h3 class="mume-header" id="%E9%87%8F%E5%AD%90%E5%8C%96%E3%81%A8%E5%9C%A7%E7%B8%AE-quantizationcompression">量子化と圧縮 (Quantization/Compression)</h3>
<p>BVH自体がレンダリングされるジオメトリに次いで、非常に多くのメモリを消費します。したがって、BVHのノードを圧縮する方法が多数提案されています。</p>
<p><a href="https://pdfs.semanticscholar.org/c329/a3456cb8ed426472d281bea42320a37a711a.pdf">Memory Efficient Ray Tracing with Hierarchical Mesh Quantization</a> <a href="https://patents.google.com/patent/US20110080403A1/en">(US20110080403A1)</a></p>
<div align="center">
<img src="figures/quantization.png" width="512">
</div>
<p>データをQuantize(量子化)するものが一番わかりやすいでしょうか。さらにこの手法ではBVHの中にジオメトリデータをストアします。バウンディング・ボックスの頂点座標を親ノードのバウンディング・ボックスでの相対的な座標で表し、量子化することでメモリを節約します。</p>
<p>さらに、ポリゴンの頂点データなども同様に相対的な座標で表し量子化することで、データ量を大きく削減できます。形が変わってしまうと思うかもしれませんが、BVHが階層的な構造であることを思い出してください。リーフを含むノードは一般的にかなり小さいので、量子化してもそれほど目立つアーティファクトが生じません。</p>
<p><a href="https://embree.github.io/data/compressedleafbvh-hpg-2018-final.pdf">Compressed-Leaf Bounding Volume Hierarchies</a></p>
<p>レイ・トレーシングにおいてはレイとノードとの交差判定が大部分を占め、リーフ・ノードとの交差判定はそこまで多くない、という洞察をもとに、リーフ・ノードのみを圧縮します。</p>
<h3 class="mume-header" id="%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88-better-layouts">より良いレイアウト (Better Layouts)</h3>
<p>キャッシュ・ミスを減らしたり、消費メモリを減らす様々なレイアウトが提案されています。あまり触れている論文がありませんが、巡回セールスマン問題を用いてレイアウト最適化を行うこともできます。</p>
<p><a href="http://gamma.cs.unc.edu/COLBVH/">Cache-Efficient Layouts of Bounding Volume Hierarchies</a></p>
<p>キャッシュ・ミスを減らすようにノードのレイアウトを変える最適化手法です。</p>
<p><a href="https://graphics.tu-bs.de/publications/Eisemann11NMH">Implicit Object Space Partitioning: The No-Memory BVH</a></p>
<p>プリミティブのIDをうまく並べ替えることによってBVHのトポロジーを表現し、消費メモリを完全に0にする手法です。これはImplicit Object Partitioningと呼ばれ、交差判定はSAH等を用いたトップダウン型によって構築されたBVHと比べ遅くなるものの、メモリが限られた環境では役に立ちます。</p>
<p>原理は簡単です。各ノードは図のように2つのプリミティブのIDによって表現されます。2つのプリミティブはX、Y、Zどれかの軸のSlab(広がり)を表します。幅優先で構築された(左に詰めた)完全2分木型のBVHの場合、ノード(インデックスを<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.65952em;vertical-align:0em;"></span><span class="mord mathit">i</span></span></span></span>とします)の左の子ノードの先頭のプリミティブのIDは<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>2</mn><mi>i</mi><mo>+</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">2i+2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.74285em;vertical-align:-0.08333em;"></span><span class="mord">2</span><span class="mord mathit">i</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span>、同様に右の場合は<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>2</mn><mi>i</mi><mo>+</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">2i+4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.74285em;vertical-align:-0.08333em;"></span><span class="mord">2</span><span class="mord mathit">i</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">4</span></span></span></span>として求めることができます。こうすることでAABBのデータと子ノードへの参照が不要になるわけです。</p>
<p>構築時Slabの軸はモートン・コードのように<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>x</mi><mi>y</mi><mi>z</mi><mi>x</mi><mi>y</mi><mi>z</mi></mrow><annotation encoding="application/x-tex">xyzxyz</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="mord mathit">x</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord mathit" style="margin-right:0.04398em;">z</span></span></span></span>とラウンド・ロビンで決めます。メモリを使わないこのようなBVHの品質を上げるという後続の研究がないように思われるので、いろいろ試してみても面白いかもしれません。</p>
<div align="center">
<img src="figures/zero.png" width="512">
</div>
<h3 class="mume-header" id="%E3%83%8F%E3%83%BC%E3%83%89%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%AB%E3%82%88%E3%82%8B%E3%82%82%E3%81%AE-hardware">ハードウェアによるもの (Hardware)</h3>
<p>これは理論的な部分ではないので、いくつか論文を羅列するにとどめます。時間ができれば読んで解説を追記したいと思います。</p>
<p><a href="https://tutcris.tut.fi/portal/files/11500579/egsr_preprint.pdf">Fast Hardware Construction and Refitting of Quantized Bounding Volume Hierarchies</a></p>
<p><a href="http://www.tut.fi/vga/publications/PLOCTree_A_Fast_High-Quality_Hardware_BVH_Builder.html">PLOCTREE: A FAST, HIGH-QUALITY HARDWARE BVH BUILDER</a></p>
<p><a href="https://tutcris.tut.fi/portal/files/13191965/paper.pdf">MergeTree: A Fast Hardware HLBVH Constructor for Animated Ray Tracing</a></p>
<p><a href="http://www.cemyuksel.com/research/papers/dual_streaming_hpg2017.pdf">Dual Streaming for Hardware-Accelerated Ray Tracing</a></p>
<h2 class="mume-header" id="%E3%83%97%E3%83%AD%E3%83%80%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E7%89%B9%E6%9C%89%E3%81%AE%E8%A9%B1%E9%A1%8C">プロダクション特有の話題</h2>
<h3 class="mume-header" id="%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%83%A2%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%96%E3%83%A9%E3%83%BC-animationmotion-blur">アニメーションとモーション・ブラー (Animation/Motion Blur)</h3>
<p>BVHがレイ・トレーシングのデータ構造として主流になったのには理由があります。考え方自体が非常に単純でありkd-treeよりロバストであること、またプロダクション・レンダラで必須のモーション・ブラーの実装がしやすい、などが挙げられるでしょうか。</p>
<p>過去RotaionやRefittingの組み合わせでアニメーションに対応する方法が提案されてきました。しかし、これらの手法は多数のCPUコアでスケールさせるのが意外に難しく、プロダクションレンダラではモーション・ブラーが適応されたオブジェクト専用のBVHを構築することが多いです。専用のBVHを利用する場合、メモリの消費が2倍近く、あるいはそれ以上になってしまうので、個人的にはあまり好きではありません。</p>
<p><a href="http://dcgi.fel.cvut.cz/home/bittner/publications/abvh-eg2015.pdf">T-SAH: Animation Optimized Bounding Volume Hierarchies</a></p>
<div align="center">
<img src="figures/tsah.png" width="320">
</div>
<p>アニメーションに対応するため、SAHを時間軸に拡張したものです。コスト関数はすべてのフレームのSAHコストを重み付き平均したもので、これを最小化するようにReinsertionによる最適化を行い、単一のBVHをすべてのフレームに使用します。</p>
<p>ノードを取り除いて最適な場所に挿入しますが、どのノードを選択するかによって、パフォーマンスは大きく変わります。バウンディング・ボックスの表面積を時間軸で積分した値に比例して選ぶのが良いですが、Metropolis-Hastingサンプリングによってインポータンス・サンプリングを行います。前回選んだものと同じノードが選ぶのは効率が良くないので、違うノードが選ばれた時だけアクセプトします。よって、選ばれたノードのサンプルは完全に積分値の分布に従うわけではありません。</p>
<p>また、1つのノードを挿入して重みを更新するのは効率が悪いのである程度のノードをまとめて処理したのち、重みを一気に更新します。論文では、このバッチのサイズは、BVHで使われているノード数の2%としています。</p>
<p><a href="https://fpsunflower.github.io/ckulla/data/2018_tog_spi_arnold.pdf">Sony Pictures Imageworks Arnold</a></p>
<p>モーションブラー用のBVHはこちらの文献にあるように、各ノードがモーションパスの両端に相当する2つのバウンディング・ボックスを持っています。交差判定中はそれらが適宜線形補間されます。</p>
<p><a href="https://www.solidangle.com/research/Arnold_TOG2018.pdf">Arnold: A Brute-Force Production Path Tracer</a></p>
<p>当然といえば当然ですが、Solid AngleのArnoldも同じアプローチです。</p>
<p><a href="http://gruenschloss.org/msbvh/msbvh.pdf">MSBVH: An Efficient Acceleration Data Structure for Ray Traced Motion Blur</a></p>
<div align="center">
<img src="figures/msbvh.png" width="512">
</div>
<p>Spatial Splitsを使って構築した場合はプリミティブ(厳密にはその参照)が複製され、複数の別のノードから同じプリミティブが参照されます。プリミティブが1つのノードのバウンディング・ボックスだけに収まらず、複数にまたがるためです。言い換えれば、一つのプリミティブへの参照が複製されます。静的なシーンであればこの扱いはそれほど難しくはないのですが、モーション・ブラーが関わってくると厄介です。</p>
<p>この論文ではSplitsを使いながらもノードの重なりを減らす方法を提案しています。</p>
<p>はじめに、時間を<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mo>=</mo><mn>0.5</mn><mo separator="true">,</mo><mo>(</mo><mn>0</mn><mo><</mo><mo>=</mo><mi>t</mi><mo><</mo><mo>=</mo><mn>1</mn><mo>)</mo></mrow><annotation encoding="application/x-tex">t=0.5, (0<=t<=1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.61508em;vertical-align:0em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">0</span><span class="mord">.</span><span class="mord">5</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mopen">(</span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span></span><span class="base"><span class="strut" style="height:0.36687em;vertical-align:0em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.65418em;vertical-align:-0.0391em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span></span><span class="base"><span class="strut" style="height:0.36687em;vertical-align:0em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span>で固定してSBVHを構築し、BVHのトポロジーを決定します。ポリゴンはSplit(切断)されるため、バウンディング・ボックスとポリゴンの交点を求める必要がありますが、それらを重心座標系で保持しておくと、モーション・セグメントの両端点(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mo>=</mo><mn>0.0</mn></mrow><annotation encoding="application/x-tex">t=0.0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.61508em;vertical-align:0em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span><span class="mord">.</span><span class="mord">0</span></span></span></span>と<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mo>=</mo><mn>1.0</mn></mrow><annotation encoding="application/x-tex">t=1.0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.61508em;vertical-align:0em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span><span class="mord">.</span><span class="mord">0</span></span></span></span>)での座標も容易に求めることができます。両端でのバウンディング・ボックスは両端での交点を全て含むようフィットさせます。この両端でのバウンディング・ボックスはクリッピング・ボックスと呼ばれます。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>0</mn><mo><</mo><mi>t</mi><mo><</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">0<t<1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68354em;vertical-align:-0.0391em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.65418em;vertical-align:-0.0391em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>では2つのクリッピング・ボックスを補間し、それを用いて交差判定を行います。</p>
<p>バウンディング・ボックスの重なりが完全になくなるのではありませんが、Spatial Splitsを使用したBVHでもモーション・ブラーを効率よく扱えるようになります。</p>
<div align="center">
<img src="figures/degenerated.png" width="320">
</div>
<p>この手法には考えられる欠点が2つあります。一つ目は、図のように<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mo>=</mo><mn>0.5</mn></mrow><annotation encoding="application/x-tex">t=0.5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.61508em;vertical-align:0em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span><span class="mord">.</span><span class="mord">5</span></span></span></span>の時にポリゴンが縮退する場合、Clippingがロバストにできないであろうということ、二つ目は、前述のように<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>0</mn><mo><</mo><mi>t</mi><mo><</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">0<t<1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68354em;vertical-align:-0.0391em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.65418em;vertical-align:-0.0391em;"></span><span class="mord mathit">t</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>ではクリッピング・ボックスを補間しますが、t=0.5付近では補間されたバウンディング・ボックスがタイトにフィットしないということです。実際あまり起きないのではと思うかもしれませんが、モーション・グラフィックスなどでの使用も考えると、不安要素は取り除いておきたいものです。</p>
<p><a href="https://embree.github.io/papers/2017-HPG-msmblur.pdf">STBVH: A Spatial-Temporal BVH for Efficient Multi-Segment Motion Blur</a> <a href="https://patents.google.com/patent/US9430863">(US9430863B1)</a></p>
<p>モーション・セグメントが1つではなく、複数の場合へと拡張しています。構築の際には、プリミティブを時間軸でグループ分けするか、オブジェクト単位でグループ分けするか、交差判定の負荷が小さくなるように決定します。T-SAHではバウンディング・ボックスの再フィッティングが必要ですが、この手法では必要ありません。</p>
<h3 class="mume-header" id="%E8%AA%A4%E5%B7%AE-robustness">誤差 (Robustness)</h3>
<p><a href="http://jcgt.org/published/0002/02/02/">Robust BVH Ray Traversal</a></p>
<p>交差判定の誤差は厄介な問題です。BVHはkd-treeに比べロバストですが、それでも問題が生じます。この論文ではその原因と対応策について述べています。</p>
<h2 class="mume-header" id="%E8%AA%B2%E9%A1%8C-open-problems">課題 (Open Problems)</h2>
<p>非常に多くの研究が行われてきていますが、まだまだ答えのはっきりしない問題がたくさんあります。研究テーマとして挙げておきます。</p>
<h3 class="mume-header" id="%E8%A4%87%E6%95%B0%E7%A8%AE%E9%A1%9E%E3%81%AE%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96">複数種類のプリミティブ</h3>
<p>いろいろな種類のプリミティブ、カーブやポリゴン、パーティクルなどをどう扱うかというのはなかなか難しい問題です。一種類のプリミティブだけを扱うBVHを複数構築するのがよいか、すべてのオブジェクトを一緒に扱って一つのBVHを作るか選択肢は2つありますが、それぞれに長所短所があります。</p>
<p><a href="https://projet.liris.cnrs.fr/m2disco/pub/Congres/2014-HPG/2%20Ray%20Tracing/2%20Exploiting%20Local%20Orientation%20Similarity%20for%20Efficient%20Ray%20Traversal%20of%20Hair%20and%20Fur.pdf">Exploiting Local Orientation Similarity for Efficient Ray Traversal of Hair and Fur</a></p>
<p>この論文では、毛をうまく扱うために、毛の向きに沿うようバウンディングボックスをトランスフォームするという方法が提案されています。しかし、行列をストアするので追加で必要となるメモリが問題となってきます。また、過度にカールした髪の毛の扱いも厄介です。</p>
<h3 class="mume-header" id="%E7%9B%AE%E7%9A%84%E5%88%A5%E3%81%AEbvh">目的別のBVH</h3>
<p>現状、一般的には目的別(交差判定、衝突判定、照明計算)のBVHが用意されます。用途の数を<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>O</mi></mrow><annotation encoding="application/x-tex">O</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.02778em;">O</span></span></span></span>、プリミティブの種類を<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span></span></span></span>、さらに静的・動的の組み合わせを考えると<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mn>2</mn><mo>×</mo><mi>O</mi><mo>×</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">2 \times O \times P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathit" style="margin-right:0.02778em;">O</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathit" style="margin-right:0.13889em;">P</span></span></span></span>のBVHを管理しなければならず、非常に厄介です。どのようにすれば万能なBVHをつくることができるでしょうか?個人的にはこの研究をしたいと思います。最近ではLight Transportでも使われるようですし、複雑さは増していく一方です。</p>
<h3 class="mume-header" id="wide-bvh%E3%81%AE%E6%9C%80%E9%81%A9%E5%8C%96">Wide BVHの最適化</h3>
<p>ここで取り上げた最適化手法はReorderingを除いてWide BVHにそのまま使えないもの、あるいは組み合わせの爆発で使用が現実的でないものばかりです。また最適化はEPOや、シーン内におけるレイの分布を考慮に入れて行うべきですが、それらを見積もるにもコスト(時間とメモリ)がかかるので、SAHを減らすように最適化を行うものが主流となっています。</p>
<h3 class="mume-header" id="%E4%BB%96%E5%88%86%E9%87%8E%E3%81%A7%E3%81%AE%E5%88%A9%E7%94%A8">他分野での利用</h3>
<p>レンダリングでは3次元のBVHが主に使われますが、次元を上げたり下げたりすれば、この記事で取り上げたテクニックは、データベース等の全く別の分野で使えると思います。</p>
<h2 class="mume-header" id="%E3%81%95%E3%81%84%E3%81%94%E3%81%AB">さいごに</h2>
<p>様々な手法を紹介しましたが、いかがだったでしょうか?私の主観が入ったコメントが多くなりましたが、長年やってきて正しいと思うことを述べるように努めました。論文は紹介した以外にもまだまだありますので、後日追記したいと思います。また、知っていることを全て書いたわけでもありませんが(研究に取っておきたいアイデアや仕事を通じて知ったこと等書けないこともあります)、この記事がBVHに興味を持ってもらえるきっかけとなれば幸いです。また、BVHよりもさらに優れたデータ構造が存在することも十分考えられますので、固定観念にとらわれず、いろいろなアイデアを自由に試してみてください。</p>
<p>一緒に研究したいという方、学校などで話して欲しいという方は<a href="https://twitter.com/ShinjiOgaki">ご連絡</a>ください。</p>
</div>
</body></html>