SDF在計算的是空間中的每一個點,透過SDF函數都會被映射到一個數值,代表該點到最近物體表面的最近距離。也因為本身是 Signed,可以用正負號來代表在內外,或是表面上。從下面的圖也可以看到在內外可以根據函數大小值去做出顏色標示,所以如果要查詢點到物體邊緣距離時,只要去採樣顏色值就可以快速反算出距離。
- d = 0 : 點在表面上
- d > 0 : 點在外部
- d < 0 : 點在內部

2D SDF結果視覺化 來源:Demystifying Signed Distance Functions
平常我們在2d bitmap放大的時候常常會變糊,但用SDF紀錄邊緣距離,就可以在GPU透過shader插值,去計算出銳利平滑的邊緣。在體積與碰撞計算也會變得有效率,在 3d 渲染可以用 raymarching 來繪製複雜幾何形狀。最後是存取靈活性高,可以透過聯集、交集、差集、min()、max() 把多個簡單形狀組合成複雜模型。

SDF 字體渲染對比 來源:libGDX / Distance field fonts - libGDX
2D SDF 定義
以圓形來說,我們可以用畢氏定理去計算距離來求出 SDF 函數結果:
import numpy as np
def circle_sdf(x, y, center = [0,0], radius = 80):
return np.sqrt((x - center[0])**2 + (y - center[1])**2) - radius
以矩形來說,我們會用左上角、右下角當作基準點,然後我們以矩形的右上部分做舉例,如果是在矩形的右側或是上方,我們都可以直接用x或y座標其中一個直接推算出距離,但是如果是在右上方的話,則需要透過畢氏定理來計算,所以我們可以把這三種情況(右、上、右上)統整成一條公式:
float sdBox( in vec2 p, in vec2 b ) {
vec2 d = abs(p)-b; return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
其中的max()部分,拿在右方的點做舉例,他的y座標一定會小於矩形的上緣,所以是負的,但因為在右側,計算距離的時候其實直接看x座標即可,所以可以直接將y的差值視為0去計算,上方的點同理,這樣我們就可以統整在同一個算式中了。
(這部分我覺得 Inigo Quilez 的影片 https://iquilezles.org/articles/distfunctions2d/ 解釋得非常清楚,也可以參考他的網站去看更多 2D, 3D 的 SDF 計算方式跟程式實作)
用處
學會了 SDF 計算,其實可以用到地方很多,舉個例子來說,像是圖學玩家 第002篇 原創文 裡就有很詳細解釋到 Ray Marching,在比較複雜的圖形下,Raymarcher 相較於 Raytracing 可以花費比較少步數,雖然精準度會下降,但是在計算成本上會有很顯著的進步,在 RealTime 的情境下會有更大的優勢:

SDF 步數逼近焦點 來源:https://zhuanlan.zhihu.com/p/93901692
這個逼近的方式過程是每一次都會去畫一個圓,半徑是離自己最近的表面到圓心的距離,然後再沿著光線射往的方向上跟圓的交點,再畫一個會切到最近表面的圓,這樣的話會有兩種狀況會發生,第一個是物體往沒有表面的地方走,那步數就會變成很大的數字,所以一般來說可以用步數上限去把這個狀況記錄成遠離表面,也就代表光線不會被物體擋住。那另一種狀況就是光線本來就會射到物體,那在後續的幾個圓,就會越來越貼近物體,如果是個曲面的物體,幾個圓的圓心也會開始慢慢趨向物體本身包住他,最後離物體的距離到我們設定的臨界值以下,我們就可以說光線在這邊跟物體相交了,當然會有誤差,但在一般情境下這樣渲染出的結果就夠用了。
又或者是把不好以數學表示的複雜圖形,用多個圖形做交集、聯集、差集去疊出來,在 SDF 的定義下,我們就可以利用正負號的特性來完成各種邏輯的表示,其實一開始我在這塊的時候會有點困惑為什麼外圍不會被算進去,但在稍後閱讀下面圖形的時候心中可以有個前提,我們要找的區域都是在內部,所以不論是 min 或 max 最後都還是要選擇小於 0 的部分。
交集:

聯集:

差集:(在這邊我們取左邊的減右邊)

所以我們可以把這個合起來看了,在 Making of Moana (the ShaderToy) 這篇文章裡就有詳細的去比較 Raytracing 跟 Raymarching,雖然 Raytracing 可以直接用方程式去求交點,不像 Raymarching要迭代好幾次,在下面這個例子中也確實效能會比較快,可是一旦圖形變成幾千幾萬個幾何形狀疊加的時候,Raymarching 的優勢就可以顯現出來,因為不管什麼形狀他都可以直接沿著表面傳遞記錄下來。

Ray Tracing vs. Ray Marching 來源:https://wallisc.github.io/rendering/2020/12/08/Making-Of-Moana-the-shadertoy.html