復刻!レイトレーシング!

昔はやたら長い時間のかかったレイトレーシングを 現在のPCでやったらどのくらいで計算できるのか、 気になったのでやってみました。

レイトレーシングという3D CG技術があります。目に飛び込んでくる光(ray)を逆にたどる(tracing)ことで、リアルな、とは言っても今ではいかにもコンピュータっぽい3D CGを描くというものです。

このレイトレーシングでは非常に計算、描画に時間がかかります。またその手法自体はとても古く、8bits時代から雑誌や書籍にプログラムが掲載されていました。当時のパソコン少年にとっては、そのプログラムを苦労して入力し下手をすると数日間パソコンを動かすことで手に入るCGは、ある種のステータスだったと言っても過言ではないでしょう。しかし描画にあまりに長い時間がかかるため、もちろんマルチタスクなど無い時代ですから、途中でこらえきれずに計算を打ち切ってしまう少年・少女も多かったと思われます。

そんなレイトレーシングを現在のPCで実行したらどうなるのだろう? そう思う1つのきっかけの記事がありました。いまから5年ほど前に書かれた記事なのですが、「MSX2+パワフル活用法」という本に掲載されたBASICで書かれたレイトレーシングのプログラム(=レイトレーサー)をC#に移植して実行したところ、16時間かかってた描画が30秒程度で終わったというものです。これと同じ本が自分の書棚に眠っていたのを思い出したのです。移植先は並列処理が得意なgolangが良いでしょう。3D CGは一般的には並列処理に向いた計算ということができますから、その性能を遺憾なく発揮できるでしょう。

さて移植に際してくだんのBASICプログラムを眺めてみたところ…

「あーこれだめだわ」

MSX BASICには構造化という概念が一切無いため、レイトレーシングにとって本質的な計算構造はほぼ失われています。もちろん「どのように計算するのか」は超具体的にコードとして残っていますが、「なぜこのように計算するのか」は完全に失われているのです。なのでそこから構造を再現し別言語へ移植するのは、3D CG描画の知識があるとはいえレイトレーサーを一度も書いたことのない私には、相当に困難だと判断しました。しかしこの程度で諦めるほど私もヌルくはないのです。

「書いたことがないのなら、書いちゃえばいいじゃない」

とはいえゼロベースもなんですから、なにか良い教材はないかと考え始めたところ、今ならJavaScript+HTML5(Canvas)でレイトレできるなということに気がついて、ググったら Tiny Raytracer が見つかりました。このTiny Raytracerは圧縮すると1KBを切るコンパクトさで、しかも圧縮前のソースが公開されています。これを golang へ移植することで経験を積ん…ってことで移植しました

オリジナルの Tiny Raytracer はブラウザ上で実行して5から6秒というところ。一方、golang移植版は並列化前で1.2秒、4並列化して0.3秒ほど。使っていたPCが4コアであること、加えて各行を1つのgoroutineに描画させるお手軽並列化を採用したこと、もろもろ考え合わせるとgolangの並列化のオーバーヘッドの少なさが際立ちます。golangのソースコードの書き方としてはかなりベタな感じで褒められないのですが、この移植でレイトレーサー実装の勘所は押さえられました。

さぁいよいよMSX BASICからgolangへの移植…できました。今回はソースを分けたりデータ構造を定義したりと少しはちゃんと書きました…が速度の方はあまり考えていません。で、その描画結果はこちら。

書籍MSX2+パワフル活用法の巻末にあった別データのものですが、いかにもCGっていう感じで良いですね。

で肝心の実行時間ですが…200msec(=0.2秒)でした。書籍にはシンプルな4つの球体で16時間かかるとありましたので、ほぼ倍のオブジェクトがあって反射も多いこちらでは、少なく見積もっても倍の32時間はかかることになります。とある報告には、2泊3日の修学旅行から帰ってきても終わってなかった、というものがありましたが、まぁ32時間で計算しますと…57万倍?! しかもこれはまだ並列化していませんので、先の事例に基づけば4倍になるはずですから…推定で 200万倍 か… 技術の進歩ってすげー

と、感心したところでふと気が付きました。おう、これJavaScriptに移植したらどうなんだよ? 気になったから、やりました。下のボタンで実際に試せますのでやってみてください。



(ここに描画にかかった時間が表示されます)

ということで…

Let’s ray tracing!


2014/04/15 21:20 追記

  • 64bit版のgolang使ったら、32bit版では200msecだった計算時間が80msecになりました。つまり…144万倍(1コア)から576万倍(4コア時)になりますね。
  • 誤字脱字を修正しました。
  • JS版の床部分の模様がgolang版と違っていたのを修正しました。