穴日記

どうだ明るくなったろう

SIGGRAPH ASIA 2018のCourseに応募して採択されたので発表した話

これはレイトレアドベントカレンダー2018の記事です。でもレイトレの話じゃないです。ポエムです。

qiita.com

はじめに

コンピュータグラフィックスの国際学会であるSIGGRAPH。そのASIA版であるSIGGRAPH ASIAが2018年である今年は東京で開催されました。

sa2018.siggraph.org

東京開催ということで、もともと参加しようとは考えていたのですが、色々あってCourseに応募、採択されたので発表してきました。当日は、以下の内容を nikq (@nikq) | Twitterさんと話しました。

Practical HDR and Wide Color Techniques in Gran Turismo SPORT
http://www.polyphony.co.jp/publications/sa2018/

詳細はAbstractを参照してほしいですが、要するにGran Turismo SPORTというゲーム開発において得られた、HDRや広色域関係の知見を理論から実際の実装まで幅広く共有するという趣旨のCourseです。ここでいうHDRとは近年のTVが対応するようになったHDR出力のことです。

SIGGRAPH (ASIA)のCourseに応募しました、という話はインターネットでもあまりない(特に日本語)ので、一連の経緯や何をしたかを簡単にまとめることにしました。皆もどんどんCourse発表しましょう。

応募までの経緯

さて、最初はSIGGRAPH ASIAで何か発表しようとは全く考えていませんでした。しかし、技師長 (@gishicho) | Twitter さんが所用で飛行機にのったときSIGGRAPH ASIAのお偉いさんと偶然会って何かCourse出してよとお願いされたらしく、我々にお鉢が回ってきました。このようなCourse発表は会社としてのアピールにもなるしいい経験にもなるだろうということで、@nikqさんと自分の二人で何か考えて出そうということになり、応募することになりました。

