GraphicsLab Project 之 Curl Noise

做者:i_dovelemonhtml

日期:2020-04-25算法

主題:Perlin Noise, Curl Noise, Finite Difference Methodsession

 

引言

        最近在研究流體效果相關的模擬。通過一番調查,發現不少的算法都基於必定的物理原理進行模擬,計算量相對來講都比較高昂。最終尋找到一個基於噪音實現的,可在視覺上模擬流體效果的方法:Curl Noise。題圖就是經過 Curl Noise 模擬的流體向量場控制的百萬粒子的效果。curl

 

背景知識

        在講解什麼是 Curl Noise 以前,咱們須要瞭解一些相關背景知識。ide

 

向量場(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}$ui

$\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}$url

        其中,$P$,$Q$,$R$ 各表示一個標量函數,即它們的返回值是一個標量;$\vec{i}$,$\vec{j}$,$\vec{k}$ 各表示一個基向量。(參考文獻[1])spa

        上面數學的解釋你們可能不熟悉,可是不少人或多或少的都看過向量場的圖片形式,以下所示:code

 

散度和旋度(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 Flow

相關文章
相關標籤/搜索