先放結果展示:
海浪的模擬,可以理解為一堆任意方向的正弦波的疊加,這些正弦波的頻譜(相位和振幅)會隨著時間而變化。
\[h(\overrightarrow x, t) = \sum_ \tilde h (\overrightarrow k, t) e^
\]\[\begin
其中:&l_m, l_n為整個m*n個網格的邊長 \\
\overrightarrow k &= (k_x, k_z) \\
k_x &= \frac, -\frac \leq m < \frac \\
k_z &= \frac, -\frac \leq n < \frac \\
\overrightarrow x &= \left( \frac, \frac \right) \\
& 0 \leq x < m \\
& 0 \leq y < n \\
\end
\]這裡需要說明下,這個式子和標準2d idft長得不太一樣:
\[x(x, y) = \frac \sum_^\sum_^ x(m, n) e^ + \frac)}
\]這是因為:
\(\overrightarrow k \cdot \overrightarrow x\)的結果就是$ 2\pi (\frac + \frac) $這裡和公式是一樣的
求和的範圍不一樣,標準公式是從0-m、0-n,而這裡是\(-\frac \leq m < \frac\)和
\(-\frac \leq n < \frac\)這裡推導下
\[設\overrightarrow k = \left( \frac, \frac \right); 0\leq m < m, 0\leq n < n
\]\[\begin
h(x, y, t)
&= \frac \sum_^ \sum_^ \tilde h(m,n,t) \exp\left( j \overrightarrow k \cdot \overrightarrow x \right) \\
&= \frac \sum_^ \sum_^ \tilde h(m,n,t) \exp\left( j \frac + j \frac \right) \\
&= \frac \sum_^ \sum_^ \tilde h(m,n,t) \exp\left( j2\pi \left( \frac + j \frac \right) \right) \exp \\
&= (-1)^ \frac \sum_^ \sum_^ \tilde h(m,n,t) \exp\left( j2\pi \left( \frac + j \frac \right) \right) \\
\end
\]最後相當於是在標準的ifft上面乘了乙個\((-1)^\),之前程式沒有這麼寫,直覺上l的引數總是不太對,l比較大的時候,海浪應該比較密集,l比較小的時候,比較平緩,改成這樣就符合直覺了。
我們有了從海浪頻域轉到海浪時域的工具,那麼海浪的頻域是怎麼來的呢?通過海洋統計學,可以得到海浪的頻譜隨著時間變化的函式:
\[\tilde h(\overrightarrow k, t) =
\tilde(\overrightarrow k) e^} +
\tilde(-\overrightarrow k) e^}
\begin
其中:&l為整個n*n個網格的邊長 \\
&k_x = \frac, -\frac \leq m < \frac\\
&k_z = \frac, -\frac \leq n < \frac\\
&\overrightarrow k = (k_x, k_z)\\
&k = |\overrightarrow k| \\
\\另外:
&\tilde(\overrightarrow k) = \frac (\xi_r + \xi_i) \sqrt \\
&p_h(\overrightarrow k) = a\frac}} |\overrightarrow k \cdot \overrightarrow \omega| ^ 2 \\
\overrightarrow \omega:&風向 \\
v:&風速 \\
\end
\]可以看到,這個函式巨複雜無比,不過好在我們也是可以讓它跑在gpu上的。
得到了波浪的頻譜,就可以動手開始實現了,hlsl實現如下:
#pragma kernel generatespectrum
#pragma kernel ifft2x
#pragma kernel ifft2y
static const uint fft_stages = 8;
static const uint fft_dimension = 1 << fft_stages;
static const uint fft_butterflys = fft_dimension >> 1;
static const float pi = 3.14159265;
groupshared float2 pingpongarray[fft_dimension * 2];
uint reversebits(uint index, uint count)
float2 complexmultiply(float2 a, float2 b)
void butterflyonce(float2 input0, float2 input1, float2 twiddlefactor, out float2 output0, out float2 output1)
float2 euler(float theta)
texture2dsrctex;
rwtexture2ddsttex;
void ifft2(uint2 id, bool horizontal)
else
uint2 offset = uint2(0, fft_dimension);
[unroll]
for (uint s = 1; s <= fft_stages; s++) else else
} }}[numthreads(fft_butterflys, 1, 1)]
void ifft2x(uint3 id : sv_dispatchthreadid)
[numthreads(1, fft_butterflys, 1)]
void ifft2y(uint3 id : sv_dispatchthreadid)
const static float g = 9.8;
float pow2(float x)
float pow4(float x)
float2 h0(float2 k_v, float k, float2 w, float v, float2 xi, float sqrta)
float _time;
float _sqrtamplitude;
float2 _winddirection;
float _windspeed;
float _patchlength;
texture2dtwinrandomgaussiantexinput;
rwtexture2dspectrumoutput;
rwtexture2dgradientspectrumoutput0;
rwtexture2dgradientspectrumoutput1;
[numthreads(4,4,1)]
void generatespectrum(uint3 id : sv_dispatchthreadid)
有幾點需要注意下:
原本是想利用hammersley sequence做gpu上的隨機數生成,但是這樣發現還是出現了一些重複的pattern,於是還是放在cpu端去做。
生成phillips spectrum可以抽出時間引數t,預計算一部分(generatespectrumstepone),減少每幀需要做的計算量。
tessendorf, j., 2001. simulating ocean waters. in siggraph course notes (course 47), acm siggraph
fynn-jorin flügge, realtime gpgpu fft ocean water simulation
FFT與遊戲開發(二)
首先補充下傅利葉變換的原理 把函式分解到一系列正交基上,這些正交基復合下面的條件 begin int f k x f j x mathrm d x neq 0 k j int f k x f j x mathrm d x 0 k neq j end 其實這就是一種基的變換,圖形學中也有一些變換,如座...
遊戲開發入門(五)遊戲動畫系統
1.遊戲的發展史也是遊戲動畫的發展史 最初的2d動畫是通過序列幀來完成的,和動畫電影原理一樣,將多張連到一起就形成了動畫,表現力比較差。如今的3d動畫其實原理相同,但是在各個細節的處理已經大大不同了,需要多個動畫完美的銜接融合,與外界環境有著很好的互動。2.動畫是關於時間的函式 剛體動畫 隨著時間變...
Android遊戲開發菜鳥之路(五)
最近一直在根據 android games beginning 一書寫乙個遊戲框架,2d遊戲基本的功能此框架基本可以實現,因此突發奇想,寫乙個簡單的連連看遊戲試試這個框架的功力。連連看遊戲的主要兩個重點是遊戲布局的生成和判斷兩個模組是否可以消除。先說遊戲布局的生成 這裡我實現的方法比較樸素,一開始先...