· 8 MIN READ
紙飛機的摺疊數學:從 A4 到 BufferGeometry
買過紙飛機模型的人都見過同一種失敗:表面太平、邊緣太圓、光一打像塑膠。問題不在多邊形數量——一架真正摺出來的紙飛機只需要幾十個三角形。問題在法線。
摺痕是法線的斷崖#
一張紙摺起來之後,摺痕兩側是兩個平面,法線相差幾十度。平滑法線(computeVertexNormals() 之類)會把斷崖抹成圓角,於是紙變成橡膠。正確做法是:同一個位置的頂點,屬於兩個面時拆成兩份,各帶各的法線。
// 龍骨左側與右側共用機鼻頂點位置,但法線不同
const nose = [0, 0, -0.5];
const keelL = { pos: nose, n: norm(-0.4, 0.6, -0.7) };
const keelR = { pos: nose, n: norm( 0.4, 0.6, -0.7) };代價是頂點數翻倍,但總量不過幾百個。省這個是得不償失的。
幾個讓它「像摺的」的幾何事實#
- 龍骨不是平的。 紙有厚度,摺過之後中央會自然隆起。我的機身截面是一個淺 V,夾角約 152°,不是 180°。
- 翼面上反 6°。 paper glider 沒有副翼,靜穩定度全靠這個角度。視覺上也更好:平翼看起來像斷了。
- 翼尖上折要帶弧度。 真紙的翼尖是摺痕加扭曲,用兩個三角形加一點 twist 比單一的硬翻邊自然。
- 機鼻要收尖但不能薄成線。 兩層紙疊在一起是 0.24mm,擠出時給它一個 0.6mm 的實體厚度,逆光時邊緣才有那條透光的高光線。
程序化貼圖:三張畫布就夠#
不放任何外部貼圖。用 Canvas 生成三張:
- 法線貼圖:摺線畫成硬邊階梯(高斯 sigma 小於 1px),紙纖維用拉長的 value noise 疊加,最後疊一層極細的顆粒。纖維方向要沿紙紋(長邊),亂方向一眼就假。
- 粗糙度貼圖:基底 0.82,摺線處壓到 0.72——摺過的地方紙纖維被壓實,會稍微反光。這個差異小到單獨看根本發現不了,但它是「舊紙」和「新影印紙」的區別。
- 泛黃:只在邊緣和摺痕周圍做 2–4% 的暖色偏移。髒汙是禁忌,髒了就是道具,泛黃才是紙。
動態比靜態更騙人#
靜止的模型再準,動起來不對就全毀。三件事:
- 氣流抖動用 Perlin,振幅 0.05,位置、俯仰、滾動各用不同 seed,三個軸同一個 noise 會像蕩鞦韆。
- 翼尖擺動是彈簧:
v += (target - x) * k - v * d,高速時目標角 0.8°。線性插值沒有過衝,沒有過衝就沒有彈性。 - 轉彎滾動用阻尼追值,25–35° 之間。紙飛機滾動永遠領先於轉向——這是它穩定性的來源,鏡頭要是跟不上這個滯後,看起來就是 CG 在飛,不是紙在飛。
完整的頂點表不複雜,難的是每一條邊都要回答同一個問題:這條邊兩側,是同一個平面嗎?