【Siggraph 2019】Multi-Resolution Water Rendering in Crest

照例对方案的要点做一个总结:

  1. 方案是面向PC&主机设计的,性能在PC上都算高的(全部特性打开,6.6ms)
  2. 采用多个cascade构成clipmap、lod来覆盖广阔的区域,同时保障近景的精度
  3. 波形实现用的是Gerstner Wave的方案,通过多个Gerstner Wave的叠加来实现海洋波形的模拟,同时通过cascade的作用来降低叠加wave过多时的消耗
  4. 设计了一套通用的波形输入框架,支持各种波形输入的效果叠加:将波形的输入剥离出来,并设计了相应的通用格式,方便实现各种输入数据的叠加
  5. 参考盗贼之海的shading逻辑,基于wave的波长来调节shading的效果,以实现兼顾近景远景的次表面散射效果的模拟
  6. 在前面的数据输入框架中,叠加了flowmap,并针对叠加后的问题做了处理,做到了小尺寸、大尺寸波浪与flowmap数据的兼容,基于这个方案可以实现海面上的漩涡效果
幻灯片6.JPG

17年分享过后,有几个项目开始接入,作者跟另一位兄弟开始把业余时间都投入到这个里面,经过产品的使用与反馈,对项目的效果做了进一步的完善,这次把一些新的进展给大家做一个分享(依然保持开源,这也是一条非常诱人的路啊)。

这里需要强调的一点是,方案目前只是面向PC跟主机。

幻灯片7.JPG

适配到了多个平台,也适配了不同的风格。

幻灯片8.JPG

最后5张是为LWRP管线设计的一个demo场景的,下面有个视频。

幻灯片9.JPG
幻灯片10.JPG

这里给出了文章大纲:

  1. 先对业界的方案做一个比对
  2. 再来介绍一下本次分享的重点,multi-scale & multi-resolution框架,基于这个框架可以根据视角来实现不同的计算LOD,从而可以很好的保障近景的精度与足够的渲染范围。
  3. 接着来介绍上面的框架的一些优点
  4. 再接着17年分享的着色逻辑,谈谈19年的进展
  5. 最后看看性能以及未来的工作
幻灯片11.JPG

这里给出了一个场景的效果,以及与之相关的水体绘制所需要的输入贴图数据,可以看到,贴图数据总共有五类(每类对应于一列),同类贴图有多张,从上到下分别对应的是覆盖不同范围(相邻翻倍)的数据,从左往右分别是:

  1. water depth,水体深度贴图,黑色表示0,通常是陆地海拔过高,其实也可以看成是某种clamp版本的场景高度图
  2. water flow,水体流向图
  3. displacement,水体形状所需要的贴图
  4. foam贴图,基于displacement计算得到
  5. shadow,通过jitter采样可以实现水体的软影、硬影效果

这些图都是运行时计算的,可以支持各种动态效果

幻灯片17.JPG

接下来看看前人工作,这里不可能囊括所有的方案,只会介绍一些最近成功落地的

波形是水体效果表现优劣的关键,这里对前人的方案做了分类,分别是动画波形与动态模拟波形方案。

动画方案:

  1. 无状态,预测方便,可以很方便的实现网络同步
  2. 比如可以通过多个波形(如gerstner wave)的叠加来给出相对真实的表现,但是这个叠加是有限制的,数目过多导致性能问题,数目过少效果又不真实,神海3的做法是将Gerstner wave跟wave particle结合,前者给出大体轮廓,后者给出高频细节,从而实现两者的兼顾(神海4则抛弃了Gerstner wave,直接用了多种不同频率的wave particle叠加来实现)
  3. 另一种方案则是FFT,借助不同频率的FFT的叠加(分别覆盖不同范围),可以很好的实现高低频信号的兼顾,具有不错的真实感

模拟方案:

  1. 运行时模拟得到波形结果,天然支持与环境的动态交互效果
  2. 因为无法tilable,所以随着范围的扩大,性能下降的很快,暂时无法广泛应用
  3. 这里给了两种代表性的方案(UIWS的涟漪模拟跟浅水方程SWE)
  4. 关于这块的分析,Jeshke2018的工作做了比较细致的整理

两种方法各有优劣,业界常用的做法是将两者进行结合,动画方案给出大体形状,模拟方案实现交互细节

