四、水体渲染的波形模拟技术
按照流派进行分类,可将上文总结的水体渲染波形模拟的二十三种方法分为如下几类:
1. 线性波形叠加方法
- 正弦波(Sinusoids Wave)[Max 1981]
- Gerstner 波 (Gerstner Wave) [Fournier 1986]
2. 统计模型方法
- 快速傅立叶变换(Fast Fourier Transform)[Mastin 1987]
- 空间-频谱混合方法(Spatial -Spectral Approaches)[Thon 2000]
3. 波动粒子方法
- 波动粒子方法((Wave Particles) [Yuksel 2007]
- 水波小包方法(Water Wave Packets)[Jeschke 2017]
- 水面小波方法(Water Surface Wavelets)[Jeschke 2018]
4. 基于物理的方法
- 欧拉方法(Eulerian approaches)[Kass 1990]
- 拉格朗日方法(Lagrangian approaches) [Stam 1995]
- 欧拉-拉格朗日混合方法(Hybrid approaches)[Brien 1995]
5. 预渲染方法
- 顶点高度位移贴图(Vertex Height Map Displacement) [Yuri 2005]
- 流型图(Flow Map)[Vlachos 2010]
- 离线FFT贴图烘焙(Offline FFT Texture) [Torres 2012]
- 离线流体帧动画烘焙(bake to flipbook)[Bowles 2017]
6. 其他方法
- 凹凸纹理贴图(Bump Mapping)[Schachters 1980]
- 分形噪声(Fractal Noise)[Perlin 1985]
- 分形布朗运动(Fractal Brownian Motion,FBM)[Addison 1996]
- 程序化形状(Procedural Shape)[Ebert 1999]
- 基于体素的方法(Voxel-Based Solutions) [Yann 2003]
- 二维波动方程(2D Wave Equation)[Nishidate 2005]
- 屏幕空间网格(Screen Space Mesh)[Muller 2007]
- 浅水波浪模拟(Water Wave Simulation)[Grenier 2018]
下面将对其中比较常见的方案进行盘点。
4.1 线性波形叠加方法
线性波形叠加方法的主要思路是累加不同的线性波形函数以构造波浪表面。可以将其理解为波动现象在深水中引起水颗粒运动的一种解析解。
业界主流的波形函数主要分为正弦波(Sinusoids Wave)和Gerstner波(Gerstner Wave)两种,下面分别进行说明。
4.1.1 正弦波(Sinusoids Wave)[Max 1981]
作为比较早期的水面波形模拟方案,正弦波(Sinusoids Wave)的特点是平滑,圆润,适合表达如池塘一样平静的水面。
图 Unity下实现的基于正弦波(Sinusoids Wave)的水体
1981年,Max[Max 1981]首先提出了采用高低振幅的正弦波曲线的序列组合来模拟水面起伏的想法。将水体表面采用高度进行建模,则基于正弦波(Sinusoids Wave)的方法在时间t的每个点(x,z)上计算的高度y = h(x,z,t)的通用公式为:
- 其中
是波的总数
-
是第i个波的振幅
- 是波矢量
-
是其脉冲值(pulsation)
-
是自由表面的高度
正弦波(Sinusoids Wave)目前在水体渲染领域已经很少直接使用,业界往往青睐于使用它的进化版Gerstner波。
4.1.2 Gerstner 波(Gerstner Wave) [Fournier 1986]
作为正弦波的进化版,Gerstner 波(Gerstner Wave)的特点是波峰尖锐,波谷宽阔,适合模拟海洋等较粗犷的水面。
图 Unity下实现的基于Gerstner波的水体
Gerstner 波(Gerstner Wave)也常被称为Trochoidal Wave,在流体动力学中,其为周期表面重力波(periodic surface gravity waves)的欧拉方程的精确解,由Gerstner在1802 年初次发现,并在1863年由Ranine独立重新发现。在1986年由Fournier等人引入水体渲染领域。
图 Gerstner Waves波形
图 Gerstner Waves水颗粒的运动轨迹为圆形
选择一组波矢量ki,振幅Ai,频率ωi和相位φi,对于
,Gerstner Waves的通用公式为:
Gerstner Waves由于计算量可控,性价比高,在游戏水体渲染领域的应用较为广泛。不少3A游戏了采用Gerstner Wave作为水体渲染的基础实现。如下文Wave Particles部分也会提到的《神秘海域3》,即是采用了Gerstner Wave + Wave Particles的水体渲染方案组合。
4.2 统计模型方法
4.2.1 快速傅立叶变换(Fast Fourier Transform , FFT)[Mastin 1987]
作为目前电影业界广泛采用的海洋表面渲染解决方案,基于快速傅立叶变换(Fast Fourier Transform , FFT)的水体渲染方法的特点是真实感出色,全局可控,细节丰富,但计算量相对较大。
自1986年[Mastin 1987]将基于FFT的水体渲染方法引入水体渲染领域,以及2001年Tessendorf着名的水体总结文章《Simulating Ocean Water》[Tessendorf 2001]对其的推广之后,至今其仍然是电影工业模拟海洋表面的标准解决方案。
图 Unity下实现的基于FFT的水体
傅里叶变换(Fourier transform)是一种线性积分变换,用于信号在时域和频域之间的变换。而快速傅里叶变换 (Fast Fourier Transform,FFT), 是一种可在O(nlogn)时间内完成离散傅里叶变换(Discrete Fourier transform,DFT)的高效、快速计算方法集的统称。最初的快速傅里叶变换方法早在1805年就已由高斯推导出来,并于1965年由Cooley和Tukey重新提出,并渐渐被大众所熟知。从此,对快速傅里叶变换(FFT)算法的研究便不断深入,数字信号处理这门新兴学科也随FFT的出现和发展而迅速发展。根据对序列分解与选取方法的不同而产生了FFT的多种算法。
FFT的基本思想是把原始的N点序列,依次分解成一系列的短序列。充分利用DFT计算式中指数因子 所具有的对称性质和周期性质,进而求出这些短序列相应的DFT并进行适当组合,达到删除重复计算,减少乘法运算和简化结构的目的。
关于FFT的算法细节这里就不展开讲了,放两张经典的总结图:
基于FFT的水体渲染方法,也常被各类文献称为基于频谱(spectrum-based)的方法,其核心思想是基于FFT构造出水体的表面高度。具体而言,该方法使用从理论或测量统计数据获得的海浪频谱(最常见的频谱为[Tessendorf 2001]使用的Phillips频谱)来描述海洋表面,结合大量的正弦波的叠加在频域中生成波型的分布,然后执行逆FFT,将数据转到空间域,经过运算生成位移贴图(displacement map)。最终,由位移贴图导出表面法线贴图,以及其他相关数据,如代表白沫区域的Folding Map。
图 基于FFT的水体渲染流程 [NVIDIA 2004]
采用FFT的水体渲染方法从90年代开始广泛用于电影制作(离线渲染),从2000年代开始广泛用于游戏(实时渲染)。离线渲染和实时渲染的选择,主要在于当时硬件计算能力可以承受多少分辨率的高度图的实时运算。早期的游戏,如Crysis,由于硬件计算量的限制,采用了64 x 64的高度图分辨率。而由于硬件的发展,目前512 x 512的分辨率的计算量已经在实时渲染中较为普遍。而电影工业中采用FFT生成的高度图,由于可以采用离线渲染,以及品质的要求,分辨率一般较大,如早在1997年的《泰坦尼克号》的海洋渲染的渲染,就已经采用了2048 x 2048分辨率的高度图。
图 着名电影《泰坦尼克号》中,基于FFT方法离线渲染的海洋,采用2048 x 2048分辨率的高度图
4.3 波动粒子方法
4.3.1 波动粒子(Wave Particle) [2007]
波动粒子(Wave Particle)方法最初由Yuksel于2007年[Yuksel 2007]提出,该方法的核心思想是采用粒子代表每一个水波,并允许波反射以及与动态对象的相互作用。这种方法将动态模拟三维水波的复杂度降维到模拟在平面上运动的粒子系统的级别。
波动粒子(Wave Particle)方法结合了线性叠加方法的灵活性和基于FFT方法的稳定性和视觉细节,其可控制性和性能,以及出色的交互表现,是模拟实时水体交互的不错方案。
图 Wave Particle (图片来自[Yuksel 2007])
需要注意的是,波动粒子(Wave particles)的作用不只是代表波浪的位置。它们还可以带有描述其形状和行为的其他属性,例如振幅(amplitude)和半径(radius)。
当相邻波动粒子之间的距离大于半径的一半时,该方法会将一个波动粒子转换为三个新的波动粒子。新的波动粒子直接从现有波动粒子中吸收能量(即振幅)。从而减小了现有波动粒子的振幅,而整体波峰的总振幅保持不变。三个子粒子的振幅和扩散角度变为父粒子的三分之一,而新粒子的半径与父波动粒子保持相同。如下图所示。
图 (a)和(b)分别是波动粒子的初始位置和它们形成的波峰(c)和(d)是波粒经过一定距离后的位置和波动粒子形状(图片来自[Yuksel 2010])
因为在波动粒子系统中,假设每个波动粒子在两侧都具有两个相同的相邻粒子,所以当一个波动粒子细分时,其会在两侧产生两个新的波动粒子,如下图所示。
图 具有相同扩散角度的两个相邻波动粒子之间的距离(图片来自[Yuksel 2010])
另外,Wave Particles方法还可以与现有各种方案进行结合和改进。
2007年Yuksel提出的原版Wave Particles的波动粒子的生成源来自点状的粒子波源。对此,《神秘海域3》对其进行了改进方案。在《神秘海域3》中,并没有使用点状粒子波源,而是在环形区域中放置随机分布的粒子源,以近似开放水域的混沌运动,从而产生一个可平铺的向量位移场(vector displacement field)。
图 《神秘海域3》中基于随机分布wave particles粒子源的波浪模拟方法 [SIGGRPAPH 2016]
《神秘海域4》中则采用了多分辨率Wave Particles方案,从另一个角度对Wave Particles方法进行了改进。
图 《神秘海域4》中的多分辨率Wave Particles [SIGGRPAPH 2016]
4.3.2 水波小包方法(Water Wave Packets)[SIGGRAPH 2017]
在波动粒子(Wave Particles)的基础上, Jeschke和Wojtan[2017]于SIGGRAPH 2017引入了以理论群速度(theoretical group speed)传播的水波小包(Water wave packets )方法。该方法继承了基于频谱的方法的优点,如数值稳定性和理论上准确的波速。同时,他们通过将全局余弦波分解成一系列更短的波分量,从而避免了基于频谱的方法的复杂性。
图 水波小包方法(Water Wave Packets)的paper demo [SIGGRPAPH 2017]
图 水波小包方法(Water Wave Packets)的原理图示
图 水波小包方法(Water Wave Packets)渲染效果图
4.3.3 水面小波方法(Water Surface Wavelets) [SIGGRAPH 2018]
随后的[SIGGRAPH 2018],Jeschke等人对Water Wave Packets进行了改进,提出了新的水面小波方法(Water Surface Wavelets)。水面小波方法(Water Surface Wavelets)基于欧拉方法,自由度与空间区域有关,与波动本身无关。因此,该方法可以和GPU更好的结合,因为计算复杂度是恒定的,因为不随粒子的数量而变化。不过该方法由于提出时间较新,性能也没有太大优势,所以目前还没有听说有实际的实时渲染项目在使用。
图 Water Surface Wavelets paper Demo [SIGGRPAPH 2018]
图 水面小波方法(Water Surface Wavelets)渲染效果图 [SIGGRPAPH 2018]
4.4 基于物理的方法
基于物理的水体模拟方法一般比较昂贵,由于可以离线渲染,所以在电影工业中具有很好的运用。由于现阶段很难用于实时渲染,这边仅进行一个综述性的总结。
基于物理模型的水体模拟方法的核心是对Navier-Stokes方程(Navier-Stokes Equations,NS方程)进行求解。Navier-Stokes方程是一组描述液体和空气之类的流体物质的方程,描述作用于液体任意给定区域的力的动态平衡。除了水体模拟之外,其还可以用于模拟天气,水流,气流,恒星的运动。
Navier-Stokes方程如下:
- 为流体速度矢量(fluid velocity vector)
- 为流体压力(fluid pressure)
- 为流体密度(fluid density)
- 为运动粘度系数(kinematic viscosity coefficient)
- 为梯度微分(gradient differential)
- 为拉普拉斯算子(Laplacian operators)
常用的求解Navier-Stokes方程的方法有欧拉方法(Eularian Method)和拉格朗日方法(Lagrangian Method)两种:
- 欧拉方法(Eularian Method)是一种基于网格的方法。它从研究流体所占据的空间中各个固定点处的运动着手,分析被运动流体所充满的空间中每一个固定点上流体的速度、压强、密度等参数随时间的变化,以及由某一空间点转到另一空间点时这些参数的变化。
- 拉格朗日方法(Lagrangian Method)是一种基于粒子的方法。它从分析流体各个微粒的运动着手,即研究流体中某一指定微粒的速度、压强、密度等参数随时间的变化,以及研究由一个流体微粒转到其他流体微粒时参数的变化,以此来研究整个流体的运动。最常用的拉格朗日方法是光滑粒子流体力学(Smoothed Particle Hydrodynamics,SPH)方法,其核心渲染思想为流体模拟产生粒子,然后多边形化粒子以产生波。
图 基于SPH方法的水体渲染表现
图 Houdini下的流体模拟
除了独立的两种方法之外,还有结合两者的欧拉-拉格朗日混合方法(Eularian-Lagrangian Hybrid approaches),其主要思想是使用欧拉方法来模拟流体的主体,并使用拉格朗日方法来模拟诸如泡沫,喷雾或气泡之类的细小细节。
FX Guide上有一篇关于电影业界使用流体模拟方法的不错文章,感兴趣的朋友可以了解一下:https://www.fxguide.com/fxfeatured/the-science-of-fluid-sims/
另外,也可以采用bake to flipbook方法,将离线的流体模拟,烘焙成flipbook帧动画,用于实时渲染。
4.5 预渲染方法
4.5.1 流型图(Flow Map)
流型图(Flow Map),也常被称为矢量场图(Vector Field Map),本质上是一种基于矢量场平移法线贴图的着色技术,或者可以理解为一种UV动画,由VALVE的Vlachos在SIGGRAPH 2010上的talk《Water Flow in Portal 2》被大众所熟知。值得一提的是,VALVE在2010年就超前地使用了Houdini来制作flow map。
图 原版Flow Map的算法原理 [SIGGRAPH 2010]
图 基于Houdini的Flow Map创作工作流 [SIGGRAPH 2010]
Flow Map除了用于水体的渲染以外,任何和流动相关的效果都可以采用Flow Map,如沙流,以及云的运动等效果。
图 基于Flow Map的水体渲染 @Unity
图 基于Flow Map的水体渲染 @UDK
Flow Map的核心思想是预烘焙2D方向信息到纹理,以在运行时基于UV采样,对流动感进行模拟。
图 基于Flow Map的形变
Flow Map的典型使用代码如下所示:
- //get and uncompress the flow vector for this pixel
- float2 flowmap = tex2D( FlowMapS, tex0 ).rg * 2.0f - 1.0f;
- float cycleOffset = tex2D( NoiseMapS, tex0 ).r;
- float phase0 = cycleOffset * .5f + FlowMapOffset0;
- float phase1 = cycleOffset * .5f + FlowMapOffset1;
- // Sample normal map.
- float3 normalT0 = tex2D(WaveMapS0, ( tex0 * TexScale ) + flowmap * phase0 );
- float3 normalT1 = tex2D(WaveMapS1, ( tex0 * TexScale ) + flowmap * phase1 );
- float flowLerp = ( abs( HalfCycle - FlowMapOffset0 ) / HalfCycle );
- float3 offset = lerp( normalT0, normalT1, flowLerp );
复制代码
4.5.1.1 Flow Map变体:《神秘海域3》Flow Map + Displacement
另外,Flow Map可以和其他渲染技术结合使用,比如《神秘海域3》中的Flow Map + Displacement:
4.5.1.2 Flow Map变体:《堡垒之夜》Flow Map + Distance Fields + Normal Maps
以及《堡垒之夜》中的Flow Map + Distance Fields + Normal Maps [GDC 2019, Technical Artist Bootcamp Distance Fields and Shader Simulation Tricks]
4.5.1.3 Flow Map变体:《神秘海域4》Flow Map + Wave Particles
或者《神秘海域4》中的Flow Map + Wave Particles[SIGGRAPH 2016, Rendering Rapids in Uncharted 4],都是进阶模拟水体表面流动与起伏效果的不错选择。
图 《神秘海域4》中的Flow Map + Wave Particles。通过将flow map与波浪粒子(wave particle grids)网格配合使用,可以非常精确地控制波浪的方向。在此示例中,采用一个循环的flow map来构成漩涡
如下为《神秘海域4》中Flow + Wave Particles的伪代码:
- timeInt = time / (2.0 * interval)
- float2 fTime = frac(float2(timeInt, timeInt * .5)
- posA = pos.xz - (flowDir/2) * fTime.x * flowDir
- posB = pos.xz - (flowDir/2) * fTime.y * flowDir
- gridA0 = waveParticles(posA.xz, freq0,scale0)
- gridA1 = waveParticles(posA.xz, freq1,scale1)
- …
- gridB0 = waveParticles(posB.xz, freq2,scale0)
- gridB1 = waveParticles(posB.xz, freq3,scale1)
- …
- float3 pos0 = gridA0 + gridA1 + gridA2 + gridA3
- float3 pos1 = gridB0 + gridB1 + gridB2 + gridB3
- pos = blend(pos0, pos1, abs((2*frac(timeInt)-1)))
- pos.y += heightMap.eval(uv)
复制代码
4.5.2 离线FFT贴图烘焙(Offline FFT Texture)
离线FFT烘焙(Offline FFT Texture)方法最初由《刺客信条3》团队开始采用[Torres 2012]而进入大众视野,思路为基于离线FFT预渲染出一系列高度图,烘焙得出一系列法线贴图或矢量位移贴图(vector displacement maps),并在运行时进行采样。FFT的周期性质可以让烘焙得出的贴图非常适合做tiling。
图 基于离线FFT烘焙的《刺客信条4》的水面表现
图 基于离线FFT烘焙的《刺客信条3》的水面表现
4.6 其他方法
除了上述5大类方法之外,还有一些其他的常见水体渲染方法,可以总结如下:
- 凹凸纹理贴图(Bump Mapping)[Schachters 1980]
- 分形布朗运动(Fractal Brownian Motion,FBM)[Addison 1996]
- 程序形状(Procedural Shape)[Ebert 1999]
- 分形噪声(Fractal Noise)[Gonzato 2000]
- 基于体素的方法(Voxel-Based Solutions) [Yann 2003]
- 屏幕空间方法(Screen Space Mesh)[Muller 2007]
- 矢量位移贴图(Vector Displacement Map)[2009]
其中,凹凸纹理贴图(Bump Mapping)比较早期的水体模拟方案,主要思想是扰动参与光照计算的法向量,并通过凹凸纹理的连续移动来模拟海浪的随机运动。目前凹凸纹理贴图几乎是实时水体渲染的必备贴图之一。
而分形噪声(Fractal Noise)方法核心思想是基于不同频率Perlin噪声的叠加,混合出分形噪声,以构建海面高度场。
矢量位移贴图(Vector Displacement Map)的核心思想则是使用空间中的向量的颜色通道在方向与大小上置换对应几何体的顶点。
图 一个标准的矢量位移贴图(Vector Displacement Map) @Arnold Render
图 基于矢量位移贴图(Vector Displacement Map)的水体渲染 @Arnold Render
其他的方案相对而言比较小众,都有对应paper,篇幅原因这里就不展开总结了。
这边放一个令人印象深刻的SIGGRAPH 2017上 ,Crest Ocean System基于动态程序化形状(Procedural Shape)的水体渲染视频,可以允许玩家和海洋进行互动:
基于程序化形状的水体渲染
|