仕組みから考えるレンダリングの高速化[Blender Cycles Rendering]


# 初めに
Blender Advent calendarの初日を担当させていただきます。実は、Blender歴、数ヶ月です。まだまだ初心者なんですが、出会ってからここまで、その凄さを何度も時間したのでこれが無料で使えるの?? という凄さをより多くの人にお伝えできればと思いながら記事を書いています。今日以降、25日間なるべく多くの方がBlenderについてその魅力や、便利な使い方、ご自身が作られたものの解説など記事を書いていただけて、コミュニティーの盛り上がりに寄与できればと思っています!!!

# Blenderのレンダリングアルゴリズム
前書きが長くなりましたが、この記事ではBlenderのレンダリング(特にCycles)の仕組みについて簡単に説明し、どうずればそのレンダリングの時間を短縮できるのかを説明していきます。

## 動作環境
Blender2.81
Windows 10

## レイトレーシングとは?
Blenderのcyclesでは、Integratorsというレイトレーシングのアルゴリズムが使われている。そこで、まずレイトレーシングについて説明していきます。

身の回りにある、コップや、机には色がありその色を私たちは、光を通して認識しています。真っ暗な場所では、色も形も目やカメラで認識することはできません。このように、目で何かを認識するためには、その物体から、私たちの目にやってくる光をが必要です。

