你有沒有想過,遊戲裡那片一望無際、浪會起伏、船會搖晃的海,是怎麼做出來的?
這篇文章以 Godot 引擎和開源海洋系統 Crest 為例,用九個白話概念,帶你從零看懂「即時海洋渲染」。不需要會寫程式,也不需要數學基礎。文章最後有影片版,九段動畫會把每個概念畫給你看。
先說結論:一張網子、一疊圖、幾萬個波
整個海洋系統的核心可以濃縮成一句話:
顯示卡先把「每個位置的水要移多少」算好,存成一疊跟著相機走的圖;畫面要畫海時,再讀這些圖,把一張網子推成浪。
下面九個概念,就是把這句話一塊一塊拆開。
1. 水面是一張會動的網子
電腦畫 3D,畫的全部都是很多小三角形拼成的「網子」(mesh)。海面也一樣,就是一張很大的網子。網子上的每個交點叫做頂點。
只要讓每個頂點上下移動,平平的網子就會起伏起來,看起來像一道浪;再多疊幾種移動方式,就開始像真的海面了。
2. 近看細、遠看粗(LOD)
海一眼望去有好幾公里。如果每個地方都畫得一樣密,三角形會多到幾百萬個,電腦根本跑不動。
解法是一圈一圈的網子:靠近相機的地方最密,每往外一圈,格子就大一倍。這個技巧叫做 LOD(Level of Detail,細節層級)。這圈網子會跟著相機走,所以不管你開船開多遠,海都看起來無限大。
真正的難處在細節:網子移動時只能「一格一格地跳」,否則水面會像在游動;圈和圈的交界也要慢慢過渡,才不會出現裂縫或跳動。
3. 浪是很多波疊在一起
一個正弦波(像 ~~~ 的曲線)非常規律,一看就知道是假的。把幾個大小、速度、方向不同的波加起來,形狀就複雜多了。
真實的海,是成千上萬個波疊在一起。遊戲常見兩種做法:
- Gerstner 波:手動挑幾個波疊起來,浪峰比正弦波尖,簡單又好控制。
- FFT 海洋:用海洋學的統計資料描述「各種波各有多少」(海浪頻譜),再用快速傅立葉轉換(FFT)一次把幾萬個波疊好。電影和遊戲做海,大多用這個方法。
4. 顯示卡同時做幾百萬件小事(Shader)
CPU 像幾個很聰明的教授,一次做一件事;GPU(顯示卡)像幾千個小學生,每人只會簡單的計算,但是大家同時做。
寫給 GPU 的小程式叫做 shader,分成三種:
- vertex shader:每個頂點跑一次,決定它移到哪裡(把網子推成浪)。
- fragment shader:每個像素跑一次,決定它是什麼顏色(反光、泡沫、透明)。
- compute shader:不畫圖,只拿 GPU 來算東西(算浪、算泡沫)。
5. 用圖片存數字(紋理)
平常說的「貼圖」是一張圖片,但圖片裡的每個像素,其實就是幾個數字。
Crest 把「每個位置的水要移多少」寫成一張圖,每一格存一個高度。compute shader 把數字算好寫進去,vertex shader 再讀出來,把網子推成浪。因為有好幾圈網子,所以是一疊圖(紋理陣列),每一圈一張。這就是整個海洋系統的核心。
6. 讓它看起來像水
形狀對了,還要看起來像水:
- Fresnel 效果:低頭往下看,光大多穿進水裡,水看起來是透明的;往遠方斜著看,水面像鏡子一樣反射天空。
- 泡沫:浪被擠尖的地方會起白色泡沫,然後慢慢散掉。
- 水色:水越深顏色越深藍;越淺越偏綠,也越透明。
7. 讓船浮起來
船要浮起來,就得知道船底每個位置的水有多高。做法是在船身放幾個探針,哪個探針沉得越深,就把那裡往上推得越大力,船就會跟著浪傾斜。
麻煩的是,水的高度是顯示卡算的,傳回來給物理引擎大約會晚兩幀,船會慢半拍。解法是用船現在的速度,預測兩幀後的位置,把延遲補回來。
8. 跑到水下(後處理)
相機穿過水面時,畫面中間會出現一條水線;到了水下,整個畫面要加上藍色的霧、從上面照下來的光束,海底還有晃動的光紋(焦散)。這些都是畫面畫好之後再整張處理的,叫做後處理。
9. 全部串起來
每一幀(一秒大約 60 次),整個流程都會跑一遍:
- CPU 決定相機在哪裡、這一幀要算哪些東西。
- compute shader 在顯示卡上算出浪、泡沫和海床深度。
- 結果寫進一疊跟著相機走的圖。
- 水面 shader 讀這疊圖,把網子推成浪、塗上顏色。
- 就是你在畫面上看到的海。
接下來:零基礎做遊戲海洋
這篇是「零基礎做遊戲海洋」系列的第 0 集。從下一集開始,我們會在 Godot 裡從零動手,一集一集做出會起伏的浪、光照與反射、泡沫、浮在海上的船、無限大的海,最後潛到水下。每一集都以海為主題,順便帶到需要的 Godot 基礎。
下一篇:第 1 集「裝好 Godot,搭出第一片海」(即將推出)
Crest Water 由 Wave Harmonic 開發,MIT 授權:https://github.com/wave-harmonic/crest。本文的圖片來自本頻道影片「從網子到大海」。








