Vanilla Lab

← 回文章列表

AA 45 分的模型寫出25KB的秋水共長天一色,然後被我退回前一版...

2026-10-19
低太陽下的開闊海面

整件事的起點就是王勃。(雖然滕王閣序是江不是海)
這是我現在寫的版本,但當時只給了愛馬仕第一行...

Make the most realistic ocean you can, in raw WebGPU (single HTML, no libraries, no external assets).

Mood: open sea at low sun, warm haze, one lone seabird.
The feeling of「落霞與孤鶩齊飛,秋水共長天一色」.

Camera: floating ~7 m above the swell, 58° FOV, opening view wide enough to see the horizon. Drag to look, WASD to move.

Constraints: 60 fps on an M1 at 1280×633, 4× MSAA.

Done means: it runs, you screenshot it yourself and look at it, and zooming in doesn't reveal a grid.

當時只給了第一行。沒有規格、沒有參考圖、沒有『大概要幾分像』,我把它丟給 DeepSeek V4.1-Flash-1017,讓它自己決定什麼叫最寫實。

👉 點這裡全螢幕開
(Chrome 113/Safari 26 以前不支援 WebGPU,只會看到靜態圖)

第一版:Gerstner,7 分

它挑了最穩的路:20 道 Gerstner 波疊起來,波長從 74 公尺一路收到 60 公分,方向繞著風向散開。水面用螢幕空間投影網格(Johanson projected grid),每個三角形在畫面上大小一致,不會像輻射狀網格那樣在幾十公尺外開始打結。

天空跟水面共用同一個散射函式。所以「秋水共長天一色」不是調色,而是同一個函式算出來的:水面的天,就是天空那顆太陽、那道霾。反射與背景不可能對不上,因為它們是同一個東西。

4 倍 MSAA、60 fps、單檔 25 KB、零外部資源、零貼圖。浪是程式算的,雲是程式算的,太陽也是。

然後我看著它,給了 7 分。滑順、有氣氛,但浪的節奏看得出來是數學。

第二版:FFT,4 分

我要求升級。它照做了,而且做得比我預期的完整:512×512 的 Phillips 風浪譜、兩張 storage buffer 做 ping-pong、每幀 18 個 compute dispatch 跑完兩軸 IFFT,頂點著色器再從高度場取樣。全部在 GPU 上,60 fps 沒掉。

遠看真的不一樣。浪不再是平行脊線,是真正隨機的海面。我在遠景截圖前後比了兩張,水面 79% 的像素在變,波譜確實在動,不是貼圖捲動。

然後把鏡頭拉近,畫面就垮了。「這版的水跳動太假了,上版的還比較好看。」
👉 第二版 「海水變成外星球滾動的地表」
(Chrome 113/Safari 26 以前不支援 WebGPU,只會看到靜態圖)

為什麼貴的輸給便宜的

不是操作的問題。FFT 的高度場一格 0.66 公尺,鏡頭一近,一格被放大到幾十像素,高頻浪的相位又跑得快,就看得到一格一格跳。Gerstner 是解析函式,法線逐像素重算,貼到多近都不會跳。

貴的版本輸給便宜的,原因不在演算法,在取樣密度跟觀看距離。波譜再正確,取樣撐不住近距離,人眼看到的是「跳」,不是「真」。

中間還踩了三個坑,每一個都值得記下來。

一是 queue.writeBuffer 在同一個 command buffer 裡不保證順序。18 個 dispatch 如果邊送邊寫參數,全部都會讀到最後一組,等於 17 個白跑。解法是每個 dispatch 一個 256-byte 槽位、一個 bind group。

二是 FFT 的正規化。頻譜取共軛之後,實部就是高度,不用再乘 N²;我多乘了一次,浪高變成幾千公尺,相機直接埋在玻璃裡。畫面上是一團抽象的反光,看起來像材質壞掉,其實是世界尺度錯了。

三是 headless 瀏覽器黑畫面不一定是 shader 壞了。分頁隱藏時 requestAnimationFrame 會整個停掉,幀數永遠是 0、畫面永遠是黑的、console 永遠乾淨。那天有一半的時間在追這個,最後是 Page.bringToFront 四個字解決。

最後沒做的東西

白沫。70 公尺的湧浪,浪頂是一條幾百公尺長的帶,任何「浪頂高度超過某個值就上白沫」的寫法都會變成一張會跟著浪跑的白紙。我試過兩次,一次是一片奶白,一次是滿畫面白點。最後選擇不做:乾淨的湧浪比假的白沫好看。這是取捨,不是完成。

分數是那天之後才來的

這批工作發生的時候,Artificial Analysis 還沒收錄 1017。
我當時寫下一句:我猜會落在 40 分上下,這個猜測留著當驗證點。

答案是 45 分,比預測高五分,在開放權重模型裡排第二,只輸小米 MiMo V2.6 Pro 的 46 分。前一版是 39 分,一口氣加了六分。

權重攤開是 763B 參數,活躍 16B。
每次推論只叫醒百分之二的權重,卻要寫出上面那個 512×512 的 FFT、18 個 compute dispatch、和一套跟天空共用的散射函式。

分數解釋了它為什麼寫得出來,但沒有解釋那天下午真正的成本。
494 次呼叫裡,海洋這一段只花 165 次、約 NT$10;剩下的一大半,花在三次退版、三個坑、和一個最後決定不做的效果上。

結論

  • 演算法升級不等於畫面升級:FFT 的波譜比 Gerstner 正確,但取樣撐不住近距離,使用者看到的是跳,不是真。正確的技術用錯地方,比不升級更糟。

  • 便宜的先上:Gerstner 版先能看、能跑、能被 judge,才有資格談升級。反過來做,會花兩倍時間得到一個更差的結果,還得退回去。

  • 分數與判斷是兩件事:AA 給 45 分,開放權重第二,這顆模型確實寫得出 512×512 的 FFT 海;但決定「這版不要」的還是人,那一段沒有 benchmark 可以量。

模型把 shader 寫出來了,決定退回前一版的還是人。

這一片海的數字

  • 單檔:25 KB / 743 行

  • 效能:60 fps(M1, 1280×633, 4× MSAA)

  • 這個 session:494 次 API 呼叫、輸出 77.5 萬 token、約 ¥5.8(NT$26)

  • 海洋這一段:165 次呼叫,估計約 NT$10

  • AA 分數:45( Intelligence Index v4.3.2,開放權重第二,但他只是Flash版,期待Pro版破50分 ? )

  • 外部資源:0(幾何、材質、天空、文字全部程式生成)



౨ৎ Anthony 不負責任編輯 ~

留言 (0)