水波模擬是計算機圖形學中的經典問題。要在實時應用中呈現逼真的水面效果,必須在物理真實感與計算效率之間取得平衡。本文將詳細介紹如何利用 C++ 實現一個高效的實時水波模擬系統,涵蓋波的物理特性、數學建模、數據結構設計以及渲染技術。
在開始編程之前,我們需要理解水波的幾個核心物理特性:
傳統正弦函數或精確物理公式計算量過大,無法滿足實時渲染(≥20 FPS)的需求。我們採用一種基於差分的快速近似算法,僅使用加減法和位移運算。
建立兩個與水面圖像等大的數組 buf1 和 buf2,分別存儲前一時刻和後一時刻每個像素點的波幅(能量)數據。初始狀態下水面平靜,所有值為 0。
假設某點下一時刻的振幅受其上下左右四鄰域點及自身當前振幅影響,且四鄰域影響機會均等:
X0' = a(X1+X2+X3+X4) + bX0
在無阻尼理想條件下,系統總能量守恆,即所有點振幅之和不變,可得 4a + b = 1。取最簡解 a = 1/2, b = -1,因 1/2 可用右移運算 >>1 實現,避免乘除。最終公式為:
X0' = (X1+X2+X3+X4)/2 - X0
引入實際阻尼,每次計算後波幅按比例衰減,經測試 1/32 (即 >>5) 為宜。
void RippleSpread() {
for (int i = BACKWIDTH; i < BACKWIDTH*BACKHEIGHT - BACKWIDTH; i++) {
// 波能擴散
buf2[i] = ((buf1[i-1] + buf1[i+1] + buf1[i-BACKWIDTH] + buf1[i+BACKWIDTH]) >> 1) - buf2[i];
// 波能衰減
buf2[i] -= buf2[i] >> 5;
}
// 交換緩衝區
short *ptmp = buf1;
buf1 = buf2;
buf2 = ptmp;
}
利用水面折射原理,水面傾斜導致視線偏移。我們用相鄰點波幅之差近似偏移量:
xoff = buf1[k-1] - buf1[k+1]; // 左右波幅差
yoff = buf1[k-BACKWIDTH] - buf1[k+BACKWIDTH]; // 上下波幅差
根據偏移量從原始圖像采樣像素到渲染目標,實現水面折射視覺效果。渲染循環中需進行邊界檢查,防止越界訪問。
在指定位置製造負脈衝波源,模擬投石入水。以石頭入水點為圓心,在半徑範圍內將波幅設置為負值(-n, n∈[32,128]),圓形區域近似處理。代碼如下:
void DropStone(int x, int y, int stonesize, int stoneweight) {
// 邊界檢查
for (int posx = x-stonesize; posx < x+stonesize; posx++)
for (int posy = y-stonesize; posy < y+stonesize; posy++)
if ((posx-x)*(posx-x) + (posy-y)*(posy-y) < stonesize*stonesize)
buf1[BACKWIDTH*posy + posx] = -stoneweight;
}
該算法計算量與波浪複雜度無關,幀率穩定。在 AMD K6-200 上 320×240 分辨率可達 25 FPS。此方法易於擴展至煙霧、大氣、陽光等流體特效模擬。
通過物理特性分析、數學近似建模、雙緩衝區迭代與折射渲染,我們實現了高效逼真的實時水波模擬。代碼結構清晰,採用 DirectX 窗口模式,便於調試與學習。