回到Crest的方案,Crest提供了一套可伸缩的框架:

  1. 支持对多种波形数据进行快速累加
  2. 支持一个相对大范围的动态模拟

目前方案中,大体波形是通过对多个Gerstner Wave叠加完成的,理论上也可以换成多个FFT叠加,而细节模拟则是通过对Ripple Simulation方案模拟得到,理论上也可以换成SWE。

幻灯片19.JPG

接下来看看波形数据是如何使用的:

  1. 盗贼之海是直接对FFT贴图进行采样来实现波形的模拟
  2. 刺客信条2012则是将数据写入到一个屏幕空间buffer(projected grid),这种方案天然支持LOD
  3. Crest则是将多套不同LOD(相同分辨率,覆盖不同范围)贴图结合起来使用,与之类似的,Insomniac在Resistance 2中尝试将多套FFT(不同频率)叠加起来使用,其中奇数序列的贴图会旋转45度
  4. 神海4的做法则是以wave particle为基底,通过程序化生成的primitive来对波形效果进行雕刻,并添加一个局部的形变贴图用于驱动特效
幻灯片21.JPG

相比于此前的方案,当前方案主要是将数据抽取出来,做成了一套相对独立的框架,基于这个框架可以很方便的实现数据的添加删除与更新,以实现各种各样的效果。

幻灯片22.JPG

接下来就来看看这套框架的细节

幻灯片23.JPG

位移数据用于描述各个点的偏移,水面可以看成是一个平面的grid,基于位移贴图的偏移才形成了丰富的波形效果,因为是3D偏移,因此可以用一个颜色来表示。

幻灯片27.JPG

前面说了,水体的数据都用贴图来表示,同一种类的贴图具有相同的分辨率,覆盖不同的范围,这里可以用一个texturearray来表示,目前的设计中,每张贴图分辨率为512.

每一张贴图覆盖范围都是前一张贴图的两倍,8张贴图在当前的设计中,最大覆盖范围为8公里(以角色或相机为中心)

左下角的图给了一个debug view,为了避免两级之间过渡的跳变,这里还在接缝处做了blend。

这种设计的好处是,只需要增加一张贴图,就可以将覆盖范围扩大一倍,具有很好的伸缩性。

幻灯片28.JPG

跟17年介绍的细节一样,随着视角的抬升,mesh的精度会平滑的调整以覆盖更大的范围

幻灯片30.JPG

这里用一个简单的例子来描述框架是如何工作的。

假设开发者需要在水体上添加一个垂直方向的偏移,比如基于smoothstep函数实现,要怎么做呢?

那就是在场景添加一个quad,之后在shader中返回一个高度数据(计算逻辑写在shader中,比如实现smoothstep),并且将混合参数设置为叠加,之后通过某种方式将这个输出注册到系统中。

之后在运行的时候,系统就会整合各种来源的数据,按照一定的算法进行计算,输出波形效果。

除了波形,shading、foam也可以采用类似的逻辑实现。

基于这种方式,可以为各种不同的gameplay需要提供方便的接入手段。

幻灯片31.JPG

这里对方案做了个总结,简单来说就是可以适配无限大的范围,具备较好的伸缩性,对数据的输入做了抽象设计,允许各种各样的输入数据,而计算逻辑则是统一的。

幻灯片32.JPG

这里带有流向的大洞就可以通过上述框架很容易实现,其他的比如standing wave也可以通过同样的方式实现。

幻灯片33.JPG

接下来看看位移贴图实现的一些细节,进一步介绍框架的优点。

幻灯片34.JPG

在当前的框架里,支持用多种分辨率来完成水体的绘制。这里以Gerstner Wave举例,在运行时对多个Wave进行累加的时候,遭遇的一个严重问题就是性能问题,但是这个问题在当前框架的实现中将不存在。

这个效果不只是对于Gerstner Wave有效,FFT也一样

幻灯片35.JPG

Gerstner Wave:

  1. Analytical Wave
  2. 每个粒子都是圆形运动,靠经表面的半径大
幻灯片38.JPG

公式给出如上图所示:

  1. cos表示高度的偏移
  2. 水平的偏移用sin表达
幻灯片39.JPG

通过将多种具有不同振幅不同波长(相邻翻倍)的Gerstner Wave叠加起来以得到符合视觉效果的波形displacement数据。