ところで、Courseとはなんなのでしょうか。これは、本家SIGGRAPHでも盛んに行われているのですが、業界の専門家が1〜複数人集まってなんらかのテーマに基づいて1〜3時間くらいの時間で(場合によってはもっと長い時間かけて)講義、講演の形式で発表を行う、というものです。有名なところでいうと物理ベースレンダリングについての幅広い知見を共有するためのCourse、Physically Based Shading in Theory and Practice(https://blog.selfshadow.com/publications/s2017-shading-course/)やリアルタイムレンダリング(ゲーム業界)における知見を共有するためのCourse、Advances in Real-Time Rendering in 3D Graphics and Games(http://advances.realtimerendering.com/)などがあります。

SIGGRAPHといえばTechnical Paper、すなわち技術論文の発表が有名です。しかし、Course形式による教育的な講演も大変充実しているのが特徴です。(ほかにもTalkなどもありますね)SIGGRAPH ASIAでも本家に比べれば小規模ながら、Course発表枠が設けられています。

以上、Technical Paperはともかく、Courseなら発表できんじゃね?という軽い気持ちから応募することになったのでした。

応募方法

さて、応募することになったわけですが、どうすればいいのでしょうか。実際、応募したことある人なんて周りにはいなかったので応募する方法を探るところからはじめました。

一応SIGGRAPH ASIA 2018のウェブサイトには応募要項(https://sa2018.siggraph.org/jp/submitters/courses)が載っているのですが、これが微妙に分かりづらい。しょうがないのでSIGGRAPH 2018の方のCourseの応募要項も読み合せつつ情報を集めるのでした。

まあその辺りの情報をちゃんと読めばそんなに難しいことは無いです、実は。

まず、一番最初にやることはSubmission Siteへのアカウント登録です。Submission SiteというのはTech PaperやCourseの応募(Submit)をする人が基本的には全員登録するウェブサイトです。ここから各種情報の登録を進めることになります。

んで、締め切りまでにこれこれこういう内容でCourseしたいです、というのをSubmitすることになります。ここでポイントなのは、Tech PaperにSubmitする場合は当然Paperを書き上げ、それをSubmitするわけですが、Courseの場合は発表内容が完全には出来上がってなくてもSubmitできます。
具体的には、タイトル、講演者情報、発表概要、シラバス(概要よりももうちょっと詳しい講演計画)、発表する意義、コースノートが必要になります。

このうち、コースノートが実際の発表に使うスライドや何らかの英語文書(論文の体裁のこともある)なわけですが、実はこれは最終的に使用するものでなくても何とかなります。といっても、まったく何でもいいわけでは無く、我々は以前@nikqさんが作成していたそれなりにちゃんとした英語の発表スライドを流用して使用しました。あくまで、最終的な発表に使うものである必要が無かった、というだけでショボイものでいいわけではありません。

また、シラバスはかなりちゃんと作りました。Chapter 1ではこういう話をして、Chapter 2ではこういう話をして、というような発表概要を箇条書きで仕立てたものです。まあこれも最終的な発表内容とは違ってよいんですけど、きちんとしたものである必要はあるようです。

ともかく、こういった情報をまとめてSubmitすれば応募完了、となります。
ちなみに、2018/7/13が締め切りだったようです(その日に応募した)

採択

Submitしたら採択を待つのみです。採択通知は2018/9/4に来ました。ほんとはもうちょっと早く来る予定だったぽいですが、若干遅れました。内容は、Conditionally Acceptedです。Tech Paperもそうですが、文句なしのAccept!というのはなかなか難しくて、大抵はConditionally Acceptedです。(Courseの実情は詳しくは知りませんが)これは、その名の通り条件付き採択と呼ばれるもので、「基本的には採択してやるし、発表させてやるが、レビュアーの意見をちゃんと反映させる条件を満たすこと」という意味です。

レビュアーという言葉が出てきました。SIGGRAPH ASIAにおいてTech Paperは誰にでも発表させるというわけにはいきません。品質を維持する必要があるからです。そこで、SIGGRAPH ASIAでは応募されたTech Paperは5人の業界のエキスパートによってレビュー(審査)されます。レビューを行うのはレビュアーと呼ばれます。レビュアーはPaperに点数とコメントを付けて返却します。そして、一定以上の点数を受けたPaperのみがめでたく(Conditionally) Acceptedとなるわけです。本家SIGGRAPHも似たような仕組みがあります。細かい仕組みは微妙に異なりますが、学会という枠組みではよく行われていることです。

この、レビューシステムがCourseにおいても存在します。3名の匿名のレビュアーに我々の応募内容が送信されます。応募者はやはり匿名になっており、レビュアーは点数とコメントを付けて返却するわけです。我々のレビュースコアはそれなりに良く、コメントも全体的に評価高かったものの、コースノートの不備を指摘されてやや点数を落とされました。コースノートは最終発表とは違っても良い、というようなことを書きましたが、さすがに不完全すぎたようです。そりゃそうだって感じですね。本気で高いスコアで通すなら、もっときちんとオーガナイズされたコースノートを提出するべきでしょう。

採択から発表まで

採択されたら採択承諾をして最終バージョンを提出しないといけません。これがPublishされるバージョンになります。

とりあえず、レビュアーコメントを大まかに反映させます。実はタイトルに関する指摘があり、修正したものが最終的なバージョンだったりします。しかし一番大変なのはコースノートの提出です。今度のバージョンはPublishされるので不完全なものではだめです。

しかしながら、最終提出の期限はかなり厳しいです。採択通知が来たのが9/4だったわけですが、最終提出期限は9/18です。14日じゃコースノートを完全なものにするのなんて不可能です。(そもそも、発表資料を作ったのが発表一か月前とかです)フルタイムで本業をやってる我々からすると厳しいスケジュールです(もちろん、ちゃんとすごいコースノートを提出してる人もいる)

しょうがないので、コースノートはExtended Abstractのようなものにしました。これは、他のCourse発表者もしばしば行っているテクニック(?)なのですが、PublishされるコースノートはAbstractとシラバスを足したような簡素なものにしておくというものです。これならPublishされてもそんなに恥ずかしくないですね。表紙には、発表終了後にスライドなどをアップロードするウェブサイトのURLを貼っておけば完璧です。

ちなみに、他の例としてはコースノートとして発表スライドを全部付ける、というようなものもあります。これも結構よくある手法です。2ページ程度のテクニカルレポート風のものを付ける人たちもいます。
一番すごいのは、何十ページ(100ページ以上のことも)ある英語論文の体裁でCourseの内容を詳細に記述したものを付けている人たちもいます。すごいですね。

また、Copy Rightsに関する処理とかもあります。ウェブ上でフォームを埋めていくだけなのですが、こんなの知らんかったわって感じだし世の中には知らないことがたくさんありますね。でも言われてみれば世間の論文とかって表紙にCopy Rightsが記述されてますよね。あれをジェネレートする処理です。ジェネレートした文書をコピペします。

発表

雑務が終わったらいよいよ発表準備です。仕事その他を色々やってた結果、発表一か月半くらい前から準備する感じになりました。具体的には、スライド作成と発表原稿作成、さらにはその発表の練習です。

まずスライドを作るわけですが、当然全部英語です。さらに、発表時間は40分です。我々はCourseのなかでも、1時間45分の時間が割り当てられたShort Courseでした。これを二人で50分、40分、くらいの分担でやることになったので自分の分は40分というわけです。
40分の発表というのはそれだけでもそれなりに大変なものですが、全部英語ということでさらに大変なうえに、発表のレベルはSIGGRAPHSIGGRAPH ASIAです。本場のSIGGRAPHのCourseなんかを聞いたことのある人は分かると思いますが、3,40分の発表でも大抵非常に内容が濃いです。また、論理展開や図もちゃんとしてます。というようなことを意識しつつスライドを書いていくわけです。

スライド自体は、1か月くらいである程度形にまとまったのですが、そしたらこんどは口頭発表の準備です。もちろん、全部英語で発表します。これに関しては、最初からある程度割り切って、英語原稿を事前に全部用意して基本的にはそれを読み上げるような形にしました。
もちろん、理想的には原稿は全部頭に入れてスラスラと話せるほうが良いでしょう。しかし、大学時代に教わったことの一つとして、ノンネイティブが英語発表するときは原稿に頼った方が良い、というものがあったのでそうしました。原稿が無いと途中で発表に詰まった時にかなりやばいことになります。というわけで事前に原稿を全部用意しました。
といっても、PC画面をずっとにらめっこするわけではなく多少なりとも聴衆に意識を向けるようにはしました。

で、原稿も出来てきたら事前の発表練習です。発表練習はやりすぎるということはないでしょう。しかし、最後の方はいろいろな進行がかなりシビアになってしまい、あまり発表練習できませんでした。英会話の先生に2回ほど聞いてもらった+自宅で一人でブツブツ発表練習+社内で1回通し練習、程度のものです。これはもう少し回数があっても良かったかも。
自宅では10回以上通しで読み上げ練習していたのですが、これはかなり有効だったようには思います。

本番

まあそんなこんなで本番です。本番の前に、発表者には準備部屋が与えられます。他の人たちがどういうスケジュールだったかは知らないのですが、我々は初日の午前発表だったため、準備部屋は前日割り当てられました。

会場である東京国際フォーラムに行き、適当に受付に聞きつつ割り当てられた部屋に行きます。発表会場とは別の部屋なのですが、1時間ほど割り当てられており、発表者はPCをモニタにつないだりできます。今思うと、別の発表会場のモニタで正しく映像が出たからと言って本番の発表会場で正しく出るとは限らないわけで、なんの意味があったのだろうかという気もしますが、前日に発表会場の雰囲気を知っておくのは当日の緊張を和らげるだろうし、まあ行ってよかったとは思います。(実際、前日まで資料作成や発表準備していたので、時間は少しでも惜しかった)

で、いよいよ当日です。当日は9時発表だったので8時に現地入りしてチケットを貰って発表会場に入りました。運営がグダグダで、発表直前になるまで会場を空けないというダメっぷりだったうえにそもそも参加登録受付に長蛇の列ができてて9時の段階でチケットを受け取れた人があまり多くなかったようです。それでも、時間とともに徐々に参加者は増えていったようです。Pixarの偉い人やHoudiniの偉い人も参加してたらしく、発表をほめていた(伝聞)らしいので良かった良かった。

以下のウェブサイトで発表スライド等は公開です。

http://www.polyphony.co.jp/publications/sa2018/

まとめ

超大変でした。SIGGRAPHとかでめちゃくちゃ立派なCourseしてる人たち、本当に頭が下がります。すごいです。英語力の分を差し引いても、とても大変です。しかし、英語スライドつくって英語発表するというのはいい経験になったような気がします。みんなも挑戦してみると良いのではないでしょうか。おわり。

Blue-noise Dithered Samplingの実験をしてみた

これはレイトレ合宿4!?アドベントカレンダー記事です。

Blue-noise Dithered Sampling

先日開催されたSIGGRAPH2016には私も参加しましたが、興味深いものの実装が大変そうなものから何の役に立つのかわからない技術まで様々な知見を得ることができました。
その中でも割とさくっと実装できそうで、かつ効果もありそうな発表があり、それがBlue-noise Dithered Samplingです。これは、Roll the Diceというトークの中の発表の一つで、Arnoldという有名なレイトレーシング(パストレーシング)ベースのオフライン商用レンダラを開発しているSolid Angle社による成果ということになっています。
今回は、この発表を元に適当に実装して簡単な実験をしてみました。

実装は以下に置いてあります。

github.com

Blue-noise 

ブルーノイズについてはあまり深くは説明しませんが、以下の図のように各点間の距離がなんとなく等しくなるように平面上に散らばっている点群の性質をブルーノイズと呼んだりします。

f:id:h013:20160822172701p:plain(http://www.geometry.caltech.edu/BlueNoise/より)

二次元平面からのサンプリングはコンピュータグラフィックスにおいて様々な箇所で頻出の問題です。完全にランダムに行えば、それはホワイトノイズになります。周波数領域で周波数に偏りが無い(=ランダム)だからです。ブルーノイズの場合、低周波成分が小さく、高周波成分が大きい、といった特性を持っています。人の網膜細胞もこのパターンらしいです。

さて、ブルーノイズはもともと画像の二値化などのディザリング処理において良くつかわれていました。今回の手法は、このアイディアをサンプリングに応用することでいい感じにディザリングされたレンダリング画像を少ないサンプル数で得る、という手法になります。

実装概要

まず、解きたいレンダリングの問題が何次元の積分なのかについて考えます。例えば、モーションブラーであれば時間方向に積分するため1次元の積分になり、アンビエントオクルージョンであれば半球上で積分するため2次元の積分になります。
このように、解きたい積分がd次元のとき、d次元分の乱数列([0, 1)の範囲)を生成します。2次元の場合、(0.3152, 0.8611)みたいな感じになりますね。普通のレンダリングの場合、このような乱数列を各ピクセルごとに独立に複数回数生成してモンテカルロ積分のサンプルの生成に利用することで画像を得ます。

しかし、今回の場合は全ピクセルで同一の乱数列を使うとします。そして、それぞれの乱数列をピクセルごとに事前に決めておいたd次元の値([0, 1)の範囲)でオフセットします。もし、事前に決めておいた値が以下の図のように完全にランダムなら、結局各ピクセルごとに独立の乱数列を生成するのとほとんど変わりません。

f:id:h013:20160822174126p:plain(128x128、[0, 1)の範囲の乱数列)

しかし、このオフセット値を以下の図のようにブルーノイズを用いればレンダリング結果はディザリングされたものになり、より望ましくなります。

f:id:h013:20160822173929p:plain

これが今回の手法の要旨です。

ブルーノイズオフセットの生成

さて、それではブルーノイズオフセット値はどのようにして得るのでしょうか。これは、論文に詳しく解説されていますが、焼きなまし法による最適化によって得るようです。
まず、与えられたサイズのオフセットマスク画像をd次元、[0, 1)の範囲の乱数列で初期化します。そして、あるオフセット値と別のオフセット値の間で定義されるエネルギー関数の総和を最小化するように、適当に選んだ二つのオフセット値を入れ替える、ということを反復的に行えば良いらしいです。最小化したい式は以下のようになります。

f:id:h013:20160822174528p:plain

https://www.solidangle.com/research/dither_abstract.pdfより)

ここでpi, qiはマスク上の位置で、ps, qsは各サンプル、dはオフセットの次元です。分母のσは定数になります。

で、これを実装するわけですけど今回自分は焼きなまし法は特につかわず(パラメータチューニングとか面倒だし)エネルギー関数を事前に計算しておいて、適当にオフセット値を入れ替え、再計算したエネルギー関数が減少していれば入れ替えを採用、減少してなければ入れ替えは棄却、ということを指定された回数繰り返すようにしました。
さらに、エネルギー計算の再計算も自分以外の全オフセット値に対して実行していると重いため、自分の周囲にのみ着目するような近似計算にして高速化を行ってみました。

わりと適当でしたが、それなりにうまく生成されているように見えたので良しとします。(若干品質が低いような気もする)

実際のレンダリング

レンダリングするときは、既に解説したようにブルーノイズディザリングしたいモンテカルロ積分のサンプル生成にブルーノイズでオフセットした値を投入するだけです。
今回の実験ではモーションブラー(1次元)とアンビエントオクルージョン(2次元)の二つについて試してみました。

結果

それでは結果です。画像は全て512x512でレンダリングしています。オフセットマスクのサイズは128x128です。

以下はモーションブラーの結果です。

f:id:h013:20160822175355p:plain(ランダムオフセット / 1spp(1 sample per pixel))

f:id:h013:20160822175459p:plain(ブルーノイズ / 1spp)

ブルーノイズの方はランダムに比べてノイズが均等に分布していることがわかります。
ただ、サンプル数を1から9sppに上げると両者の差は小さくなりました。f:id:h013:20160822175650p:plain(ランダムオフセット / 9spp)

f:id:h013:20160822175720p:plain(ブルーノイズ / 9spp)

ちなみに、リファレンスとして256サンプリングすると以下のようになります。

f:id:h013:20160822175746p:plain

次に二次元の積分アンビエントオクルージョンの結果です。

f:id:h013:20160822175830p:plain(ランダムオフセット / 1spp)

f:id:h013:20160822175850p:plain(ブルーノイズ / 1spp)

モーションブラーほどではありませんが、ブルーノイズの方が良い結果を得られています。しかし、やはりサンプル数を増やすと以下のように、結果は似たようなものになりました。

f:id:h013:20160822180128p:plain(ランダムオフセット / 9spp)

f:id:h013:20160822180155p:plain(ブルーノイズ / 9spp)

ちなみに以下はリファレンスです。

f:id:h013:20160822180254p:plain

アンビエントオクルージョンも設定によってはブルーノイズの方がかなり良い結果を出すこともあります。例えば以下。

f:id:h013:20160822190029p:plain(ランダムオフセット / 1spp)

f:id:h013:20160822190047p:plain(ブルーノイズ / 1spp)

f:id:h013:20160822190105p:plain(リファレンス)

まとめ

どうも高次元の積分になると効果が薄まっていくようです。この手のサンプリング手法は大体そういう傾向がありますね(Low Descrepancy Sequenceとか)。また、サンプル数を増やすと割とすぐ似たような結果になってしまいました。なんかバグってる可能性もありますが、元の論文のアブストの結果画像も1sppで比較してるのが多く、9sppで比較してるのはボリュームレンダリングだけだったので相性みたいなのがあるのかもしれません。

また、マスク画像のサイズも、今回は適当に128x128と決めましたが結果に影響あるかもしれません。

しかし、1sppで比較した時は間違いなく単純なランダムよりはいい結果になっています。オフラインで長時間レンダリングできるシチュエーションだとちょっと微妙かもしれませんが、むしろリアルタイムレンダリングで、サンプル数を出来る限り減らしたいような状況で活きてくる手法かもしれません。

おわり。

WebGLでリアルタイム流体シミュレーション+レンダリングを実装してみた

はじめに

なんかWebGLが流行ってるらしいのでWebGLすることにしてみました。OpenGLはぼんやりとやったことがあったのですが、ウェッブ技術に対する疎さが深刻化しているのでモダンで先端的なテクノロジーを追及するためにもWebGLの習得は急務といえました。

WebGLJavaScriptと呼ばれるプログラミング言語を用いるようです。僕がJavaScriptを最後に書いたのは四年くらい前にクック●●●のハッカソン?みたいなのに参加した時その場で習得してその場でアプリを作ったとき以来です。それ以来一度も書いていません。

まあJavaScriptとか意識高い大学生でも書けるわけだし適当にAPIを呼び散らかす分には何の問題もないでしょう。というわけでWebGLのサンプルコードを読みながらサンプルドリブンでJavaScriptを習得すんのがいいだろと思いました。

先に結果だけ貼っておきます。いろいろあって流体シミュレーションしつつレンダリングするようなものになりました。
githole/webglSmoke · GitHub

サンプルドリブンプログラミング

とりあえずWebGLのサンプルコードが乗ってるサイトを探しました。WebGLは流行りなだけあって無限にそういうサイトは存在します。こういう時はGoogleで上の方に出てくるサイト=高品質なサイト、というヒューリスティックを実行します。今回参考にしたのは以下の二つのサイトです。

Learning WebGL

wgld.org

前者は英語、後者日本語です。両方のサイトの最初の方のページをブワーって読んでローカルにコピーして実行したりしてみます。適当にコードを書き換えたりするうちに概ねWebGLの初期化の方法を覚えます。

この段階で、WebGLの初期化にさえ至ればあとは慣れ親しんだOpenGLとなんら変わらないという悟りを得ます。こうなればしめたものです。OpenGLAPIを呼ぶのはそう難しいことではありません。GPUにデータを送って、GPUにドローコールを積むだけです。

一つ、面倒なことがあるとすれば行列まわりの演算です。GPUプログラミングなんで、CPUで行列演算してGPUに頂点送ってシェーダーセットしてドローコール呼ぶだけなんですが、その行列演算が驚くべき面倒さです。今回はglMatrixを使いました。なぜか、最新版は4x4行列の逆行列の計算関数が実装されてないっぽく、過去のバージョンから関数を引っ張ってきたりしています。実は実装されてた、みたいな落ちだったら笑えますね。

2D流体シミュレーション

そもそも、「ああ、ブラウザで流体シミュレーションを見たいなあ」と思ったのが今回ウェッブに手を出すきっかけだったので流体シミュレーションを実装します。2Dの流体シミュレーション(WebGL)は無限にあるのですが、3Dは全然ありません。まったく気合いの足りないことです。

というわけで、とりあえず2Dの流体シミュレーションを実装→3Dのシミュレーションに拡張、という手順を踏むことにします。

2Dの流体シミュレーションは、特に頭を使いたくなかったので、Jos Stam先生のStable FluidsをそのままGLSLに移植しました。ググればいくらでも情報が出てくるでしょう。基本的に、ナビエ・ストークス方程式を離散化して解いてるだけですが、移流の解決のためにセミラグランジュ法を使っているのでいい感じに安定になります。

安定なのはいいんですけど、セミラグランジュ法はウンコみたいに数値拡散が起こってしおしおになります。よって、今回はナビエ・ストークス方程式の拡散項は無視してます。数値拡散だけでもやばいのにまともに拡散項なんて解いている場合ではありません。この方法はBridson先生も推奨していたような気がします。まあ移流スキームをもうちょい賢い感じにすればいいんですけど、今回の本旨じゃない気がしたのでこの辺で終わりにします。

さて、WebGLで流体シミュレーションみたいなGPGPUじみたことを行うにはどうすればよいか?という話です。WebCLみたいなのもあるみたいですがとりあえずWebGLだけ使うとすると、レンダーターゲットを二枚用意してピンポンするのが一番です。

このようにすると、二次元の離散化格子はそのままテクスチャの各テクセルに対応するのでシミュレーションも直感的に実装できます。境界条件をちゃんと設定する必要はありますが、どうせWebGLのテクスチャボーダーの処理は二種類くらいしかないので適当に指定します。

テクスチャフォーマットは、RGBAのFLOATテクスチャにしました。これは、本当は拡張機能なのですが、さすがにUnsinged Byteのフツーの8ビットテクスチャだと精度が全然足りないし、パックとかアンパックを組み合わせ始めると泥沼式に面倒さが増していくのであきらめて拡張機能を使いました。

3D流体シミュレーション

というわけで、GLSLへの移植と結果の確認自体はまあすぐできました。やるだけですね。問題はここからです。WebGLは先進的なテクノロジーにも関わらず3Dテクスチャをサポートしてないらしいのです。そしたらどうやって3Dシミュレーションすればいいのでしょう。

まあ実はそんな大したことはありません。3Dテクスチャを2Dテクスチャ上に展開してマッピングしてやればいいのです。シミュレーション空間が64x64x64だとすると、512x512の2Dテクスチャを確保して、その上に64x64のタイルを縦横8枚ずつならべれば64x64x64相当のテクスチャとみなすことができます。

この手の処理は大概テクスチャ境界が面倒になるものですが、FLOATテクスチャは最近傍フィルタしか使えないのでどうにでもなります。

そしたら、あとは2Dシミュレーションを3Dに拡張しつつテクスチャサンプリング部分を自前でアドレッシングするようにするだけです。ヤッター!

レンダリング

シミュレーションが首尾よくできたら最後にレンダリングです。これが結構面倒です。ボリュームレンダリングの手法は無数にあります。

しかし、最初考えてた方法(光源方向からシャドウボリュームテクスチャを生成していい感じにやる)は、WebGLがどうやらマルチレンダーターゲットに対応していないらしいので破綻しました。まあ対応してたところで、イマイチなんですけど。

しかたないので普通にカメラからレイマーチング+光源にも各サンプル点からレイマーチングという方法にしました。この方法だと、サンプリング数がO(N^2)(Nはサンプル数)みたいな感じになりあまり筋がよくないのですが、プライマリのレンダーターゲットの解像度を半分にしてみたりきちんとAABBで枝刈りしたりみたいなことをしていったら結構速度が出るようになったのでそれでよし!としました。

高速化とか

シミュレーションステップの順番を細かく変えたり、圧力項を解くためにガウスザイデルする際、前フレームの結果を使い回りしたり、みたいな涙ぐましい最適化をします。まあまあ速くなります。

おわり

まあなんとかシミュもレンダリングもできました。dat.gui.jsとかStats.jsとか、便利ライブラリを突っ込むとそれっぽくなってかっこよくなります。組み込みも簡単です。

だいたい、一日ちょっとくらいで絵が出て、あとは細かい調整って感じでしたし、どんどんWebGLすればいいんじゃないでしょうか。

シミュレーションを放置しておくと割と爆発したりするんですが、これは色々シミュレーションを雑にやってるせいです。TODOってことで。

githole/webglSmoke · GitHub