基於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!------------------
對於畫布內每個點 , 取其坐標(x, y)的整數部分: , 該坐標即為其所在晶格的左上角頂點的位置;然後計算其到所在晶格左上角頂點的坐標之差,也就是其小數部分構成的向量 , 並取其與左上角頂點梯度向量 的點積:
然後採用類似的計算得到其餘三個頂點的點積:
......... 右上角
......... 右下角
......... 左下角
---------------------(ii)我插!---------------------
然後我們就要試圖對這四個算出來的點積進行線性插值, 如果直接用到左上角晶格頂點距離作插值係數的話, 可以觀察一下相應的插值函數圖:
由此可見, 在插值接近0(點 接近其所在晶格左上角頂點)或1(點 晶格右下角頂點)時, 函數的變化率非常尖銳, 這會導致結果圖在各個方格交界處有明顯的不連續跡象; 所以我們轉而使用開始和結束都光滑過渡(即, 一階導數和二階導數都為0)的函數
,使用 對fx, fy分別進行處理,來獲得平滑的結果, 在3D情形下, 還要算fz;
記算得的2個權重為 ;
隨後做3次Linear interpolation:
前兩次以 為權重, 算出晶格左上角頂點(i, j), 右上角頂點(i+1, j)之前算出來點積的插值a,
以及左下角和右下角之間的插值b;最後用 為權重算出a與b的權重, 如圖:
II. 實現---Substance Designer中的2D Perlin Nosie
理論和原理就到此為止了; 接下來講的是我在Substance Designer中的實現代碼, 我順便把perlin noise拓展了一下, 做出來了FBm noise, 如圖:
其中負責生成Perlin Noise的部分為:
-----------------------------------------------------------------------------------------------
左邊的1024x1是一張白雜訊圖 , 是1/1024, 2/1024, ... 1這1024個不同灰度值隨機排列而成的一張條狀帶, 用來提供隨機的Hash Table.何為Hash Table? 這是一個非常常用且強大的數據結構, 要講的話講10篇文章都講不完; 簡而言之, 隨機數的產生方式如下:
設白雜訊圖 中位置 處的灰度值為 ,
則晶格頂點(i, j)對應的隨機數(x. y)為
-----------------------------------------------------------------------
中間的Pixel Processor是隨機數發生器, 負責利用輸入的白雜訊來提供隨機值.
這裡使用hash的目的是為每個晶格頂點賦予對應的固定隨機值, 如果使用SD內置的隨機數的話,就不方便以晶格位置為seed來獲取隨機值了。
------------------------------------------------------------------------
最主要的部分是右邊的Pixel Processor:
(不懂編程的讀者可能不太懂, 我在這裡簡略解釋這張圖的一些元素:
$pos, $size是SD自帶的系統常量, 分別表示像素位置和圖像長寬; min表示取得輸入值中的最小值; sample節點負責在指定的輸入圖(比如input0, 就是輸入的第一張圖)在輸入的位置(比如$pos)處提取顏色.)
其邏輯與上文所描述的原理一致.這裡就不加累述了,有興趣的讀者不妨下載下來打開看看~
Perlin.sbs //dependency提示就不用管它了
推薦閱讀:
※本車十次碰撞,成績九勝一平:《火爆狂飆》與Criterion瘋狂
※美術和程序同時掉水裡先救誰?
※讓角色半透明:樹形結構(三)
※GMS2中滑鼠點擊後自動選擇最靠前的物體
※基礎1——遊戲物體和腳本——造一個鍾