幻灯片40.JPG
幻灯片41.JPG
幻灯片42.JPG
幻灯片43.JPG
幻灯片44.JPG

将相邻两个cascade的数据混合起来(高精度数据覆盖区域小,只与低精度区域的某个quad的数据混合)

幻灯片46.JPG
幻灯片47.JPG
幻灯片48.JPG
幻灯片49.JPG
幻灯片50.JPG

这里用几张图解释了高精度跟低精度混合后,会起到一定的细分(平滑)作用,并用了一条一维的曲线做了进一步的说明。

幻灯片51.JPG

这里演示了采样密度下降后波形随之变得平滑的效果

幻灯片52.JPG

性能相关:

  1. 将一些周边的逻辑从循环中移出来,采用SIMD,每次计算处理四个Gerstner Wave,每个Cascade通常包含8个Wave,也就只用循环两次即可
  2. 每个Cascade的贴图为512的,最终8个Cascade叠加了224个Wave,大概花费的时间给出如上(GTX 1070上耗费0.47ms)
幻灯片53.JPG

对displacement波形逻辑做一下总结:

  1. 采用的多分辨率方案不只是适用于Gerstner Wave,其他波形如FFT也同样适用,这种方案可以兼顾性能跟效果,既避免了密度过低带来的锯齿问题,也避免了密度过高导致性能消耗
  2. 最后的混合pass会使得结果变得平滑,这个虽然在效果上有一定的减损,但总体来说,可以看成是正优化
  3. 最后,还会通过一个异步pass将数据回读到CPU用于gameplay逻辑,后面也在考虑通过将这个过程放到GPU上来做进一步降低损耗。
幻灯片54.JPG

接下来看看动态波形模拟部分

幻灯片55.JPG

波形模拟想要实现上图所示的效果:

  1. 水面上的物件能够对水面的波形产生影响(涟漪、波纹等)
  2. 支持近景的交互效果跟远景的交互效果(远处游轮对水体的影响)
幻灯片56.JPG

这里来介绍一下基本思路:

  1. 模拟是发生在Height Field上的,作用的结果会叠加到前面的Displacement波形上
  2. 右侧的debug view给出了模拟的结果,红色、绿色分别表示高度以及垂直方向上的速度
  3. 通过compute shader完成模拟计算,在前面的多分辨率框架下,每个cascade会dispatch一个shader,彼此独立计算
  4. 最后在前面displacement的叠加pass中完成结果的应用,无需额外的计算pass
幻灯片57.JPG

扩散(Dispersion)是一种大尺寸波形传播速度快于小尺寸波形的现象,这个效果有助于增强水体表现的真实感。

默认的波形计算公式中,波形的传播速度是一个常量,这里只需要给每个cascade的波形添加一个变量来控制即可,得到的结果是每个cascade中的波形传播速度是相同的,对标的是该cascade中最小的波长的波形传播速度(虽然效果上有一些影响,但是在游戏项目中来说,是合适的)

在进入细节介绍之前,先来给出前人的实现方案:

  1. Canabal 2016等项目采用一个大尺寸的拉普拉斯kernel来得到相应效果,并通过一个mipmap金字塔结构来优化性能,但这个实现复杂度会偏高
  2. Day 2009采用频域模拟的方案实现,需要FFT跟IFFT计算,复杂度跟消耗都偏高
  3. Jeschke 2018则直接在一个大尺寸的分辨率上进行多次模拟,消耗就无法接受
幻灯片58.JPG

一些实现细节:

  1. 会基于当前cascade的scale以及CFL条件中的C来动态调整time step(作用是?)
  2. Wiki中对CFL有详细描述,简单来说就是一个wave每个timestep不能跨越一个grid的尺寸,上图给了CFL的计算公式(2D)
  3. 其中参数C可以用于描述计算的稳定性,这里用0.6来进行显式欧拉模拟
  4. C固定下来后,波形的传播速度跟grid的尺寸就取决于cascade了,基于上面的公式,我们就可以计算出模拟的timestep(也就是频率)
  5. 在这样的逻辑下,我们就可以天然支持LOD(远处cascade的计算频率就可以变低,在视角抬高后,波形的模拟频率也可以降低)
  6. 模拟同样是发生在8个cascade上,以512的分辨率进行,覆盖8km的范围,近景处计算频率要提高,大概需要3次模拟,在1070的设备上花费0.6ms
