← 返回作品場景
海洋渲染

遊戲裡的海是怎麼做出來的?零基礎看懂即時海洋渲染(Godot × Crest)

你有沒有想過,遊戲裡那片一望無際、浪會起伏、船會搖晃的海,是怎麼做出來的?

這篇文章以 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 次),整個流程都會跑一遍:

  1. CPU 決定相機在哪裡、這一幀要算哪些東西。
  2. compute shader 在顯示卡上算出浪、泡沫和海床深度。
  3. 結果寫進一疊跟著相機走的圖。
  4. 水面 shader 讀這疊圖,把網子推成浪、塗上顏色。
  5. 就是你在畫面上看到的海。

接下來:零基礎做遊戲海洋

這篇是「零基礎做遊戲海洋」系列的第 0 集。從下一集開始,我們會在 Godot 裡從零動手,一集一集做出會起伏的浪、光照與反射、泡沫、浮在海上的船、無限大的海,最後潛到水下。每一集都以海為主題,順便帶到需要的 Godot 基礎。

下一篇:第 1 集「裝好 Godot,搭出第一片海」(即將推出)


Crest Water 由 Wave Harmonic 開發,MIT 授權:https://github.com/wave-harmonic/crest。本文的圖片來自本頻道影片「從網子到大海」。

熱門文章

從零做出空戰遊戲的體積雲:Godot 4 實作與效能調校

這陣子在把一款台灣的 HTML5 射擊遊戲《台海雄鷹》改寫成 Godot 4 的 3D 空戰遊戲。飛機會在 50 到 2000 公尺高度、以時速數百到上千公里飛行,所以「雲」不只是背景:玩家會從雲底下穿過、鑽進雲裡、在雲上面纏鬥。這篇記錄我怎麼從零做出一套體積雲與天氣系統,以及最後怎麼把它調到三檔畫質、並修掉一個很難察覺的畫面瑕疵。 📦 範例程式 :文中的體積雲我抽成了一個可以直接執行的 Godot 4 範例,打開就能在雲裡飛、切換天氣和畫質, clouds/ 資料夾也可以直接複製到自己的專案使用(MIT 授權): https://github.com/smdexw/godot-volumetric-clouds 從 4200 公尺俯看的晴天積雲(高畫質) 一、先研究「要做什麼」,再自己寫 我手邊有一套 Unity 的天氣商業套件。它功能很完整:體積雲、降雨、閃電、霧、天空、日夜、風、極光……但它是為一般遊戲設計的,也不能直接搬到 Godot。所以我的做法是:只參考它「有哪些功能、整體架構怎麼分工」,程式碼一行都不用,依照空戰遊戲的需求重新設計。 整理下來,對空戰最有價值的依序是:天氣設定與轉換系統、體積雲、霧與空氣透視、閃電、海面雲影、降雨、日夜循環。極光、積雪、地面濕氣這些在海上用不到,直接不做。 另外有幾個「一般遊戲沒問題、但空戰會出事」的地方,從一開始就要避開: 高速穿雲 :飛機一秒前進一百多公尺,雲旁邊的畫面變化非常大。 降雨 :跟著鏡頭的雨粒子是照步行速度設計的,時速一千公里時全部被甩在後面。 雲在飛行高度裡 :雲層剛好落在戰鬥的高度範圍,「飛進雲裡」是常態,不是例外。 二、天氣系統:一種天氣是一組數字 我把一種天氣定義成一個 WeatherProfile 資源:雲量、雲底和雲頂高度、雲的種類(層雲到積雲)、密度、暗度、雨量、閃電頻率、霧、太陽被遮掉多少、高空卷雲……目前有晴、少雲、多雲、陣雨、陰、暴風雨、薄霧幾種預設。 換天氣時不是全部一起漸變,而是錯開: 要下雨時,雲先變厚(用掉 70% 的時間),雨在 40% 時才開始下;放晴時雨很快停,雲慢慢散。 這個小細節讓天氣轉換看起來很自然。 左上晴天積雲、右上陰天、左下暴風雨、右下從暴風雨雲底往上看 三、體積雲:光線步進 雲是用光線步進(ray marc...