はじめに
uRaymarching 関連の話題になります、Twitter で SDFr と uRaymarching を試されている方から質問をもらいまして、調査をはじめました。前回の記事はこちら:
これまでは物体表面までの距離を距離関数で記述してレイマーチングする手法を紹介してきましたが、この距離は別に関数でなくても問題ありません。ある地点から物体までの距離がどれくらいの場所にあるのかを符号付き(外部なら +、内部なら -)で示せればよいわけで、距離情報を 3 次元的に格納した 3D テクスチャでも実現できます(ちなみに TextMeshPro では 2 次元的に文字までの距離を格納したテクスチャでフォント描画を行っています)。キューブの中に入ったレイのある地点において 3D テクスチャをサンプリングし、その距離分だけ再度レイをすすめる、を繰り返していくことによって格納されたオブジェクトの表面へ到達することができます。本エントリではこの 3D テクスチャを SDF テクスチャと呼び(SDF: Signed Distance Field、符号付き距離場)、この描画を uRaymarching で実現することについて見ていきます。
デモ
ポリゴンはキューブですがこんな画が出ます。
また従来どおり smin 計算などでこういった表現もできます。
テクスチャを Repeat や Mirror にすれば繰り返しもできます。
環境
- Unity 2019.4.1f1
- uRaymarching v2.1.0
- SDFr (1cfba97)
今回のために少し uRaymarching を更新したので最新版のリリース(執筆時点では v2.1.0)を以下よりダウンロードしてください。
SDFr
SDF テクスチャは Unity では SDFr というプロジェクトを利用することで得ることができます。これは単一または複数の Mesh を RHalf 形式で -1 ~ +1 の範囲で書き込んだ SDF テクスチャを生成してくれるアセットです。VFX Graph とも組み合わせてパーティクルの衝突計算にも使えるようです。
Raymarching や VFX Graph のサンプルも付属しています。
Bake ボタンをSDFData
という Texture3D
を含む ScriptableObject
を生成してくれるので、そこから色々情報を得ることができます。
今回は生成された 3D テクスチャだけを利用します。SDFr の Example > Data > sdfData_stanfordLucy64 に Stanford Lucy のデータがあるのでこれで色々試していきます。
uRaymarching の設定
設定
ビルトインパイプラインのフォワードでやっていきます。また、カメラがキューブポリゴン内部にも入り込めるように Camera Inside Object
にチェックを入れておきます。またオブジェクトの拡縮ができるように Follow Object Scale
にもチェックしておきましょう。
Properties
3D Texture を読み込むためにマテリアルのプロパティにスロットを追加します。
[Header(Additional Properties)] _Volume("Volume", 3D) = "" {}
Distance Function
SDF テクスチャに距離情報が入っているのでとてもシンプルになります。
Texture3D _Volume; SamplerState sampler_Volume; inline float DistanceFunction(float3 pos) { return _Volume.SampleLevel(sampler_Volume, pos + 0.5, 0).r; }
Conditions のところで World Space にチェックを入れない場合は、ここでやってくる pos
は -0.5 ~ +0.5 のローカル座標になります。なので、これを 0 ~ +1 の範囲になるように 0.5 だけ足してあげた座標のテクスチャの値をサンプリングします。形式は RHalf なので r にその地点から物体表面までの距離が入っているので、これを距離関数の戻り値として採用しています。
Post Effect
簡易的なオクルージョンと、法線色をうっすらと乗せてみます。
inline void PostEffect(RaymarchInfo ray, inout PostEffectOutput o) { float a = 1.0 * ray.loop / ray.maxLoop; o.Occlusion *= pow(1.0 - a, 2.0); o.Albedo *= (o.Normal + 1.0) / 2; }
マテリアルの設定
Properties のところで追加した _Volume
を設定するスロットに Stanford Lucy のテクスチャを入れます。
また、今回 v2.1.0 からアップデートで追加した NormalDelta
を 0.01 ほどの値に設定します。これは法線情報は距離関数の微分によって得ているのですが、その微分を行うための微小距離を定義するものです。以前は 1e-4 を固定値として使っていましたが、この値だとテクスチャは数式ほどなめらかでないので次のようにノイズが発生してしまいます。
おわりに
空間解像度はそんなに高くないので、まだブロックノイズ的な小さいキューブが見えてしまっています。3次元的な勾配情報を保持した RGBHalf な画像にしてクオリティを上げるみたいなことも必要そうです。
また、smin 計算などで他のオブジェクトと結合しようとした際に、バウンディングボックスの外ではテクスチャ境界面の距離が適用されてしまうのでちょっと変に見えてしまいます(デモのボールとの結合はばれないようにキャプチャしています)。
距離関数内でレイとバウンディングボックスの距離計算なども必要そうです(SDFr ではやっているようです)。