GPU Gems 基於正弦波之和的水面渲染 (1)

幾個月前,我在知乎上問了關於水面渲染的問題。托各位朋友的福,我得到了很多有用的資料。前段時間一直忙於學業,最近終於有時間來研究一下水面渲染技術了。

FOXhunt 總結了水面渲染的一些技術,有興趣的朋友可以去看看。我決定從其中的「基於正弦波之和的水面渲染」開始。

我參照的資料是 GPU Gems 的第一章節。個人覺得,這章寫得過於簡潔了,忽略了很多基礎和細節。有些公式不實際推導一下的話,根本不知道其中某些變數的意思。並且,網頁版和紙質版在這一章節的一些公式中,存在一些錯誤;以及有些變數的定義沒說清楚。為了看懂這些公式,必須額外查很多資料。總之,我決定一步一步、慢慢地實現這個方法。

整體思路

  • 波可以向遠處傳播
  • 複雜的波形可以通過正弦波的疊加得到
  • 渲染需要兩種數據:頂點位置,頂點法向量
  • 頂點等價于波的採樣點,採樣點的法向量可以從波的函數求得

所謂採樣點,可參考 GPU Gems 上的這張圖

即,在波上取若干個點,求出這些點的 (x, y) 作為頂點坐標。

1.2.1 節可以求出 t 時刻各頂點的坐標

那麼,首先以求頂點位置 (採樣點位置) 為目標吧。

這裡,稍微說一下正弦波的公式。2D 情況下,沿 x 軸正方向傳播的正弦波,在 t 時刻 x 點處的 y 方向位移是

y(x, t) = A,sin,2pi (,frac{t}{T},-,frac{x}{lambda},),  x>0

其中,A 是振幅,T 是周期,λ 是波長。波速v = flambda,頻率f = frac{v}{lambda},角速度varphi = frac{2pi}{T}

注意,GPU Gems 1.2.1 對頻率的定義是錯的。首先,紙質版上定義的是omega = 2pi / L。這裡,它用omega 代表頻率,用 L 代表波長。而網頁版更是少印了個 π. 並且,網頁版在波速 (S) 的定義那一行中,也少寫了個 π, 紙質版上是varphi = S 	imes 2pi / L

言歸正傳。上式中,令omega = -frac{2pi}{lambda}就可以得到 GPU Gems 上的格式

y(x,, t) = A,sin,(,xomega + tvarphi,)

注意,這裡的 ω 不是頻率,只是一個代號。由於我假設正弦波沿 x 正方向傳播,所以 GPU Gems 上的方向向量 D 可以省略。

擴展到 3D 場景時,GPU Gems 上的公式 (1) 其實應該寫成這樣

其中,old{D} = left[ cos	heta,sin	heta 
ight] 表示「正弦波的發射方向」,而 θ 代表:想像一個以 (0, 0) 為中心的單位圓,θ 是半徑與 x 軸正方向的夾角 (從第一象限開始、逆時針方向增加)

這裡,我用下面 3 個正弦函數的疊加,在 Octave (Matlab 的影分身) 上做了模擬:

輸出結果是

Octave 代碼在這裡。

今後打算

求出採樣點上的法向量,並根據採樣點 (頂點) 和法向量,渲染水面。


推薦閱讀:

Ray Marching Ocean
譯:UE4是如何渲染一幀的(1)
為什麼需要模擬HDR
UE4中快速實現簡單GPU流體模擬
走樣與反走樣(Aliasing/Anti-Aliasing):Graphics Cases

TAG:計算機圖形學 |