基於Substance Designer 的2D/3D Perlin Noise(II)

最近花了些時間搜集和整理關於2d/3d雜訊, 以及程序化紋理/建模/特效相關的論文, 感覺學到了不少東西, 但終究還是紙上得來終覺淺, 在實現的過程中發現自己實在還是太弱了...好了, 不發牢騷了, 進入主題吧.


I. 原理---Gradient Nosie(梯度雜訊)

在前一篇文章中, 我提到的SubstanceDesigner中的2D Perlin noise, 實際上是一種稱為sparse convolution 的 nosie, 最早由Lewis提出, van Wijk也提出過類似的spot noise, 之後我做流體模擬時可能會提到這個人.

這篇文章, 我將主要介紹經典的Perlin noise演算法, 從最簡單的2D情形開始說起:

---------------------(i)Slicing!-------------------

Perlin將空間(在2D情形下就是一張空白畫布)劃分為nxn個的方形網格, 每個網格的長度剛好為整數, 例如256x256的就可以分成256x256個網格:(當然, 也可以分成128x128, 64x64, etc. 但其實思路是一樣的, 這裡為了簡潔起見, 只講晶格長度為1的情形)

在每個晶格(Lattice, 在這裡就是方格)的頂點M處設置一個隨機的(梯度)向量(Mx, My):

----------------(ii)Dot Dot Dot!------------------

對於畫布內每個點 P , 取其坐標(x, y)的整數部分:  (Floor(x), Floor(y)) , 該坐標即為其所在晶格的左上角頂點的位置;然後計算其到所在晶格左上角頂點的坐標之差,也就是其小數部分構成的向量 (fx, fy) , 並取其與左上角頂點梯度向量 Grad(i, j) 的點積:

(fx, fy)ullet(Grad(i, j)_{x}, Grad(i, j)_{y}) = fx ast Grad(i, j)_{x} + fy ast Grad(i, j)_{y}

然後採用類似的計算得到其餘三個頂點的點積:

(fx - 1, fy) ullet (Grad(i+1, j)_{x}, Grad(i+1, j)_{y}) ......... 右上角

(fx - 1, fy - 1) ullet (Grad(i+1, j+1)_{x}, Grad(i+1, j+1)_{y}) ......... 右下角

(fx, fy - 1) ullet (Grad(i, j+1)_{x}, Grad(i, j+1)_{y}) ......... 左下角

---------------------(ii)我插!---------------------

然後我們就要試圖對這四個算出來的點積進行線性插值, 如果直接用到左上角晶格頂點距離作插值係數的話, 可以觀察一下相應的插值函數圖:

y = x

由此可見, 在插值接近0(點 P 接近其所在晶格左上角頂點)或1(點 P 晶格右下角頂點)時, 函數的變化率非常尖銳, 這會導致結果圖在各個方格交界處有明顯的不連續跡象; 所以我們轉而使用開始和結束都光滑過渡(即, 一階導數和二階導數都為0)的函數

f(t) = 6t^{5}-15t^{4}+10 t^{3} ,使用 f(t) 對fx, fy分別進行處理,來獲得平滑的結果, 在3D情形下, 還要算fz;

記算得的2個權重為 f(fx) = w_{x}, f(fy) = w_{y} ;

隨後做3次Linear interpolation:

前兩次以 w_{x} 為權重, 算出晶格左上角頂點(i, j), 右上角頂點(i+1, j)之前算出來點積的插值a,

以及左下角和右下角之間的插值b;最後用 w_{y} 為權重算出a與b的權重, 如圖:


II. 實現---Substance Designer中的2D Perlin Nosie

理論和原理就到此為止了; 接下來講的是我在Substance Designer中的實現代碼, 我順便把perlin noise拓展了一下, 做出來了FBm noise, 如圖:

其中負責生成Perlin Noise的部分為:

-----------------------------------------------------------------------------------------------

左邊的1024x1是一張白雜訊圖 W , 是1/1024, 2/1024, ... 1這1024個不同灰度值隨機排列而成的一張條狀帶, 用來提供隨機的Hash Table.何為Hash Table? 這是一個非常常用且強大的數據結構, 要講的話講10篇文章都講不完; 簡而言之, 隨機數的產生方式如下:

設白雜訊圖 W 中位置 (x, 0.5) 處的灰度值為 Perm(x) ,

則晶格頂點(i, j)對應的隨機數(x. y)為

(Perm(i +(Perm(j)), Perm(i +(Perm(j) + 1))

-----------------------------------------------------------------------

中間的Pixel Processor是隨機數發生器, 負責利用輸入的白雜訊來提供隨機值.

這裡使用hash的目的是為每個晶格頂點賦予對應的固定隨機值, 如果使用SD內置的隨機數的話,就不方便以晶格位置為seed來獲取隨機值了。

------------------------------------------------------------------------

最主要的部分是右邊的Pixel Processor:

(不懂編程的讀者可能不太懂, 我在這裡簡略解釋這張圖的一些元素:

$pos, $size是SD自帶的系統常量, 分別表示像素位置和圖像長寬; min表示取得輸入值中的最小值; sample節點負責在指定的輸入圖(比如input0, 就是輸入的第一張圖)在輸入的位置(比如$pos)處提取顏色.)

(點擊可看高清的節點圖)

其邏輯與上文所描述的原理一致.這裡就不加累述了,有興趣的讀者不妨下載下來打開看看~

Perlin.sbs //dependency提示就不用管它了?

pan.baidu.com


推薦閱讀:

本車十次碰撞,成績九勝一平:《火爆狂飆》與Criterion瘋狂
美術和程序同時掉水裡先救誰?
讓角色半透明:樹形結構(三)
GMS2中滑鼠點擊後自動選擇最靠前的物體
基礎1——遊戲物體和腳本——造一個鍾

TAG:圖像處理 | 技術美術 | 遊戲開發 |