目次
TL;DR
Cornell boxというものです。
はじめに
この記事はCCS Advent Calender 2023の20日目の記事です。
昨日の記事は燐酸さんのラブライブ!概論です。ラブライブはいちばん最初のやつ(School Idol Festival?)を数話見た気がします。アイマスとのコラボなど盛り上がっていますし、履修登録したいと思います。
明日の記事はモリブデン先輩の女装サロン(素振り)です。高専出身の私は、高専在学中に終わっている女装ばかり見てきたので、本当の女装というものを知ったとき、"今"を生きられるのかもしれません。Mo先輩は美形なので大丈夫だと思います。(??)
初めましての方は初めまして、そうでない方はいつもTLを荒らして申し訳ありません。
寒さがこたえる今日この頃、いかがお過ごしでしょうか。私はGPUを酷使して暖をとっているので問題ありません。嘘です。CCSのゆういちろう(20)と申します。よろしくお願いいたします。
本記事では、自分がよくアップしている、変な箱の中に球が2つ置いてある画像に一体何の意味があるのかということを、頑張って説明させて頂きたいと思います。基本的にややこしいことは自分も分からないので書きません。
CGの話になりますので、そういった分野に興味がある方は、ぜひお話しましょう!
既にご存じの方は、マサカリの代わりにレイやフォトンをたくさん投げて頂けると幸いです。
で、何なのか
Cornell boxというものです。レンダリング手法の品質をテストするための実験用データとして生み出されました。その名の通り、発案したのはCornell大学のCindyら(初出論文はこちら)です。標準的なものは球の代わりに直方体が立っていることが多いです。
(自分はテストするレンダリング手法の関係で、ガラス球・金属球の方がわかりやすいのでこちらを使用しています)
これで何がわかるのか
CGとは、コンピュータを用いて画像を生成する技術の総称であり、CGにおけるレンダリングとは、空間や物体の形状・材質等を表すデータから、その空間や物体をまるで目で見たり、撮影したり、あるいは絵に描いたかのような画像を生成する技術を指します。
(と思っています。)
このレンダリング、中でもトゥーンな結果ではなく、物理的に正しそうな結果を目指すもの(物理ベースレンダリングと呼ばれます)では、可能な限り、物理的な光の振る舞いを再現する必要があります。しかし、残念ながらコンピュータの演算能力は、光の振る舞いを全て再現するには足りません。そのため、今まで数多の偉大なる諸先生方が"現在使える演算能力で、可能な限りそれっぽい画像を出す"技術について研究され、その結果様々な手法が生まれました。
たぶん黎明期には、結果を見れば確かに今までの手法よりそれっぽい、ということが一目で分かったんだと思います。しかし、計算機の進化とそれに伴う手法の進化のおかげで、レンダリングする対象によってはどこまで正確に光の振る舞いを再現できているのか分からず、評価しづらいという問題が生じました。例えば、レンダリングにおいて、メッシュにその物体のテクスチャを表した画像を「貼る」ことで材質のディテールを表現する手法(テクスチャマッピング)をご存じかと思います。これは革命的な技術で、あるのとないのでは大違いです。しかし、逆に言うと、あまり真面目に光の振る舞いを再現せずとも、現実で似た状況を再現して撮影したテクスチャを貼れば、場合によってはめちゃくちゃリアルな画像ができてしまうのです。これは実用上は非常に嬉しい話ですが、レンダリングアルゴリズム自体の性能評価では逆に困ったことになります。そんなわけで、光の振る舞いを再現していることが一目でわかるようなテスト環境が必要だったのです。
そこで、このCornell boxのような、"光の振る舞いをわかりやすく見られて、現実で似た状況を再現しやすい"テスト環境が発案され、評価にちょうど良いので皆が使うようになった、という背景があると理解しています。
ここで、もう一度Cornell boxのレンダリング結果を見てみましょう。
一見ただ箱の中に金属とガラスの球を置いただけのようですが、注意して見るとそれっぽいと思わせる要素が色々あることに気づきます。
- まず、それぞれの球の下に影ができています。
- 次に、右奥の金属球に、壁、床、天井、ガラス球などが映りこんでいます。
- また、左のガラス球では屈折した後ろの壁や照明と、反射した照明や左の壁が映りこんでいます。
- さらに、見慣れないかもしれませんが、ガラスの屈折によって照明の光が集まり、ガラス球の下が明るくなっています。
(集光模様、コースティクスと呼びます。プールの底がキラキラしているアレ、と言えば伝わるかもしれません。) - 加えて、壁や床が照らされているのはもちろんですが、その照らされ方も、照明からの距離だけでなく、照明の位置や形、あるいは照らされる物体の形に基づくものになっています。例えば左右の奥、角のくぼんでいる部分はちゃんと暗くなっています。
これの何がすごいのか
いやそんなん普通にシミュレーションしてるだけちゃう?と思われるかもしれませんが、前述の通り光の振る舞いの再現というのは膨大な計算量を要します。ここでは、
- 今主流の手法はどんなものか
- そもそも光の再現とかどうやるのか、なぜ膨大な計算量を要するのか
- やり方がわかったとして、現実的に近似するためにはどうするのか
ということについて書いていきます。
1. 今主流の手法はどんなものか
今現在、皆様の手元で動作するスマホ, ゲーム機, PC等のゲームでは、ラスタライズという手法が一般的に用いられています。これは簡単に言うと
- 3次元形状を、それが映るスクリーンに射影し、各形状に対応するスクリーン上の画素を求める
- 1で求めた画素を、その形状がもつ幾何情報・材質情報と光源の情報に基づいて塗る
のような流れで行われるレンダリングです。特にこの1.がラスタライズであり、2.は一般にシェーディングと呼ばれるものです。
points of HASHORI
また、シェーディング及びシェーダという単語は定義が広すぎるので、ここではいわゆるフラグメントシェーダの話に絞っています。個人的には、昨今の表現技法のことをなんでもかんでもシェーダと呼ぶ風潮は逆に混乱を招いている気がします。また一方、そもそもグラフィックスAPIについても、頂点シェーダ、コンピュートシェーダあたりに関しては、直接的には陰影付けに携わっていないので、どうなのかなぁと思っています。レイトレAPIまでくるともはや広すぎて何でもありです。OpTiXのProgramという表現の方が正しい気がしますが...
さて、この手法は高速です。古くはスーパーファミコンのスターフォックスとか(ちゃんと知りません...)から使われており、今でもPCやPS5, XBox Series S/Xの一部の最先端ゲームを除いて現役です。
問題は、なぜ高速か、ということです。この手法では、光の振る舞いの再現に類することを行っているのは2のシェーディングの部分ですが、その時点で、手元に残っている情報はスクリーンに射影された情報のみ、さらにGPUがこれを並列化して実行するため、隣の画素に対応する幾何情報すら簡単には手に入りません。つまり、ラスタライズでは光の振る舞いを計算する段階で、その画素に対応する形状・材質と、直接渡した光源などの情報以外を捨てているのです。
ちなみに、Blenderにおけるラスタライズベースのレンダラ、Eeveeを使用してCornell Boxのレンダリングを行うと、このようになります。(あくまで、本来のデータに付属するマテリアル情報のみを使用したことに注意してください。また、レンダリング時のBSDFの設定は1回のラスタライズによる処理に限ったものとしています。なお、光源に関しては点光源を天井の光源の位置に配置することで、擬似的に再現しています。)
えらいことになりました。影がないせいで現実感が全くないうえに、金属球もガラス球も死んでしまいました。それもそのはず、床や壁の画素をレンダリングする際、その画素に映っている点と光源の間に球や他の物体があるかどうか(=影ができるか)が判定できません。さらに、金属球の画素をレンダリングする際、そこに映りこむはずの壁や床の情報は使えません。また、床の画素をレンダリングする際、その上に乗っているガラス球の情報を使えません。そのため、金属球には何も映りこまないし、コースティクスも再現できません。表示される色に関する情報を、直接銀色などに変更することで金属っぽくすることは可能かもしれませんが、前述のような問題をラスタライズのみで普通に解決することは難しいといえるでしょう。
points of HASHORI 2(けっこう重要)
注意
2. そもそも光の再現とかどうやるのか、なぜ膨大な計算量を要するのか
このように、ラスタライズには限界があります。じゃあどうするんでしょうか?
そもそも、今まで触れていませんでしたが、この世のCGはなにもゲームだけではありません。映画・アニメその他、別に数日程度かけても良いCGも当然あります。処理時間に60[fps] ≒ 16[msec]などという制約をかけるから難しくなるのです。時間をかければかけるほど物理的に正確な結果が得られるモデルみたいなものはないのでしょうか?
ということで、わけわからん方程式を載せます。
L_o(x, \omega_o) = L_e(x, \omega_o) + \int_{S^2} f(x, \omega_i, \omega_o)L_i(x, \omega_i)|n \cdot \omega_i| d\omega_i
ブラウザバックはもう少しお待ちください。これは今まで出てきた「物理ベースレンダリング」の根幹である、「物理的にできるだけ正確な光の振る舞い」を表した積分方程式(単純バージョン)です。レンダリング方程式というもので、Kajiya(先生)によって1986年に提唱されました。ある物体のある点$x$から立体角$\omega_o$方向に出る光の輝度$L_o(x, \omega_o)$を求めるものです。
各項の解説(多いので分けました)
$S^2$ : $x$を中心とした単位球領域
$d\omega_i$ : 微小立体角
$f(x, \omega_i, \omega_o)$ : その物体が$\omega_i$方向から入る光をどの程度$\omega_o$方向に散乱(反射|透過)させるかを表す関数、双方向散乱分布関数、Bidirectional Scattering Distribution Function(BSDF)という
$L_i(x, \omega_i)$ : xに$\omega_i$から入ってくる光
$n$ : その物体表面の$x$における法線
$|n \cdot \omega_i|$ : nと入射角の内積の絶対値
(注 : この記事ではややこしさから逃れるため、放射量に関する議論をスキップしています。入る光とか出る光とかあやふやですよね。なので、ややこしいのが嫌な方は$|n \cdot \omega_i|$についてはスキップしていただいて大丈夫です。そうでないなら、今まで言ってきた「光」は放射輝度のことであり、これは入射放射輝度$L_i$を微小放射照度に変換するためのものです。前述のBSDFが放射照度に対する放射輝度の比を表すものであるために必要な補正項です。)
要は
- 全$d\omega_i$から入ってくる光$L_i$に
- 物体の表面$x$で散乱して$\omega_o$に向かって出射する比率$f$をかけて
- 全部足し合わせれば点$x$から方向$\omega_o$に出ていく光がわかるよね
と言っているだけです。(クソ雑)
つまり、これを全部の画素に映っている物体上の点$x$および$x$から画素に向かう角度$\omega_o$について解けばいいわけです。解散!
さて、カンの良い方はお気づきかと思いますが、解散どころか計算回数は発散します(激うまギャグ)。$L_e$, $f$, $|n \cdot \omega_i|$は簡単に求まるでしょうが、$L_i$はどうやって求めるのでしょうか?レンダリング方程式の定義より、以下が成り立ちます。
L_i(x, \omega_i) = L_o(x_2, \omega_i)
ここで$x_2$は点$x$から$\omega_i$方向を見たときに見えている物体上の点です。入ってくる光というのはどこかを出た光であるはずなので、これはいいでしょう。問題は右辺に見覚えがあることです。つまり、この積分方程式は再帰的です。レンダリング方程式は、その全微小立体角について、同じものをもう一度解かなければならないのです。そして、そのそれぞれには今と全く同じことがいえます。...困りました。
points of HASHORI 3
L_i(x, \omega_i) = L_o(x_2, \omega_i)
は成り立つのでしょうか?
答えとしては、「真空なら成り立つ」です。現実の空気では様々な微細粒子によって、必ず少しずつ放射輝度は散乱を起こして減衰しますが、真空状態なら放射輝度は減衰しないので、これが成り立ちます。マジでリアルにやる場合は$x$と$x_2$の間をさらに細かく分割などして、その間の減衰や散乱をシミュレーションしたりします。(ボリュームレンダリングと言いますが、このあたりも本当に奥が深いので分かりません。説明できません。)
3. やり方がわかったとして、現実的に近似するためにはどうするのか
というわけで、ある空間に対するレンダリング方程式の解析解を求めるのはたぶん無理なので、なんとかして近似解を求めることになります。とはいっても、数値積分しようにもやり方は色々ありますし、こういうのを真面目に議論するのは自分の乏しい数学力では不可能です。Let's 小手先! 受け売り! 天下り!!
さて、そもそもやりたいことは光の再現でした。なので、光の振る舞いをイメージしてみます。
- まず、物体表面の凹凸とかの形状情報は光の波長と比べてクソでかいので、幾何光学的に考えます。光は線!w(炎上)
- すると、光は光源を出て様々な物体で0回以上散乱し、最終的にカメラの画素(眼の視細胞)に入る直線的な経路をたどったから見えている、ということがわかります。
- これは逆に言うと、見えている光の来た経路を逆にたどっていけば、見えている光がどんな色/明るさなのかを逆算することができるということです。
あれ、光の経路を逆にたどるというのはレンダリング方程式でいうとどういう意味になるのでしょうか?
材質ごとに散乱の仕方は異なりますが、その物体が拡散反射しやすい物体であればあるほど、物体から光が散乱し得る方向はたくさんあります。つまり、光の経路を光源まで逆にたどるには、その方向のうち1つを、ランダムに選択していることになります。これはレンダリング方程式における、あり得る全入射角$d\omega_i$のうち1つをランダムに選択し、その結果を計算していることにあたりますが、これはモンテカルロ法ですね。つまり、
ある画素に入ってくるような、ランダムな光の経路をいくつか選択し、その経路からくる光を平均すれば、モンテカルロ法によってレンダリング方程式を数値的に解いていることになります。(厳密性は浜で死にました)
この手法を、光の経路"path"を追跡するので"path tracing"、パストレーシング法と言います。つまり、パストレーシング法を用いれば、かなり物理的に正確な光の振る舞いを表現できるというわけです。また、パストレーシング法の利点は、最初に述べた「時間をかければかけるほど物理的に正確な結果が得られる」ことです。モンテカルロ法には、その期待値が必ず真の値に収束し(バイアスがなく)、推定値の分散はサンプル数$N$に反比例して減少する、という特徴があります。したがって、サンプル数を$N$だけ増やせば、結果は$\sqrt{N}$の速度で真の値に収束していきます。(遅すぎる)
おまけ
では、長くなってしまいましたが、パストレーシング法によってCornell Boxをレンダリングした結果を見てみましょう。
はい。これは最初に見た画像です。実は基準としていたCornell Boxの画像は、パストレーシング法によってレンダリングしたものでした。とても長くなりましたが、最近自分がやってるのはこれです。ちなみに、その1画素当たりの経路のサンプル数は30,000程度、手元のRTX3060で10秒程度を要します。
points of KUSODEKA HASHORI
おわりに
これはめちゃくちゃヤバい記事です。何故かと言うと、話の流れを優先して色んなことを適当に書いているからです。しかし、必要悪であるとも思っています。この話の流れがなんとなく頭の隅にあれば、
物理ベースレンダリングのすべてについて述べた書籍(なぜ無料???)や、レンダリング界の神々がインターネットの海に放流している珠玉の知識たちの一例(なぜ無料???)を読む時、すこしとっかかりやすくなるかもしれないと思うからです。「3DCG」というとどうしてもDCCツールやエンジンを使用したゲーム・コンテンツ作成がメインで、レンダリングなんてD&Dでモデルデータを配置するだけでいい、という印象を受けがちですが、その裏にある原理的な部分について知ることで、どの表現が簡単でどの表現は難しいのか、D&Dでモデルを配置したとき、裏で一体何が行われているのか、といった根本的な知識を得やすくなるのではないかなと思います。
...めんどくせえと、思いましたよね!?!?!?!? 大丈夫です!!! そんなあなたのために、ここで話したような内容を実際に体感できる、低レイヤレンダリングAPI"Vulkan"を簡単に使えるライブラリをご用意いたしました!!!!! 本記事に出てくるCornell Boxの画像もこのライブラリで作成しています! それに使ったコードもサンプルとして同梱しています!! 使い方は
- こちらからVulkanをインストール
- CMakeとC++コンパイラ(Visual Studioが楽?)を用意する(CMakeはVSCodeの拡張機能を使うと楽です)
- こちらのリポジトリをsubmodule込みでクローン
- Cornell Boxのモデルデータをこちらからダウンロードして配置
- 実行!
です。また、パストレーシングに関していえば、パストレーシングをCPU上で実行するHaskellのコードも書きました。こちらは環境構築が結構めんどくさいかもしれませんが、C++とGLSLよりすべての処理を明示的に書けているかなと思いますので、コードだけでも見て頂けると幸いです。
冗談はさておき、本記事を通して、変な箱の中に球が2つ置いてある画像にはどんな意味があり、そのどういった部分が重要なのか、ということについて軽く知って頂けたなら幸いです。レンダリング沼に一緒に沈んでくださる方を募集しています。
( こちらより引用、「アウラ、かわりに卒論を書け。」)