コードリーディング。Revision 2019のevvvvil師のレイマーチングを読む。
どんも。さやちゃんぐbotです。
いちどevvvvil師のレイマーチングはきちんと読みたいなーと思っていたのですが、そろそろ読めるようになったんじゃないかということでYouTube眺めながら写経してみました。こう、ぐわーっとぐにゃーっといいかんじのビジュアルになるんですよ、evvvvil師のレイマーチング。対戦相手のLJのレイマーチングもすごく面白いですが、今回はevvvvil師のコードだけ扱います。奥からやってくる三角形が、signで右回転と左回転を切り替えたりしていて、こちらも読みごたえがあります。
ということで、今日はRevision2019のShader Showdownを読み解いた内容をつらつらと書いていきます。
レイマーチングって、カンフーの世界でひとりひとりがそれぞれのカンフーを持っている、みたいなのに似てる気がするんですよね。不慣れな頃にShadertoyを眺めてまわったりすると、それぞれみんなスタイルがバラバラな書きっぷりで、読みづらい/まったく読めないってことがザラでした。レイマーチングにはリーダブルコードも書法も作法も通用しないのです。
でもまあ、そのうちなんとなく読めるようになるので、何ごとも継続ですね。
さて、写経した結果はこんなかんじ。
ちょこちょこコメント入れてるので、これを見ながら自分でいじった方が理解は早いかもしれません。完全に写経しきれてはいませんが、エッセンスはこれで読み取れるかと思います。
ということで本文を見ていきます。
evvvvil師のシェーダーにはいくつか特徴があります。
変数名、関数名が1文字か2文字になっているのは特に目立つところです。map関数もmpと一文字削られていたりします。このあたり、タイプ時間を短縮し、指のI/Oにかかる時間をなるべく思考する時間にまわしている印象があります。
いくつかの変数はグローバルにしています。ttは時間を加工したもので、このレイマーチングではmain関数でtimeをmodしています。
b、bbはevvvvil師特有のビジュアルを作るのに重要な変数です。これは下で出てきます。
main関数です。実際にevvvvil師が書いたものとは変数名が違ったり、マーチングループを切り出していないなど差はあります。
カメラ定義をした後のRay Directionは行列を使う書き方。
vec3 rd = mat3(up, side, fwd)*normalize(vec3(uv,0.5))
ライトベクトルはこう。
vec3(0.3, 0.5, -0.5)
全体的に、0.5という値を使うことを好んでいるようです。
また、背景の空はrdを渡して、水色をベースとしてyの値でグラデーションをつけています。
vec3 sky(vec3 rd){return clamp(vec3(.1,.5,.6)-rd.y*.4,0.,1.);}
次はマップ関数…の前に、形状をモデリングしている箇所を先に見ていきます。
evvvvil師のレイマーチングは派手で繊細なルックですが、距離関数として使っているのは典型的な箱の関数一種類のみです。
この箱の関数を呼び出して、fb関数ではいくつかの細長い柱を描いています。
複数のオブジェクトを配置するのは、典型的なmin方式での距離の計算です。三項演算子でオブジェクトどうしの距離を比較して、より近い方を取っていくやり方です。また、関数自体の戻り値は距離だけではなく、マテリアルの種類をあらわす3とか6といったインデクスとあわせてvec2です。3とか5だったら黄色、6だと白い柱になります。
このシンプルなはしごがなぜあんな派手に化けるのか?というところは、次のマップ関数で見てみます。
はい、ということで、座標をabsして加工してループで回すという作りでした。
ここにグローバル変数のbとbbが出てきますが、中身はsin(p.y)です。0.5倍しているのでbbの方が値を取る範囲は-0.5から+0.5です。
bはsinの中に時間を使ったttが入っているので、時間経過で変化する値になります。
abs(p)してからb、bbを使った引き算と回転をしているので、ぐにゃぐにゃした形状が作られるのですね。
形状の作り方はここまでで、あとは色、ライティング、ブルームといったあたりを見ておきます。
マップ関数ではモデリングしてオブジェクトを判断する都度、距離を元にした値を足していっています。
g = g + 0.1 / (0.1 * d * d)
イメージとしては、これはマテリアルのエミッションを加算していくというとらえ方が良さそうに思えます。結果として、gを加算する対象のオブジェクト近傍では、ブルームの効果があらわれます。
ライティングはシンプルにランバートでディフューズを出し、マテリアルカラーに乗じているだけです。
マテリアルカラーはモデリングした際に距離とあわせて返した値から黄色(1.0, 0.5, 0.0)か白(1.0, 1.0, 1.0)で柱の色を決めています。この0.5や1.0という値の決め方もevvvvil師らしい手法です。
柱の色に対しては、追加で距離のexpに応じて空の色と線形補完しています。
最後にブルームを加えておしまい。
col = col + vec3(0.025 * g);
(ループの経過回数に応じた逆AO(col += float(j)/1024.)は元ソースにはないものですのであしからず。)
いかがでしたでしょうか。ここまで読むと、evvvvil師のコードから見えてくるものがあったかと思います。
みなさまがevvvvil師のような派手なレイマーチングを目指す一助となれたら幸いです。
ではでは。