FFT 海洋模拟:从频谱推导到 GPU 实时渲染
1. 引言
实时海洋渲染是 3A 游戏中最具挑战性的视效课题之一。当我们谈论”逼真的海面”时,真正令人信服的并不是单纯的高光与折射,而是那种来自统计学随机性的涌浪律动——长周期的大涌、中频的风浪、高频的毛刺细波,它们在频域中各自占据不同的能量区间,叠加后形成肉眼难以分辨规律的混沌美感。
为什么不用 Gerstner Waves?
Gerstner 波是正弦波的参数化变体,其波峰尖锐、波谷平缓,视觉上已非常接近真实海浪。然而它的本质是有限个确定性波形的叠加,参数越多越难手调,且难以产生真正意义上的随机海面纹理。即使叠加数十组波参数,也会在远处产生肉眼可辨的”重复模式”。
FFT 海洋的核心思想则完全不同:先在频域(Frequency Domain)用物理驱动的海浪频谱定义每个空间频率分量的能量,再通过逆快速傅里叶变换(IFFT)一次性将数万个波分量转回空间域,等效于同时叠加了成千上万组 Gerstner 波——而这在 GPU 上只需数毫秒。
| 方法 | 实质 | GPU 开销 | 随机性 | 可平铺 |
|---|---|---|---|---|
| 正弦波叠加 | 少量确定波 | 极低 | ❌ | ❌ |
| Gerstner Waves | 参数化正弦 | 低 | △ | △ |
| FFT 海洋 | 频谱统计模型 | 中等 | ✅ | ✅ |
| SWE 流体 | 偏微分方程 | 高 | △ | ❌ |
本文定位
本文的重心在于波形模拟——从频谱物理到 IFFT 计算,以及 Jacobian 驱动的白沫模拟。渲染着色部分(PBR 水面材质、折射等)将仅作概要性介绍。工程实现参考 Ocean-URP(Unity 2022 LTS + URP)及 GodotOceanWaves 两个开源项目。
2. 数学与物理基础
海洋模拟归根结底是一道物理建模题:我们要用什么样的数学结构,才能捕捉到真实海面那种”无规律的规律感”? 本节从物理直觉出发,逐步建立 FFT 海洋模拟的完整数学框架。
2.1 海浪的统计学视角:混沌中的秩序
想象你站在远洋货轮的甲板上俯视海面。你会发现一件奇妙的事:眼前的波浪从不重复,每一帧都与上一帧不同——但它又绝非完全随机的噪点。长涌从远方慢慢涌来,风浪在近处此起彼伏,偶尔一个大浪把前者掀得支离破碎。这种”有结构的随机性”,正是海洋物理的核心特征。
海洋学家的解答是:开阔洋面的风浪可以建模为一个平稳高斯随机过程。这句话拆开2个问题来理解:
高斯随机过程意味着什么?
海面上任意一点的高度
这个假设有其物理依据:在开阔深水中,不同频率的波满足线性叠加原理,互相之间几乎不传递能量(非线性效应在一级近似中可忽略)。
为什么高斯假设有效?
由中心极限定理,大量独立随机波的叠加趋向正态分布。海面上叠加着来自四面八方、历经数百公里传播的无数涌浪——它们各自有独立的随机初相位。当叠加数目足够大,任意一点的高度自然收敛到高斯分布。
这与音频工程中的白噪声生成如出一辙:无数独立的微小随机冲击叠加,最终产生高斯分布的噪声信号。
基于这个认识,我们可以在频域中独立为每个波矢量
其中:
— 两个独立的标准正态随机数,提供随机性(每次生成不同的海面) — 海浪频谱的平方根,控制各频率分量的振幅”音量”
这里
空间频率
波矢量
2.2 Phillips 频谱:风与波的第一个约定
功率谱
[Tessendorf 2001][1] 提出了一个形式优雅的答案:
要理解这个公式,不妨把它分解为三个相互独立的调控旋钮:
①
②
直觉理解:风速越高,
③
这是波传播方向单位向量
从频谱到视觉效果:改变参数会发生什么?
把频谱想象成海面波浪的”音频均衡器”:
- 风速翻倍(
m/s): 变为 4 倍,截断波长变长,海面出现更大的涌浪,整体波高约增加 4 倍。 - 振幅常数
翻倍:所有尺度的波等比例增强,海面整体变得”汹涌”,但频谱形状不变。 - 风向旋转 90°:能量分布在 k 空间内旋转 90°,海浪涌来的方向改变,但波长分布不变。
这三个参数分别控制了海浪的尺度分布、总体强度和传播方向,在 Compute Shader 实现中对应三个独立的可调量。
Phillips 频谱的局限性
Phillips 谱假设海浪”充分发展”(fully developed),即风无限持续、无边界限制。这导致两个工程上的问题:
- 高频端衰减过快:真实海面在高频段的能量比
衰减更缓慢(约 量级),导致 Phillips 谱生成的海面高频细节不足,显得”太光滑”。 - 允许逆风波:
对顺风和逆风对称,但真实海洋中顺风波远强于逆风波。可通过额外添加 因子( 控制方向性)来修正。
2.3 JONSWAP 频谱:来自北海的工程校准
Phillips 谱是一个”理论优先”的模型。1968–1969 年,五国联合海洋考察队在北海(Joint North Sea Wave Project)实地测量了数百组真实海浪数据,从中拟合出更精确的半经验模型——JONSWAP 频谱[2]:
关键参数的物理直觉:
:谱峰角频率, 为 10m 高度处的风速,与风区长度(fetch)直接相关 :峰值增强因子,这是 JONSWAP 最显著的特征——峰值处能量约是 PM 谱的 3.3 倍,波形更”集中” :Phillips 常数,由风速和风区长度联合决定 ( ), ( ):谱峰不对称宽度
“fetch”(风区)是理解 JONSWAP 的关键词。 它指的是风在开阔水面上持续吹拂的距离:
直觉类比
把海浪的生长想象成一个”共振系统”。风开始吹起的瞬间,海面只有短小的毛刺波(高频),它们吸收风能并逐渐向低频传递能量——就像钢琴的高频键先被拨响,能量慢慢向低频键”流淌”。风区越长,这个能量级联过程越充分,谱峰频率越低(对应波长越长的主波)。
当能量级联最终达到平衡时,就形成了 Pierson-Moskowitz(PM)谱,即 JONSWAP 公式中的基底项。PM 谱对应”充分发展”的海浪;而 JONSWAP 的
| 风区长度 | 谱峰周期 |
典型场景 |
|---|---|---|
| 10 km | ~4 s | 内湾、湖泊 |
| 100 km | ~7 s | 近海陆架 |
| 500 km | ~10 s | 大洋边缘 |
| 充分发展 | ~12–15 s | 开阔大洋(PM 谱) |
数据基于 JONSWAP 拟合关系
方向扩散函数:上面的
其中
最终的二维功率谱为:
其中 Jacobian 因子
FFT 框架中水深的工程约束
传统 FFT 海洋框架将水深
工程结论: 若需模拟复杂近岸地形(岸礁浅水区的波浪折射、水深渐变下的波速减慢等),必须引入 SWE 浅水方程或局部变形网格,而不能在 FFT 框架内直接实现。
2.4 弥散关系:为什么大浪比小浪跑得快?
弥散关系(Dispersion Relation)是波动力学的基石,它建立了波的空间特征(波数
在实时渲染中,水深
这条公式隐藏着一个反直觉的结论。
波的相速度(波峰移动的速度)为:
在深水中,长波比短波传播得更快。
10m 波长的波:
100m 波长的涌浪:
1000m 波长的海啸(极端情况):
一个经典的现实例证
台风过后,距离中心数千公里的海岸往往会比台风本身提前 1–2 天感受到巨浪。这正是弥散的结果:台风激发了各种波长的波,其中长涌(波长 200~500m,周期 12–18s)以 15–25 m/s 的速度超前传播,远比台风中心移速(通常 5–10 m/s)更快,成为台风的”前奏”。气象员正是通过追踪波浪周期来反推风暴位置。
弥散关系之所以对 FFT 模拟至关重要,在于它决定了每个频率分量如何随时间演化——这正是下一节的主题。
2.5 时间演化:每个波分量独立地”旋转”
有了初始频谱
物理上,海面是一系列平面波的叠加,每个平面波以固定角速度
但仅此一项不够——我们需要确保变换回空间域后得到的高度值
为什么需要复共轭项?
傅里叶变换有一个基本性质:若信号
简单说:
时间演化后,这个对称性必须依然保持,因此我们令:
第二项是对
欧拉公式视角下的时间演化:
可以把每个频率分量
- 高频分量(大
,大 )旋转快,对应涟漪的快速变化 - 低频分量(小
,小 )旋转慢,对应大涌的缓慢起伏 - 所有分量以各自的
独立旋转,互不干扰
这也解释了为什么每一帧的时间演化 Pass 如此廉价:只需对每个格点执行一次 sincos(omega * t)——这正是 TimeDependentSpectrum.compute 的全部工作。
2.6 从频域到空间域:IFFT 是如何”拼装”海面的
现在所有频率分量都有了在时刻
这正是逆傅里叶变换(IFFT)的定义:
直觉上:每个频率分量
写成
这是一个
为什么 FFT 海面可以无缝平铺(Tileable)?
离散傅里叶变换的输入/输出天然具有周期性边界。
2.7 Choppy 波:水粒子的”圆周运动”
如果只有高度偏移
这种不对称性的物理根源,在于水粒子并非原地上下运动,而是做圆周运动。
水粒子的轨迹
在深水线性波理论中,水面附近的水粒子沿圆形轨迹运动(深水)或椭圆轨迹(浅水):
- 当粒子在轨迹顶部时,水平速度与波传播方向同向(被”推着走”),粒子在水平方向被向前压缩,多个粒子”挤”向同一处——形成波峰的尖锐聚拢
- 当粒子在轨迹底部时,水平速度与波传播方向反向(被”拉着退”),粒子在水平方向被拉开——形成波谷的宽缓展开
这种水粒子的整体平移(叠加到垂直运动之上)就是水平位移(Choppy displacement)。
Gerstner 波的正确画法
| 分量 | 只有垂直位移 | 加上水平位移(Choppy) |
|---|---|---|
| 波峰形状 | 圆弧 | 尖锐 ✓ |
| 波谷形状 | 圆弧 | 宽缓 ✓ |
| 视觉真实感 | 人工感强 | 接近真实 ✓ |
| 白沫生成 | 无从判断 | Jacobian 可检测 ✓ |
Gerstner 波正是将这种水平位移参数化的结果。FFT 框架通过在频域中引入位移频谱,等效实现了同样的效果。
频域中的水平位移推导:
我们知道高度场的梯度(斜率)为
水平位移与梯度方向一致但有 90° 相位差(位移超前于斜率,波峰处水平速度最大而斜率为零),在频域中体现为乘以
类似地,高度场梯度(直接用于法线计算):
频域乘法 = 空域微分的深层含义
Choppy 位移的强度由参数
2.8 二维频谱可视化
为了直观感受频谱的形状,下面的交互演示渲染了
📡 二维海浪功率谱可视化(k 空间)
颜色越亮表示该波矢方向/波长的能量越高。中心为低频(长波),边缘为高频(短波)。箭头指示风向。
二维
3. Cooley-Tukey 蝶形算法
3.1 计算复杂度:从 到
朴素的 DFT 对
以
- 朴素 DFT:
次复数运算 - FFT:
次
FFT 的核心洞察是 Danielson-Lanczos 引理:一个
3.2 DIT(按时间抽取)分解
Cooley-Tukey DIT-FFT 将输入序列按奇偶下标递归分割:
其中旋转因子(Twiddle Factor)定义为:
注意
3.3 蝶形运算单元
单个蝶形运算(Butterfly Operation)的核心是:
直觉理解:一次蝶形运算消耗 1 次复数乘法 + 2 次复数加法,同时产生 2 个输出。每个 Stage 有
3.4 交互演示:8 点 FFT 蝶形图
下面的交互演示展示了一个
🦋 Cooley-Tukey DIT-FFT — 交互蝶形图(N = 8)
输入序列经位反转后,经过 log₂8 = 3 个 Stage 完成变换。高亮蝶形表示当前 Stage 的运算单元。
位反转示例:索引
001₂ → 反转为 100₂,即 1 ↔ 4。
3.5 Stockham FFT:GPU 友好的无冲突变体
经典 Cooley-Tukey 算法存在一个 GPU 实现上的问题:每个 Stage 都需要原地(in-place)写回,即读写同一块内存,在 Compute Shader 中会引发 bank conflict。
Stockham FFT 通过使用**双缓冲(Ping-Pong Buffer)**解决这一问题:每个 Stage 从一块纹理读取,写入另一块纹理,交替使用,始终保证访问的连续性和无冲突性。这正是 simulation.md 中 FFT.compute 采用 Stockham IFFT 的核心原因。
实践中的额外优化: 可以将旋转因子 (index_top, index_bot, twiddle_real, twiddle_imag),在每个蝶形 Pass 中直接采样,避免运行时的 exp() 计算。
4. Compute Shader 架构与工程实现
本节结合 Ocean-URP 的实际实现,逐 Pass 拆解 GPU 计算管线。整体架构分为四个阶段:
4.1 Pass 1 — 初始频谱生成
初始频谱只需在参数(风速、风向)变化时重算,是整个管线中唯一的预计算阶段。
核心内核 CalculateInitialSpectrum 对每个
📝 InitialSpectrum.compute
1 | |
4.2 Pass 2 — 时间演化
每帧执行,将静态初始频谱按时间
📝 TimeDependentSpectrum.compute — 时间推进内核
1 | |
4.3 Pass 3 — Stockham IFFT
对 Pass 2 的输出执行二维逆变换:
1 | |
Shared Memory 优化
在 Compute Shader 中,每个 Stage 的蝶形运算涉及跨越越来越大的内存间距的数据访问。对于
优化方法之一是将列数据加载到 groupshared 共享内存中,让所有
4.4 Pass 4 — 生成最终纹理
IFFT 结果经 PostProcess 后,得到空间域的:
| 输出纹理通道 | 含义 |
|---|---|
rgb.x |
高度偏移 |
rgb.y |
|
rgb.z |
|
rgb.w |
用于法线/Jacobian 计算的梯度数据 |
法线向量直接由偏导数纹理构造(无需额外 Pass):
5. 白沫仿真:Jacobian 行列式
本节是全文重点。 白沫(Whitecaps)是海浪真实感的核心要素之一,其产生机制与波浪的**非线性卷曲(Overturning)**密切相关,而 Jacobian 行列式正是量化这一现象的数学工具。
5.1 物理直觉:什么时候产生白沫?
将海面视为一个随时间演化的变形映射:每个水粒子从初始位置
其中
这个映射的雅可比行列式(Jacobian Determinant)描述了局部面积的变形程度:
展开为二维显式形式:
5.2 Jacobian 的物理意义
| Jacobian 值 | 物理解释 | 视觉表现 |
|---|---|---|
| 局部面积拉伸,水面平坦 | 正常海面 | |
| 无形变,等积映射 | 正常海面 | |
| 局部压缩,波峰聚拢 | 波峰收紧 | |
| 完全折叠,奇点 | 波峰恰好卷曲 | |
| 映射翻转,”穿透”发生 | 物理上不可能,意味着已应卷曲产生白沫 |
阈值与网格分辨率的敏感性
白沫阈值
- 低分辨率级联(如 Cascade 3,覆盖 2560m / 64 格):每格代表约 40m,高频导数信息丢失严重,
的数值偏差较大,同样的 可能几乎不产生白沫。 - 高分辨率级联(如 Cascade 0,覆盖 40m / 256 格):导数精度高,
的区域会准确对应波峰, 效果更符合物理预期。
工程建议: 将
5.3 频域中计算偏导数
梯度偏导数
因此,Jacobian 所需的四个偏导数纹理可以在 Pass 2 的同一批 IFFT 中一并计算,无需额外的 Pass。
5.4 持久泡沫模型
真实海浪的白沫并不瞬间消失,而是有一个衰减过程(持续数秒)。在 FoamSimulation.compute 中实现了经典的衰减-积累模型:
📝 FoamSimulation.compute — Jacobian 白沫内核
1 | |
5.5 白沫在着色器中的使用
泡沫纹理在海面 Fragment/Surface Shader 中与基础 PBR 材质进行混合:
1 | |
6. 渲染着色概要
本节仅对非 Jacobian 部分做概要梳理,重点在于工程落地的接口。
6.1 网格与级联
从 Compute Shader 输出的位移图需要驱动海面网格顶点。对于大范围海洋,通常采用多级联(Multi-Cascade)叠加:
| 级联 | 覆盖范围 | 主要贡献 |
|---|---|---|
| Cascade 0 | 10~40m | 毛刺细波(Ripples) |
| Cascade 1 | 100~160m | 中等风浪 |
| Cascade 2 | 400~640m | 长周期涌浪 |
| Cascade 3 | 1000~2560m | 宏观涌浪背景 |
各级联的覆盖范围由 OceanSimulationSettings.CalculateCascadeDomains 自动计算(Auto 模式),也可手动配置。渲染时按视距权重混合多级联的位移与法线。
6.2 PBR 水面材质要点
镜面反射与粗糙度
水面属于高度光滑的电介质(Dielectric),基础 IOR ≈ 1.33,F0 ≈ 0.02。
- 各向同性 GGX 微表面 BRDF(对于平静水面,粗糙度极低,接近镜面)
- 菲涅尔效应极强:掠射角(Grazing Angle)下接近全反射
- 高频法线叠加 Detail Normal Map 弥补 IFFT 分辨率上限
次表面散射(SSS)近似
海水具有蓝绿色散射,波峰处透光感显著:
- 使用基于深度的颜色衰减(Beer-Lambert 近似)
- 在背向光源的浅水区增加散射项,模拟波峰透光
- 对波峰应用额外的加法散射项(
)
7. FFT vs SWE:技术路线对比与适用场景
两种技术并非竞争关系,而是互补的——大型项目中往往将两者结合:深海用 FFT,近岸/交互区域用 SWE 或距离场碰撞。
| 维度 | FFT 海洋 | SWE 浅水方程 |
|---|---|---|
| 物理模型 | 线性波叠加(频谱统计) | 非线性偏微分方程 |
| 适用场景 | 开阔深水、无边界大洋 | 近岸浅水、池塘、河流 |
| 随机性 | 高(统计驱动) | 低(需额外扰动) |
| 物理交互 | ❌(无法与动态物体交互) | ✅(船只尾迹、角色涉水) |
| 波浪折射/衍射 | ❌ | ✅ |
| GPU 开销 | 中(主要在 IFFT) | 高(迭代求解 PDE) |
| 无限平铺 | ✅(天然可平铺) | ❌(固定域大小) |
| 代表实现 | Sea of Thieves, AC: Black Flag | GTA V 港口水域, 《荒野之息》河流 |
在 3A 项目中如何结合?
典型方案是以 FFT 渲染全局海面背景,在玩家周围约 50~200 米的交互半径内叠加一个低分辨率 SWE(或 iWave)模拟域:
- FFT 提供宏观波形的高度偏移;
- SWE 叠加局部交互扰动(船尾波浪、角色溅水、抛投物落水);
- 两者在视图空间以距离权重混合,过渡区域足够平滑。
此外,对于海岸线附近,还可以引入基于 SDF(Signed Distance Field)的水-地碰撞,配合波浪衰减曲线模拟近岸波浪冲刷效果。
8. 性能优化与总结
8.1 分辨率与级联数选取
| 场景 | 推荐配置 | 内存估算 |
|---|---|---|
| 移动端 | 64×64 / 2 级联 | ~2MB |
| PC(中配) | 128×128 / 3 级联 | ~12MB |
| PC/主机(高配) | 256×256 / 4 级联 | ~50MB |
| 影视级 | 512×512 / 4 级联 | ~200MB |
8.2 Thread Group 划分原则
[numthreads(8, 8, 1)] 对 [numthreads(16, 16, 1)] 以减少 dispatch 次数,但需注意 LDS(groupshared)的 occupancy 压力。
8.3 GPU Profiling:各 Pass 耗时参考
数据说明
以下耗时数据来自在 NVIDIA RTX 3060 Ti / Unity 2022 LTS + URP 环境下,对 Ocean-URP 进行 GPU Frame Capture(RenderDoc + Unity Frame Debugger)的实测参考值,配置为 256×256 / 4 级联,开启泡沫模拟。不同显卡、驱动版本和分辨率下数值会有差异,供量级参考。
| Pass | Compute Kernel | GPU 耗时(参考) | 瓶颈类型 |
|---|---|---|---|
| Pass 1 InitialSpectrum | CalculateInitialSpectrum × 4 cascade |
~0.08 ms | ALU Bound(exp/sqrt 密集) |
| Pass 2 TimeSpectrum | CalculateAmplitudes × 4 |
~0.06 ms | ALU Bound(sincos × N²) |
| Pass 3 IFFT(x 轴) | Fft × log₂256 × 4 = 32 dispatches |
~0.22 ms | Bandwidth Bound(大跨度读写) |
| Pass 3 Permute + IFFT(z 轴) | Permute + Fft × 32 |
~0.19 ms | Bandwidth Bound |
| Pass 4 FoamSimulation | Simulate × 4 |
~0.07 ms | ALU Bound |
| 总计(每帧) | — | ~0.62 ms | 以 IFFT 带宽为主 |
Pass 1 为条件执行,仅在参数变更时运行,不计入每帧开销。Pass 3 中 Permute(矩阵转置)虽然 ALU 量极低,但因访问模式随机,容易成为缓存效率瓶颈,建议在分析时单独关注其 L2 Cache Hit Rate。
带宽瓶颈 vs ALU 瓶颈的分析方式:
在 NVIDIA Nsight / AMD Radeon GPU Profiler 中,判断瓶颈的两个关键指标:
- Achieved Bandwidth / Peak Bandwidth:若比值 > 70%,为带宽瓶颈(Bandwidth Bound)。IFFT 的 Pass 3 通常在此列,原因是蝶形算法越到后期 Stage 访问跨度越大,严重破坏 L1 的 Warp 内数据局部性。
- ALU Utilization / SM Utilization:若比值 > 80%,为计算瓶颈(ALU Bound)。初始频谱 Pass 1 的
exp()、atan2()、sqrt()密集调用会导致此情况。
应对策略:Butterfly Texture 预计算(将 Pass 1 的旋转因子
8.4 关键优化点回顾
- Stockham FFT:双缓冲消除 bank conflict,比 Cooley-Tukey 原地版本在 GPU 上通常快 20~40%。
- Butterfly Texture 预计算:将旋转因子烘焙为纹理,避免每个蝶形单元的
exp()运算(GPU 上exp()约 20~30 个 ALU cycle)。 - 频域批处理:高度、位移、法线、Jacobian 所需的所有频域数据在同一 Pass 中计算,仅需一次 IFFT 管线完成全部输出。
- 泡沫按需开关:泡沫模拟约增加 15~20% GPU 开销,在移动端或性能受限场景可完全关闭。
- 异步回读(AsyncGPUReadback):CPU 侧仅在需要浮力物体交互时读取 1
2 个最低频级联的位移数据,延迟 12 帧,不影响渲染帧率。
8.5 总结
FFT 海洋模拟经历了二十余年的演进,从 Tessendorf 2001 的奠基性论文,到现代 GPU Compute Shader 的高效实现,其核心思想始终如一:
在频域中用物理统计模型定义能量,通过 IFFT 在空间域实例化随机海面,用 Jacobian 感知波浪破碎。
与其说这是一个”海洋渲染”问题,不如说是统计物理学、信号处理与 GPU 并行计算的交叉产物。
Tessendorf, Jerry. “Simulating Ocean Water.” SIGGRAPH 2001 Course Notes #9: Simulating Nature: Realistic and Interactive Techniques. ACM, 2001. — FFT 海洋模拟领域的奠基性论文。
Horvath, Christopher J. “Empirical Directional Wave Spectra for Computer Graphics.” ACM SIGGRAPH 2015 Talks. — JONSWAP 频谱与 Donelan-Banner 方向扩散函数在图形学中的应用综述。
Elfouhaily, T., Chapron, B., Katsaros, K., Vandemark, D. “A Unified Directional Spectrum for Long and Short Wind-Driven Waves.” Journal of Geophysical Research, 1997. — 统一方向频谱(Unified Spectrum)的权威参考。
Ang, Nigel, et al. “The Technical Art of Sea of Thieves.” ACM SIGGRAPH 2018 Talks. — 工业级 FFT 海洋在 3A 游戏中的实际落地案例。
Gasgiant. Ocean-URP. GitHub, 2021–2024. https://github.com/gasgiant/Ocean-URP — 本文工程实现的主要参考,完整的 Unity URP FFT 海洋开源实现。
2Retr0. GodotOceanWaves. GitHub, 2022–2024. https://github.com/2Retr0/GodotOceanWaves — 基于 Godot 4 Compute Shader 的 FFT 海洋实现,GLSL 版本可作为 HLSL 的参照。
Cooley, James W., and John W. Tukey. “An Algorithm for the Machine Calculation of Complex Fourier Series.” Mathematics of Computation 19.90 (1965): 297–301. — Cooley-Tukey FFT 算法的原始论文。
Stockham, T.G. “High-speed convolution and correlation.” AFIPS ‘66 Spring Joint Computer Conference, 1966. — Stockham FFT(无冲突变体)的原始来源。
知乎专栏:FFT 海洋模拟学习笔记(含 Unity 实现) https://zhuanlan.zhihu.com/p/347091298 — FFT 海洋实现参考。