以下の図のように、机にりんごが乗っている状況を考えてみます。
以下の図のように、机にりんごが乗っている状況を考えてみます。([画像はこちらから引用](https://s2.best-wallpaper.net/wallpaper/2560x1440/1310/Old-book-red-apple-desk-window-dry-leaves_2560x1440.jpg))
![Old-book-red-apple-desk-window-dry-leaves_2560x1440.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/a2e9e6e4-498d-42db-9ef4-b7759eb4d36d.jpeg)

このとき光の経路は、光源である部屋のライトなどから光が出て、リンゴの表面で反射して私たちの目に入ってきます。もちろん、リンゴに当たった光の大半は、私たちの目には入らず、どこか別の方向に飛んでいきます。


同様に、コンピューターを使って光がどこからきて、どの方向に向かって進むかを計算し、カメラに入ってきた**光の色**を現像する画像の各ピクセルごとに求める作業がレンダリングでした。([画像はこちらから引用](http://www.infonet.co.jp/ueyama/ip/glossary/pixel.html))
<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/51a8fe8d-34ab-091b-47c2-0729b906fc22.png" alt="属性" title="タイトル">
</div>

しかし、コンピュータで光源から出る全ての光に対して、どの光がリンゴのどの表面にあたりどの方向に進むか計算することは、効率が悪いです。このような計算をすると、カメラに入らない光も、一旦計算する必要が出てしまいます。
<img width="654" alt="スクリーンショット 2019-12-01 18.41.12.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/55b2c2c5-cffc-715d-641f-79a244f85a64.jpeg">
そこで、目に飛んでくる光だけについて効率的に計算しようとするがレイトレーシングの方法です。レイトレーシングでは、下図にあるようにカメラに入る光の経路を逆に辿り、目にはいいってきた光はどこからきたのか、光源はどの物体なのかを計算します。([画像はこちらから引用](https://developer.nvidia.com/))。
<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/b1c2849c-72ba-52f0-5be6-c09f52847586.jpeg" alt="属性" title="タイトル">
</div>
レンダリングの際に、必要なのは、カメラに入った光で、カメラに入ってくる光の経路は、逆にカメラからレイを出すことによって反射の法則から予測できます。(ここでは、光源から出る光とレンダリングを行うためにカメラから出す光を区別するために、レイという言葉を使うことにします。)光の経路を辿った先に、すぐに光源がある場合は、レイが初めに当たった物体の表面は明るいはずです。また、光源の色が、白であれば物体の表面も白くなるはずです。逆に何度も反射を繰り返して、光源にたどり着くような場合は、初めにレイが衝突した物体の表面は暗いはずです。

このような作業を現像する画像のピクセル数の分だけ繰り返すのが、レイトレーシングによるレンダリングです。カメラから現像する画像の各ピクセルに対して、レイを出して、どの物体のどの表面に当たるか、そして、光源はどの物体なのかを高速に計算していきます。([画像はこちらから引用](https://www.techspot.com/article/1888-how-to-3d-rendering-rasterization-ray-tracing/))

```
スクリーンの全てのピクセルを順番に取り出しそれぞれのピクセルについて {

  カメラから取り出したピクセルに向かうレイを定める;
  このレイの直線上に物体があるかどうか判定;
  レイの直線上に物体があるなら {
    レイがはじめにぶつかる物体の表面の色を求める;
    反射の法則から、さらにレイ追跡し、物体の表面の色の明るさと色を求める;
    ピクセルの色を決定する;
  }

  レイの直線上に物体がない場合 {
    ピクセルの色を背景色にする
  }

}
```
![images.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/f3ec2d49-fef1-b847-0f70-800c56e058ef.png)


では、実際にBlenderを通してレンダリングの時間を短縮する方法について詳しくみていきます。

## 計算の速度をあげる

一番初めに考えられるのは、前途の光の経路を素早く求め、色を決定するための計算を行うコンピューターのスペックをあげることです。レンダリングのような単純な作業を何度もたくさん行う場合、特定の複雑なタスク処理に特化した、CPUではなくGPUによる計算が処理を高速化してくれます。Blenderでは、2.8以降のバージョンでCPUとGPUを両立したレンダリングや、2.81以降は、レイトレーシングに特化したNvidiaのGeForce RTXシリーズによるOptiXによるレンダリングが可能になりました。後者の[GeForce RTX](https://www.nvidia.com/en-us/geforce/20-series/)を使ったレンダリングは、従来のCUDAによるレンダリングに比べ2倍以上レンダリングの時間を高速化できる場合もあり、非常に魅力的です。(また、名前にRTXはつかないもののAWSやGCPなどで提供されている[Nvidia Tesla T4](https://www.nvidia.com/en-us/data-center/tesla-t4/)もRTXに対応しています。) ([画像はこちらから引用](https://techgage.com/article/testing-blender-2-81-alpha-with-nvidia-rtx/))
![Blender-2.81-Alpha-Enable-OptiX-In-Preferences.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/c35cb4ef-8837-9d2b-2cbb-82ea81dda75c.jpeg)

また、型落ちの複数のGPUを[SLI](https://en.wikipedia.org/wiki/Scalable_Link_Interface)を使って、接続することでコスト面で抑えつつ、処理能力を向上させることができます。(2019年現在コンシュマー向けに販売されているものでは、RTX 2080 Tiを複数枚搭載するのが一番レンダリングの速度を高速化できると思われます。)

また、GPUやCPUの計算の効率をあげるためにBlenderでレンダリングを行っている最中はネットワークを切ったり、他に必要ない常駐のソフトウエアを止めることもおすすめです。

## 画質(画素数)を下げる
画質を下げればレンダリングの速度が上がることは多くの方が知っているかもしれません。これについてもレイトレーシングの観点から説明しておきます。画質が低い画像は、ピクセル数が少ないので、カメラから放出して追いかけるべきレイの数が少なくなるからです。そのため、計算する回数が少なくなり、結果としてレンダリングもすぐに終えることができます。
<img width="268" alt="スクリーンショット 2019-12-01 1.03.00.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/6a3eb1d9-100c-33ed-9d84-2ba03e029753.jpeg">

## バウンスを下げる

バウンスとは、光が壁や物体などにぶつかり反射することです。レイトレーシングでは、カメラから光源に向かって、光をたどり初めにレイがあたった物体の表面の明るさを決めていきます。一部の光は、壁と物体の間を何度も反射してからカメラにたどり着く場合もあります。このような何度も反射を繰り返してカメラに到達する光を考慮することは現実により近い表現を産む一方、長い時間計算をし続ける必要があります。そこで、通常は、この反射の回数に制限をつけて、ある数以下の反射で到達できる光源からの光にのみを考慮してレンアリングを行います。当然、バウンス(反射)を物体の表面での反射のみだけにして、間接的な光を考慮しない、つまりバウンスを一度しか考慮に入れないのであれば、レンダリングが素早く可能ですが、自然の表現からずれてしまうことになります。

![スクリーンショット 2019-12-01 12.23.29.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/17091fdd-3e45-c782-5584-259a5145b396.jpeg)

![images.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/f3ec2d49-fef1-b847-0f70-800c56e058ef.png)

各値を変更することで、レンダリングの結果がどのように変化するのかについては、以下の記事を参考にしてください。


## Volumesのステップを減らす

これは霧や煙などをレンダリングする際に特に有効です。Volume Objectをどこまで細かく、詳細にレンダリングするかを決定します。

![スクリーンショット 2019-12-01 12.56.01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/1f92c4cc-6b14-9dda-6140-62d87239a0be.jpeg)

それぞれの値を小さくすることで荒い、表現になってしまいます。

[Question about volume samples](https://blender.stackexchange.com/questions/111174/question-about-volume-samples)


## サンプル数を下げる

サンプリングの話に入る前に、まずはコンピュターで物体の表面での反射についてもう少し詳しく説明していきます。CGで物体の表面と光との相互作用を記述する際には、その反射の仕方で大きく三つに分けることができます。

<img width="300" alt="physical" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/6543fe6d-ffd2-b652-d856-ef769ea01c3a.jpeg"> <img width="300" alt="physical" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/5f1c085e-8d81-de41-fc39-42413b42b5b8.jpeg"> <img width="300" alt="physical" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/a7b2560f-6bdd-c591-b581-70e23ff36a4c.jpeg"> 

順に、環境光による反射、環境光による反射に拡散反射を加えたもの、さらに鏡面反射を加えたものを列挙しました。
環境光は、周囲からくるどの方向にも一様な光のため、物体の表面の色と明るさは一様になります。一方で、拡散反射は、方向性のある点光源から光が、物体の表面(**内部で多重に散乱して様々な方向に**)で反射するため、カメラから見た物体の明るさは、光源の位置と物体の表面の法線方向によって決まります。最後に、鏡面反射は、物体の表面での反射でキラッとした光沢が生じる原因です。

一番わかりやすい、拡散反射の例からもわかるように、現実の世界での物体表面での光の進行方向は、何度も内部で反射を繰り返すことで、様々な方向に光が放出されます。([画像はこちらか引用](https://slidesplayer.net/slide/11249888/))
<img width="302" alt="スクリーンショット 2019-12-01 0.27.04.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/b5c0fb96-dc9e-7eda-95e9-5c265e7f3805.jpeg">

このような複雑な過程の光をコンピュターで全て追いかけているとやはり時間がかかってします。なので、普通は、表面での散乱の際に光がどの方向に進むかを実際に拡散反射の光の経路を追うのではなく、ランダムに進む方向を決めてしまって、レイトレーシングを続行します。
しかし、ランダムに決定していますと実際の自然とは異なる色になってしまう恐れがあります。
そこで、このランダムに光の反射する方向を決める試行を何度もやってその平均の値を取ろうというのが、サンプルの発想です。

サンプルの回数が少なければ、その分レイトレーシングの回数を減らすことができるので、レンダリングの時間を削減することができます。一方で、サンプリングの回数が小さと、ノイズと呼ばれる実際とは異なる色がレンダリングされてしまう結果になります。

![スクリーンショット 2019-12-01 13.20.34.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/046a1460-d926-04b0-39fd-02492473d1b8.jpeg)

Blender2.81では、DenoiseというCompositer nodeが導入されていて、これを用いることでノイズをかなり軽減できます。なので、サンプル数を減らしつつDenoiserを用いるのがおすすめです。![スクリーンショット 2019-12-01 13.23.19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/ad2b1689-2ed4-1e90-8e97-2d95146d326b.jpeg)

# 最後に


じゃあ、上記のことに注意したらどのくらいレンダリングの時間が変わったの?? と思われるかもしてません。僕の場合は、もともとMacでレンダリングを一番初めにしてみて、1フレームあたり42時間という恐ろしい結果が得られてから、PCのスペックや地道な改善を繰り返し、ほぼ同じようなアウトプットを今の環境では、5分程度で得られるようになりました。(大体500倍の改善が見られました。色々な方に教えていただいたおかげです!)
さらに、クラウドでのレンダリングを利用すると、ここからさらにレンダリングの時間を5~6倍程度早めることができています。諦めずに地道な改善を重ねることが大切、でもやっぱりハードが一番大切だと感じています。

ここまで色々な方法を紹介してきました。ここに載っていないようなレンダリングの時間を短縮する方法はいくつかあると思います。(例えばeeveeを利用するなど)
こちらもreferenceの方にいくつか載せておきましたので、参考になればと思います。
また、間違っている点などありましたら、ご指摘いただけると助かります。

# Reference
レイトレーシングに関する記事
- [ray tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics))
- [How 3D Game Rendering Works, A Deeper Dive: Rasterization and Ray Tracing](https://www.techspot.com/article/1888-how-to-3d-rendering-rasterization-ray-tracing/)
- [【Blender】【Cycles】Cyclesでノイズを防ぐ方法の総まとめ](http://light11.hatenadiary.com/entry/2019/03/22/000802)
- [【Blender】【Cycles】パストレーシングの概要をサクッと学ぶ](http://light11.hatenadiary.com/entry/2019/03/19/212215)

バウンスに関連する記事
-- [Cycles Render Settings Explained: Sampling and Light Paths](https://www.youtube.com/watch?v=NhOdnOow6DY)
-- [LightPath](https://docs.blender.org/manual/en/latest/render/cycles/render_settings/light_paths.html)
-- [Light Paths(日本語)](https://cycles.wiki.fc2.com/wiki/Light%20Paths)