每天一個(gè)shader技巧day3 - Gerstner Wave
day2的時(shí)候說(shuō)到了模擬水紋水波,想到了另一個(gè)常用的波函數(shù)-Gerstner Wave,今天就來(lái)說(shuō)說(shuō)這個(gè)為什么需要這個(gè)波函數(shù)以及它的應(yīng)用。
先看看海浪圖

不難看出,現(xiàn)實(shí)世界的海浪是有尖銳的峰頂?shù)?,比較波濤洶涌。那不管是sin或者cos函數(shù),都沒(méi)有一個(gè)尖銳的峰頂去模擬這個(gè)尖銳陡峭的峰頂。
那么為什么sin或cos函數(shù)都是圓潤(rùn)的呢,我們把這兩個(gè)函數(shù)離散化一下,變成分散的點(diǎn)。
Sin wave

可以看到對(duì)于sin或cos函數(shù),對(duì)于不同橫坐標(biāo)的點(diǎn),每個(gè)點(diǎn)只會(huì)往上下兩個(gè)方向移動(dòng),對(duì)于sin波可以有如下表達(dá)
A = 振幅, l = 控制周期, v = 移動(dòng)速度, t = 時(shí)間。?是為了把周期從0到2
縮放到0到
shadertoy簡(jiǎn)單代碼如下

Gerstner Waves?
因?yàn)閟in或cos這些波函數(shù)無(wú)法正確的模擬我們想要的波浪效果,所以我們需要一個(gè)可以更加準(zhǔn)確描述風(fēng)所造成的海浪的波的表達(dá),那就是Grestner波了(查找資料過(guò)程中了解到了一個(gè)Stokes波,也是對(duì)水波浪的一個(gè)建模,但還挺復(fù)雜,有空再看)。先看看Grestner波做了什么事

現(xiàn)在,對(duì)于每個(gè)離散的點(diǎn),除了上下移動(dòng),還會(huì)左右移動(dòng),并且是一個(gè)圓周運(yùn)動(dòng)。而且在波峰位置,點(diǎn)會(huì)更加聚集,在波谷位置,點(diǎn)就相對(duì)分散。那么既然是圓周運(yùn)動(dòng)自然可以想到圓的參數(shù)方程
? ?

那么我們對(duì)每個(gè)離散的點(diǎn)的x,y做一個(gè)這個(gè)變化就行了,看看代碼

可以看到圖片有被拉伸的效果,其實(shí)做到這里就差不多。但是如果你拉高(振幅)或者縮小
(周期的話(huà))會(huì)看到一個(gè)打結(jié)的現(xiàn)象

那么這肯定不是我們想要的,如果在模擬水波的時(shí)候出現(xiàn)這種情況就會(huì)有一個(gè)斷裂的現(xiàn)象

既然是振幅和周期引起的,那么我們就對(duì)其建立一個(gè)制約關(guān)系,去解決這個(gè)問(wèn)題。這里直接說(shuō)結(jié)論?(振幅 * 周期 <= 1)時(shí)才不會(huì)有這個(gè)斷裂的問(wèn)題。根據(jù)這個(gè)不等式,我們可以引入一個(gè)新的變量來(lái)控制振幅的值,引入新變量后公式為
代碼如下
最后需要考慮的便是速度這一塊了,因?yàn)檫@是對(duì)水波浪的一個(gè)模擬,那么速度肯定不是無(wú)限快的,波的速度并不是任意的,其速度與波長(zhǎng)和重力相關(guān),直接上公式,代碼如下

二維模擬的情況到這就差不多了,推廣到三維的話(huà)也是差不多,只需要在z軸做一個(gè)和x軸相同的操作即可(可以使用一個(gè)權(quán)重控制x軸和z軸的偏移貢獻(xiàn))。同時(shí)同一個(gè)點(diǎn)的Gerstner波是可以疊加的,具體做法可以將生成波的這個(gè)過(guò)程封裝成一個(gè)函數(shù),然后返回一個(gè)vector3,在每個(gè)點(diǎn)運(yùn)行一下這個(gè)函數(shù)然后加起來(lái)即可。