幻灯片59.JPG

基于上述计算后,我们只能得到波形垂直方向上的交互效果,缺少了水平方向上的扰动。

幻灯片60.JPG

从真实的角度来看,波形应该同时具备水平跟垂直两个方向上的移动才好

幻灯片66.JPG

Tessendorf的FFT方案中描述了如何基于Heightfield中计算displacement的方法,第一个公式给出了从频域转空域heightfield的计算逻辑:

  • \tilde{h}是频域数据
  • exp是不同频率的波形公式,其中k是波形的传播方向,这个向量的长度正好对应波形的波长的倒数

第二个公式给出了水体mesh的顶点在水平方向上的偏移计算方法。

第三个公式通过梯度算子(对第一个公式的x进行微分计算),得到了高度的空间微分项,跟前面第二个公式后面项相比,就差了一个k的倒数,正好是波长,这也是为啥大尺寸的波形容易生成大尺寸的displacement的原因。

上面的公式只是用来解释基本原理与特性,并不打算直接按照这个公式来计算,因为这里不想要走复杂的FFT计算。

幻灯片67.JPG

不过这里有一个问题,那就是目前模拟得到的wave是多种频率多种波长的波浪混合而成的,我们要怎么知道应该要乘以哪一种波浪的波长?

这里粗暴的采用了当前cascade中的多种波长的平均值,在Nyquist定律的限制下,平均波长导致的结果是displacement会被低估,也就是波形起伏没那么高,这个虽然看起来真实感稍有减弱,但至少不会出现异常的bug。

(Ottosson方案在这里做了带宽的约束,限制了最大的波长,本文方法还没有尝试)

幻灯片68.JPG

所以这里是为水平方向波形效果增加的一些计算逻辑:

  1. 按照此前介绍的模拟方法进行计算
  2. 在将计算结果整合到最终的波形数据之前,先计算出波形的空间微分
  3. 在空间微分上乘以平均波长以及一个美术同学控制的缩放因子
  4. 将上面一项结果应用到前面模拟的结果中,并叠加到波形上
幻灯片69.JPG

右边是添加了水平displacement的效果,恕我直言,除了shading部分(材质、foam)有所变化之外,其他地方并没有明显区别

幻灯片70.JPG

这里对波形方案的实现逻辑做了总结

幻灯片71.JPG

这里对Jeschke 2018的Water Surface Wavelets方案做一个补充说明:

  1. 这种方案将dynamics跟visual details解耦开来,可以用一个较低的分辨率来完成dynamics计算以降低计算成本,不过虽然如此,由于方案还是在单个grid上计算的,所以时间消耗的量级跟其他方法无区别
  2. 用一个量化的数据对比下,Jeschke方案的GPU消耗为12ms,而本文方案则为0.6ms(view dependent adaptive lod),此外,显存消耗也会跟高
  3. 在效果上,Jeschke方案支持了较多细节的展示,包括ambient wave撞到边界反弹的效果等
  4. 如果将Jeschke的方案集成到本文描述的多分辨率框架中,将可以很好的解决原文中消耗过高的问题。
幻灯片72.JPG

下面看下光照散射计算逻辑

幻灯片73.JPG

光照散射解决的是光在水体中传播并最终进入人眼的计算问题

幻灯片74.JPG

这里采取的计算方法与盗贼之海的类似,都是基于水平方向的displacement的长度来的。

为什么散射需要跟水平偏移的距离关联呢,如右图所示,按照Gerstner Wave的模拟,水面上的粒子在水平上的偏移即为其运动轨迹(圆形)的半径,而这个数值与波形波峰的高度、宽度有直接的关联,而这些参数都跟散射的计算有很大的关联。

为了将这套逻辑应用到前面提到的多分辨率计算框架中,这里对displacement做了一个归一化(除以波长),并用归一化的结果来计算散射(为啥?下面会介绍为啥不能直接用displacement)

幻灯片75.JPG

最开始的计算逻辑中,散射结果只与水面的高度(深度?)有关,这种逻辑下,每当波形条件变化,都需要重新调制参数(?),非常费劲。

幻灯片76.JPG

而如果直接用盗贼之海中基于displacement的散射计算,就会在远景处得到这样一种不真实的效果。

幻灯片77.JPG

但是如前所述,如果将displacement除以波长,问题就解决了(原因没解释)。

