張國棟 王 波 朱長德 熊桂芳 郭 澍
(1.南京信息工程大學遙感與測繪工程學院,江蘇 南京 210044;2.航天遠景科技(南京)有限公司,江蘇 南京 210046)
“數字海洋”是由“數字地球”衍生而來的,構建“數字海洋”基礎信息框架是我國重大海洋計劃之一。近年來,隨著地理信息科學、遙感(Remote Sensing,RS)、計算機技術、虛擬現實(Virtual Reality,VR)的快速發展,數字海洋已無法滿足國家日益增長的需求?!叭S海洋”的發展與完善是“數字海洋”的最高目標,對我國海洋科學研究、海洋國防安全、海洋資源管理、海洋信息服務和海洋決策支持等具有非常重要的意義。海浪作為海洋的“頭發”,其重要性不言而喻,海浪模擬是一個復雜的過程,且與計算機技術息息相關。目前,各國學者對海浪的研究建模方法可分為基于流體力學的建模方法、基于海浪譜的建模方法、基于幾何模型的建模方法、基于動力模型的建模方法、基于分形的建模方法[1]。柏林噪聲(Perlin Noise)建模方法生成的視覺元素看起來更自然,是基于幾何模型使用頻率較高的一種方法。1983年,Perlin[2]因不滿當時計算機產生不自然的紋理效果,首次提出Perlin噪聲,并于1985年在SINGGRPH上公開發表該算法,憑此獲得了奧斯卡科技成果獎。隨后該方法被用于構造海浪高度場。Johanson[3]通過引入投影網格的概念,在后空間構造海面網格,并通過疊加Perlin噪聲來構造海面高度場。宋歌等[4]使用Perlin噪聲擾動的方式來模擬實現視點遠處的海洋運動。
Unity是一個由Unity Technologies研發的、讓用戶方便快捷地創建三維視頻游戲、建筑可視化、實時三維動畫、創建三維地形等多平臺的綜合性開發游戲平臺。Unity不僅能在Windows、Linux、MacOS等主流平臺上運行,還可將其開發的產品發布到Windows、WebGL、Android等平臺 上,Unity Web Player插件也支持發布網頁游戲及網頁瀏覽,是一個全面且功能強大的專業引擎。Robert[5]使用Unity平臺來模擬海岸環境的空間和位置。王懷兵等[6]使用Unity自帶的粒子系統進行船行波的三維仿真試驗。劉雪梅等[7]提出利用Perlin噪聲構造分形,網格頂點采用位移映射算法,在Unity3D中生成地形。歐陽勁夫等[8]使用Unity3D引擎來模擬光的反射和折射,利用可視化編程插件來制作水面水體著色器,從而實現對水面波浪的模擬,該方法雖實現了低性能耗損,但也存在生成的水體細節表現不足等問題。綜上所述,本研究在Unity3D平臺中,通過創建海面網格,然后使用Perlin噪聲生成海面高度場,使用高次插值函數對生成的高度場進行平滑擬合,解決由海浪波峰過于尖銳導致的失真問題,在光影效果上使用Shader Graph渲染管線,采用菲涅爾效應進行線性插值,來生成海面顏色,然后通過法線進行貼圖,生成真實狀態的海洋場景。
海浪的生成先通過三維Perlin噪聲進行疊加來產生分形Perlin噪聲,將產生的高度場映射到海面網格,然后添加波面紋理和光照,從而生成海浪模型。主要包括海面網格的構建、生成高度場、渲染紋理。
Unity創建的海面網格方便快捷,可分為以下三個步驟。
1.1.1 網格基礎組件。先打開Unity3D軟件,在Hierarchy中創建一個空物體(Game Object),并將其重命名為Perlin Waves。在inspector的Add Component中添加Mesh Renderer組件,該組件可對生成的網格模型進行渲染。創建完Game Object后,在project中創建C#腳本,腳本名要與創建的物體名相同,否則運行時會出現錯誤。
1.1.2 網格屬性。打開剛創建的C#文件,定義海面網格所需的變量,即海面寬度x、海面長度z、海面網格的分辨率Di mens ions。Unity坐標系采用左手坐標系,即用左手食指和大拇指擺出“L”的手勢,且食指向上,大拇指指右,然后伸出中指,此時中指會指向正前方。大拇指、食指、中指分別對應x、y、z軸的正方向,即海面網格的長和寬用z和x表示。
1.1.3 計算網格。由1.1.2中定義的變量來計算網格的點(verts)、UV值(uvs)及三角形(tries)。網格的點是用來計算生成的三角形,UV值是u、v紋理貼圖坐標的簡稱,其定義了圖片上每個點的位置信息,三角形顯然就是用來形成海面網格的。其中,各個值的計算方法如下。
點的計算首先要計算索引值,其計算公式見式(1)。
頂點的計算公式見式(2)。
UV坐標計算公式見式(3)至式(5)。
三角形計算見式(6)。
式中:index為格網索引值;Di mens i ons為生成的海浪的分辨率,分辨率越大代表著海浪細節表現得越充分;uvs為海浪的偏移,即海浪在某時刻的位置;vec為海浪頂點高度;tries為生成的海浪三角網格。
Unity中的網格全是由一個個三角形相互連接構建而成的,所以三角形的生成與存儲非常重要,可通過Mesh.vertices函數對其進行保存。
基礎網格構建完畢后,保存腳本文件,然后返回Unity中運行,從而生成一個網格模型。由于在編寫腳本文件時只聲明了海面網格,并沒有對網格高度進行計算,所以只能得到一個沒有波動的三角海面網格(見圖1)。
1.2.1 Perlin噪聲算法。Perlin噪聲算法是Ken Perlin提出的一種強大算法,該算法常用于生成隨機數,從而被廣泛應用于電影、游戲等領域中。在游戲領域中,該算法用于生成各種波形、起伏不平的材質、紋理等,如《我的世界》游戲中的地形就是用該算法生成的。除此之外,該算法還可生成火焰、云等效果。在電影領域中,該算法主要用來生成水面波浪。從本質上講,Perlin噪聲函數是一個隨機數生成器,但其與普通的隨機數生成器有所不同。Perlin噪聲函數是用一個整數作為參數,然后返回一個基于該參數的隨機數。為了引入噪聲函數的相關概念,用三次函數對其進行插值即可形成連續的噪聲函數。
1.2.2 高度場生成。使用二維Perlin噪聲建立海浪高度場,通過Perlin噪聲建立海浪高度場的步驟如下。
①定義一個晶格結構。構建一個二維平面,同時生成每個頂點的“偽隨機”梯度向量,即每個頂點都生成一個偽隨機梯度向量。偽隨機是指對任意組相同的輸入,必定得到相同的輸出,且其不是完全隨機的,只是在生成函數不變的前提下,每個坐標的梯度向量都是確定不變的。梯度向量是指該頂點相對單元正方形內某點的影響是正向還是負向的。二維Perlin噪聲的梯度向量一般用(-1,1)、(0,1)、(1,1)、(-1,0)、(1,0)、(-1,-1)、(0,-1)、(1,-1)這八個向量來表示。采用這些特殊梯度向量是為了避免“鏡像現象”。二維情況下的晶格是一個正方形。只要確定左下P0(x0,y0)和右上P1(x1,y1)兩個點的坐標,就可確定其所在晶格。
②確定梯度向量,得到點積。由步驟①得到晶格,輸入一個點P(x,y),該點即為給定點,可用floor函數求得P0點坐標,見式(7)、式(8)。
式中:x0、y0是P0點橫縱坐標;floor函數為向下取整函數,如a=2.2,則floor(2.2)=2.000 000。
根據給定的點P可得到晶格的頂點坐標,即P0(x0,y0)、P(1x1,y1)、P(2x2,y0)、P(3x0,y1)。然后在頂點處生成隨機梯度向量,即g00、g01、g10、g1(1見圖2)。
圖1 海面網格
圖2 晶格結構和梯度向量
接著進行點積運算,先要確定晶格頂點到P點的距離向量,用dist i,j來表示,見式(9)。
式中:di st為距離向量;i、j分別取0或1,如i=0,j=0,則P0(x0,y0)到P的距離向量用dist00來表示。則點積計算見式(10)至式(13)。
式中:s、t為點積結果。當兩個向量的夾角小于90°時,點積結果為正;當兩個向量夾角大于90°時,點積結果為負;當兩個向量夾角等于90°時,點積結果為0。
③插值,得到噪聲。一般選擇緩和曲線作為插值函數,并代入坐標進行線性插值計算。使用緩和曲線計算插值因子時,噪聲函數的起點和終點的變化比較緩慢,且中間部分變化明顯,也就是說當接近固定點時,變化速率變緩。1985年,Perlin首次提出Perlin噪聲時,使用的緩和曲線為y=3x2-2x3。該緩和曲線的一階導數滿足連續性,但其二階導數在晶格頂點處(x=0或1)不為0,呈現明顯的不連續性。所以,Perlin在2002年發表的論文中,將其改進為y=6x5-15x4+10x3,該函數無論是一階導還是二階導都滿足連續性(見圖3)。且函數越是高階,可導函數的曲線就越平滑,本研究選取y=6x5-15x4+10x3作為插值函數,進行噪聲平滑處理。
使用緩和曲線計算插值因子的步驟如下。
首先分別計算出x、y方向的加權因子,見式(14)、式(15)。
然后使用加權因子分別對x、y方向進行線性插值,先對x方向進行線性插值,見式(16)、式(17)。
最后對y方向進行線性插值,得到最終的噪聲值,見式(18)。
得到的最終插值圖像見圖4。
使用Shader Graph進行紋理渲染。Shader Graph是在Unity 2018版本后推出的一款通過可視化界面的節點連接來實現著色器的創建及編輯的可編程式渲染管線工具。相較于Unity Shader,Shader Graph的渲染管線不用編寫復雜的代碼,其圖形化編輯界面也更加友好。
Shader Graph有兩種打開方式,一是打開Unity編輯器,在創建項目時選擇Universal Render Pipeline來創建項目,通過這種方式創建的項目可直接使用Shader Graph,不再進行配置。二是創建一個默認的3D模板,然后安裝shader graph和URP渲染管線。其渲染過程如下。
1.3.1 海浪顏色設置。設置兩個顏色,分別記為A和B,使用線性插值及菲涅爾效應得到海面顏色(見圖5)。
圖3 插值函數對比
圖4 2D Perlin函數插值圖像
圖5 顏色光照設置
1.3.2 設置海浪偏移。先設置海浪紋理進行貼圖,然后對其UV值進行設置,世界坐標、像素值以及UV值的對應關系為:X-R-U、Y-G-V、Z-B、WA。在Unity中,海平面處于XZ平面,需要R、B對應U、V。接著設置偏移量,通過改變時間,來改變貼圖的移動方向。由于一張UV貼圖不能體現出海面波浪效果,所以要建立兩個方向相反的UV貼圖。最后將兩條渲染管線貼圖效果進行法線融合,將結果連接到Normal中(見圖6)。
1.3.3 設置海浪平滑。只要創建一個平滑屬性,連接到Smoothness,就可在屬性面板進行設置。
圖6 偏移設置
為了驗證Perlin噪聲算法生成的海面及添加光照后的效果,進行仿真試驗。根據本研究提出的方法,在Windows10 64位操作系統、Unity3D 2020.3.32f1c1平臺中,利用C#語言在Visual Studio 2019中編寫腳本,天空盒使用系統自帶的sky box,最終生成的海浪效果如圖7所示。圖7(a)為Normal Strength=4、Tilling=0.08時的海面,圖7(b)為Normal Strength=4、Tiling=0.2時的海面,圖7(c)為Normal Strength=8、Tilling=0.08時 的 海 浪 形 態,圖7(d)為Normal Strength=8、Tilling=0.2時的海浪形態。從圖7(a)和圖7(b)可以看出,在法線強度相同的前提下,Tilling越大,海浪越密集。由圖7(a)和圖7(c)可知,當偏移指數相同時,Normal Strength越大,海浪的顏色越深,細節表現越明顯。當Normal Strength相同時,Tilling指數越大,海浪的波紋越密集,海浪起伏變緩。
Unity3D處理光照和陰影比較方便,通過向場景中添加兩個平行光,可真實地表現出白天和夜晚狀態下的海面效果,添加光照生成的海面效果如圖8所示,圖8是在相同Normal Strength和Tilling參數下不同時間的海浪。圖8(a)為添加光照,顯示為白天效果;圖8(b)為關閉光照,顯示為夜晚效果。
圖7 不同參數下的海浪形態
圖8 不同時間的海面
本研究利用Unity3D平臺,從海面網格的創建出發,利用Perlin噪聲生成海面高度場及海面渲染三個模塊,對海面建模仿真進行研究,調整光照與法線強度,可方便快速且真實地表現出海面。雖在各個模塊都要考慮其仿真效果和真實感,但并沒考慮在自然環境下(降雨、風等)的仿真效果,以及沒有采用實時數據對海面進行建模,針對上述問題,該方法還有待進一步研究。