正解するカドの「カド」をレイマーチングでリアルタイム描画する

今夜はアニメ「正解するカド」の最終回ですね。

フラクタル図形(カド)や折り紙(ワム)が重要な要素になっていて、個人的にとても刺さるアニメでした。

最終回は楽しみですが、今日で終わってしまうと思うと寂しくも感じます。

さて、レイマーチング(スフィアトレーシング)は「カド」のようなフラクタル図形の描画がとても得意です。

そこで、WebGLによるレイマーチングでカドのレンダリングに挑戦しました!!

カド

レイマーチングでカド(MandelBox)を描画した結果です。

次のリンクからブラウザ上から動かすこともできます。

PauseをOFFにすると、カドがアニメーションします(負荷注意)。

描画の負荷が重たすぎる場合には、Pixel Ratioを1/2xか1/4xにしてください。

解説

制作における工夫点や参考資料を紹介していきます。

カドのレンダリング

アニメの「カド」はMandelBoxと呼ばれるフラクタル図形をベースに改造したもののようです。

今回はベーシックなMandelBoxをレイマーチングで普通に描画しました。

MandelBoxの距離関数は以下のサイトを参考に実装しました。

カドのシェーディングはTokyoDemoFest2017の作品の実装をほぼそのまま流用しました。

独特なカラフルな色は色相をレイマーチングのステップ数などによって変化させることで実現しています。

IBL(Image Based Lighting)やAO(Ambient Occlusion)でシェーディングの品質を高めました。IBLのためのキューブマップ画像は以下のサイトのものを利用しました。

水面のレンダリング

カドのレンダリングは特に特殊なことはしていませんが、水面には色々な工夫をこらしました。

CPUによるパーリンノイズの生成

フラクタルノイズの1種であるパーリンノイズをハイトマップとして、水面を表現しました。

ハイトマップとの衝突判定もレイマーチングにより行いました。 ハイトマップの距離関数は非常にシンプルに実装できます。

float sdGround(in vec3 p) {
	return p.y - texture2D(groundHeight, p.xz * 0.1).r + GROUND_BASE;
}

パーリンノイズの生成は起動時にCPU側で行っています。

次のサイトを参考にして、JavaScriptでパーリンノイズの計算を実装しました。

この記事を読んで、今まで「パーリンノイズ」だと思い込んでいたものが、「バリューノイズ」だと知りました。

勾配を使った本物のパーリンノイズの実装は今回が初めてだったので、勉強になりました。

水面のアニメーション

3Dのパーリンノイズを実装したので、時間方向にパーリンノイズを動かすことで、水面のアニメーションもできます。

Animate WaterというチェックボックスをONにすると、水面のアニメーションができます。

毎フレーム毎にCPU計算で256x256のパーリンノイズを生成するため、かなり激重です。ハイエンドPCでもまともに動きませんw

起動時にまとめて生成すれば良いような気がしますが、起動時間が長くなるのが嫌なので、このような仕様となっています。

衝突判定の軽量化

単純に水面のハイトマップをテクスチャとして参照する実装にしたところ、テクスチャのフェッチ回数が危険領域に突入しました。

そこで、カメラの近くの水面だけハイトマップからレイマーチングを行うようにして、 カメラから離れた水面は凹凸のない平面として扱い、判定式から解析的に衝突判定を行うことで、軽量化を図りました。

遠くの水面は形状的には平面ですが、シェーディングの法線計算ではハイトマップを参照するようにして、見た目の品質を向上しました。 ノーマルマップと全く同じ原理です。

ちなみに、今回のシーンように水面がXZ平面になっていれば、レイとの衝突判定は非常に低コストに行うことができます。

GROUND_BASE を水面の高さとして、次の t を計算し、t > 0.0 であればレイと平面が交差しています。

float t = -(ray.origin.y - GROUND_BASE) / ray.direction.y;
if (t > 0.0) {
  // Hit!!
}

なんと、floatの引き算と割り算一回ずつだけで、平面とレイの衝突判定ができます!

Fresnel反射

雑にFresnel反射も入れました。

俯瞰視点にすると、遠くの方の水面は鏡面のようになっていることが分かると思います。

Fresnel反射

水面がXZ平面になっていれば、衝突判定と同様にFresnel反射率の近似値もかなり低コストに計算できます。

float f0 = 0.7;// 垂直に入射した時の反射率。かなり大きめな値に設定。
intersection.reflectance = f0 + (1.0 - f0) * pow(1.0 + ray.direction.y, 5.0);

試作

最終的には海の上にカドが浮かんでいるシーンとしましたが、ビルのシーンも試作しました。せっかくなので画像を残しておきます。

ビルの試作段階

余談

最後に余談なのですが、作品の重要な要素であるカドとワムのどちらとも知り合いが関わっていて、あまりの世間の狭さに驚きました。

カドのレンダリングはレイトレ合宿などで繋がりのあるPheemaさんのUnityのレイマーチングによるものでした。

さらに、ワムは大学時代の指導教官の三谷純先生の幾何学折り紙そのものでした。

こんなことってあるんですね…!

アニメ版のカドの再現(2017/07/03追記)

notargsさんがアニメ版のカドの再現のヒントをくださったので、自分もチャンジしました。

こちらはCGWORLDの記事の画像の転載です。これをリファレンスとします。

CGWORLDの記事のリファレンス画像

Scaleをマイナスにして、大小の違う2つのMandelBoxを重ねてみました。 内部に入った光を吸収させる替わりにAOを強めにしました。 すると、本当にアニメ版のカドにかなり近い結果になりました!!これは面白いですね!

アニメ版のカドの再現

アニメ版のカドの再現2

こちらからアニメ版に近いカドを動かすことができます。MandelBoxを2つ重ねたことで、負荷が約2倍になりました。

カドを再現する人たち(2017/07/03追記)

カドのレンダリングに挑戦している方が自分以外にも現れたので、観測範囲内でまとめました。

notargsさん

cx20さん

自分

comments powered by Disqus

gam0022.net

Qiita

Hatena Blog

gam0022.net

Qiita

Hatena Blog