GraphicsLab Project 之 Curl Noise
作者:i_dovelemon
日期:2020-04-25
主題:Perlin Noise, Curl Noise, Finite Difference Method
引言
最近在研究流體效果相關的模擬。經過一番調查,發現很多的演算法都基於一定的物理原理進行模擬,計算量相對來說都比較高昂。最終尋找到一個基於噪音實現的,可在視覺上模擬流體效果的方法:Curl Noise。題圖就是通過 Curl Noise 模擬的流體向量場控制的百萬粒子的效果。
背景知識
在講解什麼是 Curl Noise 之前,我們需要了解一些相關背景知識。
向量場(Vector Field)
一個2D 或者 3D 的向量場,表示的是賦予空間中任意點一個 2D 或者 3D 向量的函式。公式表示如下所示:
$\vec{F}\left(x,y \right)=P\left(x,y\right)\vec{i}+Q\left(x,y\right)\vec{j}$
$\vec{F}\left(x,y,z \right)=P\left(x,y,z\right)\vec{i}+Q\left(x,y,z\right)\vec{j}+R\left(x,y,z\right)\vec{k}$
其中,$P$,$Q$,$R$ 各表示一個標量函式,即它們的返回值是一個標量;$\vec{i}$,$\vec{j}$,$\vec{k}$ 各表示一個基向量。(參考文獻[1])
上面數學的解釋大家可能不熟悉,但是很多人或多或少的都看過向量場的圖片形式,如下所示:
散度和旋度(Curl and Divergence)
首先,我們來定義一個 $\nabla$ 操作,如下所示:
$\nabla=\frac{\partial }{\partial x}\vec{i}+\frac{\partial }{\partial y}\vec{j}+\frac{\partial }{\partial z}\vec{k}$
其中$\partial$表示的是偏導數符號,不熟悉的讀者可以去複習下微積分或者參考文獻[2]。有了這個操作符之後,我們定義旋度為:
$curl\vec{F}=\nabla\times\vec{F}=(\frac{\partial R}{\partial y}-\frac{\partial Q}{\partial z},\frac{\partial P}{\partial z}-\frac{\partial R}{\partial x},\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y})$
其中$\times$為叉積操作符(參考文獻[3])。
有了旋度之後,我們再來定義散度,同樣的,公式如下所示:
$div\vec{F}=\nabla\cdot \vec{F}=\frac{\partial P}{\partial x}+\frac{\partial Q}{\partial y} + \frac{\partial R}{\partial z}$
特別的,散度和旋度之間有如下的一個關係:
$div(curl\vec{F})=0$
以上內容,參考文獻[4]。
根據上面的公式,我們可以知道,對於一個向量場的旋度場,它的散度為 0,即它是一個無源場(Divergence-Free)。而一個散度為 0 的向量場,表示這個場是不可壓縮的流體,這對日常所見的流體來說是一個很重要的視覺性質,所以據此我們可以使用一個場的旋度場來模擬流體效果。
Curl Noise
所謂 Curl Noise,即是對一個隨機向量場,進行 Curl 操作之後得到的新場。因為滿足散度為 0 的特性,所以這個場看上去就具有流體的視覺特性。如果用這個場作為速度去控制粒子,即可得到開頭視訊中流動的效果。
2D Curl Noise
前面我們說過,需要一個隨機的向量場。這裡我們使用 Perlin Noise 來進行模擬,關於 Perlin Noise 網上一堆資料,這裡就不再贅述。
我們假設 Perlin Noise 的函式為:
$N(x,y)$
它的返回值是一個標量值。然後據此建立一個新的向量場:
$\vec{F}(x,y) = (N(x,y), N(x,y))$
然後對這個新的向量場進行 Curl 操作,即可得到旋度場。
前面只說過 3D 情況下的 Curl 操作是怎麼樣的,這裡給出 2D 版本的 Curl 操作:
$curl\vec{F}(x, y) = (\frac{\partial N(x,y)}{\partial y}, -\frac{\partial N(x,y)}{\partial x})$
這裡就只剩下了最後一個問題,那就是形如 $\frac{\partial N(x,y)}{\partial x}$ 這樣的偏導數,該怎麼計算。我們這裡使用一個名為有限差分的方法(Finite Difference Method)來近似求解。
Finite Difference Method
根據文獻[2]中對於偏導數的描述,我們知道 $\frac{\partial N(x,y)}{\partial x}$ 只是一種表達方式,它的精確表示方法為:
$\frac{\partial N(x,y)}{\partial x}= N_x(x,y) = \lim_{h\to0}{\frac{N(x + h,y)-N(x,y)}{h}}$
而後面極限的表達方式則給了我們近似計算這個偏導數的方法,只要給定一個較小的 $h$ 值,就能夠近似的得到偏導數的結果。而這種計算方法即為:有限差分方法(Finite Difference Method)。
除了上面的極限表示方法之外,還有另外一種極限表示方法,如下所示:
$\frac{\partial N(x,y)}{\partial x}= N_x(x,y) = \lim_{h\to0}{\frac{N(x,y)-N(x-h,y)}{h}}$
這兩種差分方法分別稱之為前向差分(Forward Difference)和逆向差分(Backward Difference)方法。我這裡主要使用逆向差分方法。
有了計算偏導數的方法之後,我們就可以實際帶到 2D Curl 操作的公式進行計算,如下是計算 2D Curl Noise 的虛擬碼:
vec2 computeCurl(float x, float y) { float h = 0.0001f; float n, n1, n2, a, b; n = N(x, y); n1 = N(x, y - h); n2 = N(x - h, y); a = (n - n1) / h; b = (n - n2) / h; return vec2(a, -b); }
知道怎麼計算 2D Curl Noise 之後,我們用計算出來的 Curl Noise 作為速度場去控制粒子進行運動,如下是 2D Curl Noise 控制粒子運動的效果:
3D Curl Noise
有了前面 2D Curl Noise 的實現,如法炮製的實現 3D Curl Noise 的推導。
3D Perlin Noise 函式定義為:
$N(x,y,z)$
以此構造出來的 3D 向量場為:
$\vec{F}(x,y,z)=(N(x,y,z),N(x,y,z)N(x,y,z))$
對這個場進行 Curl 操作,得到:
$curl\vec{F}=(\frac{\partial N(x,y,z)}{\partial y}-\frac{\partial N(x,y,z)}{\partial z},\frac{\partial N(x,y,z)}{\partial z}-\frac{\partial N(x,y,z)}{\partial x},\frac{\partial N(x,y,z)}{\partial x}-\frac{\partial N(x,y,z)}{\partial y})$
據此,給出計算 3D Curl Noise 的虛擬碼:
vec3 computeCurl(float x, float y) { vec3 curl; float h = 0.0001f; float n, n1, a, b; n = N(x, y, z); n1 = N(x, y - h, z); a = (n - n1) / h; n1 = N(x, y, z - h); b = (n - n1) / h; curl.x = a - b; n1 = N(x, y, z - h); a = (n - n1) / h; n1 = N(x - h, y, z); b = (n - n1) / h; curl.y = a - b; n1 = N(x - h, y, z); a = (n - n1) / h; n1 = N(x, y - h, z); b = (n - n1) / h; curl.z = a - b; return curl; }
以下是根據得到的 3D Curl Noise,並一次控制粒子進行運動的效果:
結論
Curl Noise 在遊戲中有大量的運用,Unity 的粒子系統的 Noise Module 就內建了 Curl Noise 的實現。作為遊戲開發的人員,很有必要了解下這個技術的原理,便於在實際開發中靈活運用。本文的主要原理來自於參考文獻[5],感興趣的可以深入去了解。
參考文獻
[1] Section 5-1 : Vector Field
[2] Section 2-2:Partial Derivatives
[3] Section 5-4:Cross Product
[4] Section 6-1:Curl And Divergence
[5] Curl-Noise for Procedural Fluid