Distance fields & implicit shapes

SDF Gradient / Normal Estimation

距離場の勾配法線

距離の変化から表面の向きを求める。

8 / 10

Interactive demo

Preparing the demo…

SDF Gradient / Normal Estimation

Drag to orbit, Ctrl/⌘ + scroll to zoom. Use two fingers on touch screens.

This explanation is currently available in Japanese.

ひとことで

距離場の値が最も速く増える向き(勾配)は、その点から最寄りの表面へ向かう向きの逆、つまり表面の外向き法線です。レイマーチングで当たった点の法線は、近くの数点で距離を測り、その差から勾配を求めて得ます。ポリゴンのように頂点に法線を持たせる必要はありません。

n=∇f∥∇f∥,∇f=(∂f∂x, ∂f∂y, ∂f∂z)\mathbf{n} = \frac{\nabla f}{\lVert \nabla f \rVert},\qquad \nabla f = \left(\frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y},\ \frac{\partial f}{\partial z}\right)

ゲームでの使いどころ

  • 距離場で描く形のライティング:レイマーチングで描いたあらゆる形の陰影、反射、リムライト。
  • 押し出し方向の計算:粒子やキャラクターが距離場の内側にめり込んだとき、勾配の向きに距離の分だけ押し戻せば表面の外に出られます。GPU パーティクルの衝突応答の定番です。
  • 表面に沿った移動:勾配に垂直な方向へ動かすと、表面に沿って滑らせたり、壁を這わせたりできます。
  • 最寄りの表面点:p−f(p) n\mathbf{p} - f(\mathbf{p})\,\mathbf{n} で、最寄りの表面上の点が求まります(SDF のデモの探査点)。

しくみ

偏微分を差分で近似します。代表的な方法は 3 つです。

方法距離の評価回数特徴
前進差分4 回f(p+εei)−f(p)f(\mathbf{p} + \varepsilon\mathbf{e}_i) - f(\mathbf{p})。安いが、誤差が大きく少しずれる
中心差分6 回f(p+εei)−f(p−εei)f(\mathbf{p} + \varepsilon\mathbf{e}_i) - f(\mathbf{p} - \varepsilon\mathbf{e}_i)。精度がよい
四面体4 回正四面体の 4 頂点方向で評価して合成する。中心差分に近い精度で 4 回

四面体の方法(Inigo Quilez が紹介したもの)は、k0=(1,−1,−1)\mathbf{k}_0 = (1,-1,-1) などの 4 方向で

∇f≈∑i=03ki f(p+ε ki)\nabla f \approx \sum_{i=0}^{3} \mathbf{k}_i\, f(\mathbf{p} + \varepsilon\,\mathbf{k}_i)

とします。4 回で済み、偏りも少ないので、レイマーチングでよく使われます。

差分の幅 ε\varepsilon の選び方が重要です。

  • 小さすぎる:距離の差が浮動小数点の誤差に埋もれ、法線がばらついて表面がざらつきます。
  • 大きすぎる:広い範囲の平均的な傾きになり、細かい凹凸の陰影が消えます。逆にこれを利用して、あえて大きな ε\varepsilon で「ぼかした法線」を作ることもあります。
  • 画面上の 1 画素に相当する大きさ(遠くの点ほど大きく)にすると、ちらつきと細部のバランスが取れます。

実装のポイント

  • 解析的な勾配:球や箱、ノイズなど、式が分かっている形は微分も式で書けます。差分より正確で、評価回数も減らせます。
  • コンパイラによる展開:map が大きい関数だと、4 回の呼び出しがインライン展開されてシェーダーが巨大になり、コンパイルが極端に遅くなることがあります。ループで 4 回呼ぶ書き方にすると抑えられます。
  • 押し戻しに使うとき:勾配の長さは本来 1 ですが、合成や変形をした距離場では 1 からずれます。正規化してから使います。

つまずきやすい点

  • 勾配が 0 になる場所:形の中心や、2 つの形からちょうど等距離の場所(中央軸)では、勾配が定まらない(0 や不連続になる)ことがあります。押し戻しに使うときは、長さが極端に小さい場合を別扱いにします。
  • ノイズで凹凸を付けた形:細かいノイズを足した形は、ε\varepsilon の選び方で見た目が大きく変わります。デモで試してください。
  • 法線マップとの関係:メッシュの法線マップは法線を画像に保存しておく方法、勾配法線はその場で計算する方法です。

デモで試すこと

  1. 「差分の幅 ε」を最小にすると、表面がざらつきます。最大近くにすると、岩肌の細かな凹凸の陰影が消えてのっぺりします。
  2. 「差分の取り方」を切り替えて、前進差分と中心差分・四面体の違いを見比べてください。前進差分は陰影が少しずれます。
  3. 「法線を色で」を選ぶと、法線の x,y,zx, y, z を赤・緑・青で表示します。
  4. 「断面と勾配の矢印」をオンにすると、断面上のどの矢印も、最寄りの表面からまっすぐ遠ざかる向きを指していることが分かります。

References

Unreal Engine Mesh Distance Fields ↗

A general reference for this subcategory.