幻灯片78.JPG

最后看下流向效果的实现。

幻灯片79.JPG

流向是一种预定义的水体速度的特性,这里直接将flow设置为整体框架的一种输入数据,并基于这个输入数据来对水体的所有贴图进行处理,以得到更为统一的动态效果。

幻灯片80.JPG
幻灯片81.JPG

这里展示了流向数据作用的结果,流向数据是以1s为周期重复的。

幻灯片82.JPG

但是1s的周期下,在大尺寸的波浪上,会很容易看到波形的循环起伏效果,真实感有较大影响。

幻灯片83.JPG

将周期调整为4s后,问题有较好改善。

幻灯片84.JPG

但是这又会导致小尺寸波形拉伸跟锯齿变得严重

幻灯片85.JPG

最终的做法还是老招数,为不同cascade的波形设置不同的周期,从而实现两者的兼顾,这种方法还能很好的缓解flowmap本身的pulsing(跳动)效果。

幻灯片86.JPG

最终效果。

幻灯片87.JPG

对flow效果的实现方案做一个总结。

幻灯片88.JPG

接下来看看shading部分。

幻灯片89.JPG

shading也是跟cascade的scale关联的,有如下的好处:

  1. 可以消解远距离下的pattern & 噪声效果,类似mipmap
  2. 性能会更好
幻灯片90.JPG

主要的细节来自于法线贴图,剩下的大尺寸特征则来自于波形,目前最大波长为1km

幻灯片91.JPG

最后来看看性能表现

幻灯片92.JPG

这是测试场景,开启了前面说的所有特性,并采用了最高配置

幻灯片93.JPG

测试数据由Nsight Graphics给出

幻灯片94.JPG

总体的消耗是84M显存,虽然看起来合理,但还是有一些可以优化的空间:

  1. 不是所有的项目都需要动态改变的地形的,所以如果不需要的话,深度贴图可以在离线烘焙好
  2. 并不是所有数据都需要相同的分辨率的,比如foam就可以尝试使用更低一点的分辨率
  3. 并不是所有数据都需要覆盖8个cascade,同样的foam可以稍微减少几个cascade
  4. 一些临时数据其实是可以复用的,不用占据那么多空间
  5. 部分数据不需要那么高的精度
幻灯片95.JPG

这里是单帧的耗时,所有的计算都是在GPU上完成,花费6.6ms,其中模拟花费2ms,渲染1ms(还有3.6ms呢?)

幻灯片96.JPG

再来看下另一个场景,采用的是crest的默认设置,覆盖7个cascade,每个cascade贴图的分辨率是256

幻灯片97.JPG

贴图分辨率降低为原来的1/4,显存耗费也未1/4,同时模拟时间也降低为接近1/4,不过渲染的面数保持不变,所以基本一致。

幻灯片98.JPG

接下来看看未来的工作

幻灯片99.JPG

首先,目前是把所有的特性都开了,实际需要的pass应该会比这个少,后续可以优化。

目前的网格覆盖了viewer周边的广大范围,这个对于海洋来说没问题,但是放到局部水体如江河湖等就不太行了。

Dynamic Wave sim还存在另外一个问题,就是在相机移动过快的时候,会使得部分wave消失(原因未描述,只说了评估后认为不算是大问题)

幻灯片100.JPG

后续工作方向

幻灯片101.JPG

结论

幻灯片102.JPG
幻灯片103.JPG
幻灯片104.JPG
幻灯片105.JPG
幻灯片106.JPG
幻灯片107.JPG
幻灯片108.JPG

阴影贴图也是每个cascade有自己的一套数据,在计算阴影的时候会从已有的阴影贴图如CSM中进行采样,之后根据不同的阴影类型(普通高光阴影,还是体积光阴影效果)来设置不同的jitter半径

幻灯片109.JPG

这里是阴影作用的位置

幻灯片110.JPG

用红色标识出来

幻灯片111.JPG

这里给了个视频

幻灯片112.JPG

这里介绍了这个方案的不足,就是阴影是跟阴影贴图绑定的,如果水体对应的区域对应的阴影贴图数据缺失,就容易看出瑕疵(不过好像仔细看了下,也没发现问题)

幻灯片113.JPG

阴影部分的总结,可以考虑扩大shadow区域来规避上述问题

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容