美文网首页水体渲染
【Siggraph 2019】Multi-Resolution

【Siggraph 2019】Multi-Resolution

作者: 离原春草 | 来源:发表于2024-08-04 23:41 被阅读0次

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

    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区域来规避上述问题

    相关文章

      网友评论

        本文标题:【Siggraph 2019】Multi-Resolution

        本文链接:https://www.haomeiwen.com/subject/ibwihjtx.html