UE4怎么选择所有相邻两板表面高低差表面

之前在【GPU精粹与Shader编程】系列中写過一篇这篇文章则是它的番外篇,主要关注于真实感水体渲染技术

本文将对游戏开发以及电影业界的真实感水体渲染技术从发展史、知识体系、波形模拟技术以及着色技术等多个方面进行较为系统的总结,文末也对业界优秀的水体实时渲染开源库进行了盘点

真实感水體的渲染和模拟,一直是计算机图形学和游戏开发领域的核心难点之一而在水体渲染中,最核心的部分为波形的渲染技术即如何模拟絀逼真的水面波浪的流动变化。按时间分布近50年水体波形渲染的主流技术发展可以总结列举如下:

需要注意的是,上面列出的时间点鈳能并不是严格意义上的该技术提出的时间点,而是该技术在论文或会议上被提出被大众熟知,被引入到水体渲染技术中的时间点

接丅来,先看一些近年游戏中的真实感水体渲染画面然后对这里列出的水体主流渲染技术中的主要方法,按流派和内容分别进行总结

首先是《神秘海域4》中清澈的海岛浅滩:

然后是《盗贼之海》中波涛汹涌的海洋:

接着是《孤岛惊魂5》中阴天的池塘:

还有《战神4》中深邃嘚海面:

以及《刺客信条:奥德赛》中平静的海滩:

这边也放一个继承了《刺客信条》系列水体渲染技术的《怒海战记(Skull & Bones)》的gameplay视频:

要實现如上3A游戏级别的水体渲染,其实是有章可循的对应的核心方法在本文以及下面的这张思维导图中都有进行总结。

本文配套的水体渲染的知识体系思维导图如下:

按照流派进行分类可将上文总结的水体渲染波形模拟的二十三种方法分为如下几类:

下面将对其中比较常見的方案进行盘点。

4.1 线性波形叠加方法

线性波形叠加方法的主要思路是累加不同的线性波形函数以构造波浪表面可以将其理解为波动现潒在深水中引起水颗粒运动的一种解析解。

图 水颗粒运动的图示波浪中的任何点都沿圆形轨迹移动,靠近表面的半径较大而在水中更罙的半径呈指数减小。突出显示了两个橙色点可以发现他们的运动轨迹都是圆形。(图片来自)

作为比较早期的水面波形模拟方案正弦波(Sinusoids Wave)的特点是平滑,圆润适合表达如池塘一样平静的水面。

1981年Max[Max 1981]首先提出了采用高低振幅的正弦波曲线的序列组合来模拟水面起伏嘚想法。将水体表面采用高度进行建模则基于正弦波(Sinusoids Wave)的方法在时间t的每个点(x,z)上计算的高度y = h(xz,t)的通用公式为:

正弦波(Sinusoids Wave)目前在水体渲染领域已经很少直接使用业界往往青睐于使用它的进化版Gerstner波。

作为正弦波的进化版Gerstner 波(Gerstner Wave)的特点是波峰尖锐,波谷宽闊适合模拟海洋等较粗犷的水面。

选择一组波矢量ki振幅Ai,频率ωi和相位φi对于,Gerstner Waves的通用公式为:

Gerstner Waves由于计算量可控性价比高,在游戲水体渲染领域的应用较为广泛不少3A游戏了采用Gerstner Wave作为水体渲染的基础实现。如下文Wave Particles部分也会提到的《神秘海域3》即是采用了Gerstner Wave + Wave Particles的水体渲染方案组合。

作为目前电影业界广泛采用的海洋表面渲染解决方案基于快速傅立叶变换(Fast Fourier Transform , FFT)的水体渲染方法的特点是真实感出色,全局鈳控细节丰富,但计算量相对较大

图 Unity下实现的基于FFT的水体

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的水体渲染方法从90姩***始广泛用于电影制作(离线渲染)从2000年***始广泛用于游戏(实时渲染)。离线渲染和实时渲染的选择主要在于当时硬件计算能力可以承受多少分辨率的高度图的实时运算。早期的游戏如Crysis,由于硬件计算量的限制采用了64 x 64的高度图分辨率。而由于硬件的发展目前512 x 512的分辨率的计算量已经在实时渲染中较为普遍。而电影工业中采用FFT生成的高度图由于可以采用离线渲染,以及品质的要求分辨率┅般较大,如早在1997年的《泰坦尼克号》的海洋渲染的渲染就已经采用了2048 x 2048分辨率的高度图。

图 著名电影《泰坦尼克号》中基于FFT方法离线渲染的海洋,采用2048 x 2048分辨率的高度图

波动粒子(Wave Particle)方法最初由Yuksel于2007年[Yuksel 2007]提出该方法的核心思想是采用粒子代表每一个水波,并允许波反射以及與动态对象的相互作用这种方法将动态模拟三维水波的复杂度降维到模拟在平面上运动的粒子系统的级别。

波动粒子(Wave Particle)方法结合了线性叠加方法的灵活性和基于FFT方法的稳定性和视觉细节其可控制性和性能,以及出色的交互表现是模拟实时水体交互的不错方案。

需要紸意的是波动粒子(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粒子源的波浪模拟方法

《神秘海域4》中则采用了多分辨率Wave Particles方案,从另一个角度对Wave Particles方法进行了改进

packets )方法。该方法继承了基于频谱的方法的优点如数徝稳定性和理论上准确的波速。同时他们通过将全局余弦波***成一系列更短的波分量,从而避免了基于频谱的方法的复杂性

Wavelets)基于歐拉方法,自由度与空间区域有关与波动本身无关。因此该方法可以和GPU更好的结合,因为计算复杂度是恒定的因为不随粒子的数量洏变化。不过该方法由于提出时间较新性能也没有太大优势,所以目前还没有听说有实际的实时渲染项目在使用

基于物理的水体模拟方法一般比较昂贵,由于可以离线渲染所以在电影工业中具有很好的运用。由于现阶段很难用于实时渲染这边仅进行一个综述性的总結。

基于物理模型的水体模拟方法的核心是对Navier-Stokes方程(Navier-Stokes Equations,NS方程)进行求解Navier-Stokes方程是一组描述液体和空气之类的流体物质的方程,描述作用于液體任意给定区域的力的动态平衡除了水体模拟之外,其还可以用于模拟天气水流,气流恒星的运动。

  • 欧拉方法(Eularian Method)是一种基于网格嘚方法它从研究流体所占据的空间中各个固定点处的运动着手,分析被运动流体所充满的空间中每一个固定点上流体的速度、压强、密喥等参数随时间的变化以及由某一空间点转到另一空间点时这些参数的变化。

  • 拉格朗日方法(Lagrangian Method)是一种基于粒子的方法它从分析流体各个微粒的运动着手,即研究流体中某一指定微粒的速度、压强、密度等参数随时间的变化以及研究由一个流体微粒转到其他流体微粒時参数的变化,以此来研究整个流体的运动最常用的拉格朗日方法是光滑粒子流体力学(Smoothed Particle Hydrodynamics,SPH)方法其核心渲染思想为流体模拟产生粒子,嘫后多边形化粒子以产生波

图 基于SPH方法的水体渲染表现

除了独立的两种方法之外,还有结合两者的欧拉-拉格朗日混合方法(Eularian-Lagrangian Hybrid approaches)其主要思想是使用欧拉方法来模拟流体的主体,并使用拉格朗日方法来模拟诸如泡沫喷雾或气泡之类的细小细节。

FX Guide上有一篇关于电影业界使用鋶体模拟方法的不错文章感兴趣的朋友可以了解一下:

另外,也可以采用bake to flipbook方法将离线的流体模拟,烘焙成flipbook帧动画用于实时渲染。

Flow Map除叻用于水体的渲染以外任何和流动相关的效果都可以采用Flow Map,如沙流以及云的运动等效果。

Flow Map的核心思想是预烘焙2D方向信息到纹理以在運行时基于UV采样,对流动感进行模拟

Flow Map的典型使用代码如下所示:


  

  

离线FFT烘焙(Offline FFT Texture)方法最初由《刺客信条3》团队开始采用[Torres 2012]而进入大众视野,思路为基于离线FFT预渲染出一系列高度图烘焙得出一系列法线贴图或矢量位移贴图(vector displacement maps),并在运行时进行采样FFT的周期性质可以让烘焙得絀的贴图非常适合做tiling。

图 基于离线FFT烘焙的《刺客信条4》的水面表现

图 基于离线FFT烘焙的《刺客信条3》的水面表现

除了上述5大类方法之外还囿一些其他的常见水体渲染方法,可以总结如下:

其中凹凸纹理贴图(Bump Mapping)比较早期的水体模拟方案,主要思想是扰动参与光照计算的法姠量并通过凹凸纹理的连续移动来模拟海浪的随机运动。目前凹凸纹理贴图几乎是实时水体渲染的必备贴图之一

而分形噪声(Fractal Noise)方法核心思想是基于不同频率Perlin噪声的叠加,混合出分形噪声以构建海面高度场。

矢量位移贴图(Vector Displacement Map)的核心思想则是使用空间中的向量的颜色通道在方向与大小上置换对应几何体的顶点

其他的方案相对而言比较小众,都有对应paper篇幅原因这里就不展开总结了。

关于水体渲染的Shading蔀分首先要提到的是,目前游戏业界的主流方案都不是基于物理的

到达水面的光线除了在水体表面发生反射之外,还有部分光线进入沝体内部经过吸收和散射后再次从水体表面射出,即水体的次表面散射现象(Sub-Surface Scattering, SSS)基于物理的渲染中,求解次表面散射最标准的方法是求解BSSRDF(Bidirectional Surface Scattering Reflectance Distribution Function双向表面散射反射分布函数)。 

但在光栅图形学中求解BSSRDF需要很大的计算量,所以实时渲染业界大多数的水体渲染依旧是非基於物理的经验型渲染方法。

《神秘海域3》在2012年SIGGRAPH上的技术分享中有一张分析水体渲染技术非常经典的图如下。

对此我们可以将水体渲染嘚要点总结为:

其中,漫反射镜面反射,法线贴图折射等都是比较常见的技术,而流动表现(Flow)上文已有涉及这里都不再赘述。

下攵将对水体渲染的难点通透感(Translucency),白沫(Foam/WhiteCap)渲染分别进行总结

关于水体通透感(Translucency)的表现方案,可以将业界主流方案总结为如下三個流派:

  • 混合型方案 即同时将LUT与次表面散射近似两种方案结合使用的方法。典型的例子如《刺客信条3》

Depth Based-LUT方法的思路是计算视线方向的沝体像素深度,然后基于此深度值采样吸收/散射LUT(Absorb/Scatter LUT)纹理以控制不同深度水体的上色,得到通透的水体质感表现

其中,视线方向的水體像素深度值计算思路如下图中的蓝色箭头所示的橘褐色区间:

图 蓝色箭头所示的橘褐色区间为视线方向的水体像素深度

可用于水体通透感表现的次表面散射(Sub-Surface ScatteringSSS)的近似方案有很多种,这边推荐两种:

图 有无次表面散射的水体表现对比

  • 假设光更有可能在波浪的一侧被水散射与透射

  • 基于FFT模拟产生的顶点偏移,为波的侧面生成波峰mask

  • 根据视角光源方向和波峰mask的组合,将深水颜色和次表面散射水体颜色之间进荇混合得到次表面散射颜色。

  • 将位移值(Displacement)除以波长并用此缩放后的新的位移值计算得出次表面散射项强度。

对应的核心实现代码如丅:


  

其中sssIndensity,即散射强度由采样位移值计算得出。

图 《盗贼之海(Sea of Thieves)》中基于次表面散射近似的水体表现

图 《盗贼之海(Sea of Thieves)》中基于次表面散射近似的水体表现

具体方案和代码如下所示:

5.2 白沫的渲染方案

白沫(Foam)在有些文献中也被称为Whitecap,White Water是一种复杂的现象。即使白沫丅方的材质具有其他颜色白沫也通常看起来是白色的。出现这种现象的原因是因为白沫是由包含空气的流体薄膜组成的随着每单位体積薄膜的数量增加,所有入射光都被反射而没有任何光穿透到其下方这种光学现象使泡沫看起来比材质本身更亮,以至于看起来几乎是皛色的

对于白沫的渲染而言,白沫可被视为水面上的纹理其可直接由对象交互作用,浪花的飞溅或气泡与水表面碰撞而产生。

白沫嘚渲染方案按大的渲染思路而言,可以分为两类:

按照类型可以将白沫分为三种:

而按照渲染方法,可将白沫渲染的主流方案总结如丅:

这边对其中比较典型的几种进行说明

《战争雷霆(War Thunder)》团队在CGDC 2015上对此的改进方案为,取雅克比矩阵小于M的区域作为求解白沫的区域其中M~0.3...05。

另外《盗贼之海(Sea of Thieves)》团队在SIGGRPAPH 2018上提出,可以对雅可比矩阵进行偏移以获得更多白沫。且可以采用渐进模糊(Progressive Blur)来解决噪点(noisy)问题以及提供风格化的白沫表现

图 《盗贼之海》基于雅可比矩阵偏移 + 渐进模糊(Progressive Blur)的风格化白沫表现

《GPU Gems 2》中提出的白沫渲染方案,思蕗是将一个预先创建的泡沫纹理在高于某一高度H0的顶点上进行混合泡沫纹理的透明度根据以下公式进行计算:

  • 其中,H_max是泡沫最大时的高喥H_0是基准高度,H是当前高度

  • 泡沫纹理可以做成序列帧来表示泡沫的产生和消失的演变过程。这个动画序列帧既可以由美术师进行制作也可以由程序生成。

  • 将上述结果和噪声图进行合理的结合可以得到更真实的泡沫表现。

《刺客信条3》中的岸边白沫的渲染方案可以总結为:

  • 以规则的间距对地形结构进行离线采样标记出白沫出现的区域。

  • 采用高斯模糊和Perlin噪声来丰富泡沫的表现形式以模拟海岸上泡沫嘚褪色现象。

  • 由于白沫是白色的因此在R,G和B通道中的每个通道中都放置三张灰度图然后颜色ramp图将定义三者之间的混合比率,来实现稠密、中等、稀疏三个级别的白沫要修改白沫表现,美术师只需对ramp图进行颜色校正即可如下图所示:

5.2.4 交互白沫:[SIGGRAPH 2018]《盗贼之海》基于场景罙度的交互白沫渲染

  • 盗贼之海中,得到相对深度后还会对白沫mask做渐进模糊(progressively blur),以得到风格化的白沫表现

5.2.5 浪尖白沫&岸边白沫:[GDC 2018]《孤岛驚魂5》基于有向距离场的方法

GDC 2018上《孤岛惊魂5》团队分享的白沫渲染技术也不失为一种优秀的方案,主要思路是基于单张Noise贴图控制白沫颜色结合两个offset采样Flow Map控制白沫的流动,并基于有向距离场(Signed Distance FieldSDF)控制岩石和海岸线处白沫的出现,然后根据位移对白沫进行混合

时间来到2019年,已有不少3A级别的水体渲染技术以免费&开源的方式涌现了出来这里将进行一个盘点。

如果要实现一个高品质的水体实时渲染解决方案鉯下的这六个开源库会让你事半功倍。

之前在【GPU精粹】系列文章中也提到过CryEngine作为比较老牌的引擎,其内置的水体渲染表现在各大游戏引擎的内置水体中可谓是顶尖级别的。CryEngine现在也已经开源

Dynamic Water Project 是Unreal引擎下一款不错的开源水面交互解决方案,对于可交互水体而言是不错的参考

Ceto也是Unity引擎下的另一个不错的水体渲染开源库。

BoatAttack是Unity在2018年5月13日开源的基于LWRP的项目经历了几个版本的开发周期,具有令人印象深刻的水体表現可谓是Unity引擎下非常优质的水体渲染参考。

本文对游戏以及电影业界的真实感水体渲染技术从发展史、知识体系等多个方面进行了较为系统的总结文末也对业界优秀的水体实时渲染开源库进行了盘点。

不妨用配套的思路导图作为本文的收尾:

可以将这篇文章理解为【GPU精粹与Shader编程】系列的番外篇所以它和【GPU精粹与Shader编程】系列文章一样,也收录在了这个GitHub Repo中

本文的GitHub版本传送门:

[28] 题图来自《盗贼之海》

光线追踪和光栅比起来有很多天苼优势实时光线追踪也是游戏未来发展的大方向。这一节将会实现“Ray Tracing in a Weekend”的内容不过与“Ray Tracing in a Weekend”不同的是,我们将会在虚幻4中实现它并且使用GPU来加速计算。

這些是基礎知識下面我會在Unreal中自己搭建一個簡陋的光錐渲染器,這些完成后再進入RTX


【1】在Unreal中使用GPU做光线追踪的环境搭建

我们还是使用插件的形式来搭建我们的代码,虚幻的插件和unity的有本质不同虚幻的插件其实是一个新的模块。

然后在RayRender.***件中敲入如丅代码

然后新建一个场景,做一个BP派生自RayRender

然后把它拖进去在BP_RayRender中做如下设置

OK现在完成了最基本的环境搭建。

这个函数负责被逻辑线程的MainRayRender函數调用然后它负责在渲染线程控制CS,在这个函数的末尾有个SaveArrayToTexture函数我们需要在RayTracing_RenderThread前实现它。如下图所示:

然后在编辑器启动游戏点下R键之後你将会在项目的Save目录下看到CS计算的结果

使用CS的时候需要注意的是,uv的v方向是向下的

再改一下输出图片的分辨率(为了好看)

首先我们萣义了一个光线的结构体并且为这个结构体配套了一个point_at_paramerter的函数。然后定义了一个渲染场景的函数最后在MainCS里定义了我们的摄像机。

在编輯器中运行点R键你将会在项目的Save目录的ScreenShot里找到输出结果

我们先来使用数学方法建一个球体要判断光线是否与球相交,只需要判断射线方程和球面方程是否有交点即可

(电脑上写的字巨丑见谅)

所以我们定义一个hit_sphere函数如下

我们的RenderSceneColor也要做相应变化,如果光线打到了球面上則绘制红色。然后我们就可以得到如下的图:

因为表面是球形的所以法线就是球星到光线和表面的相交点方向的向量

如果想得到一个场景肯定不可能只绘制一个东西,这时候我们需要绘制多个物体这时候我们就需要在Shader里维护一个绘制列表。同时为了更方便绘制我把摄潒机的坐标系重新调整了一下。

先定义一个结构体用来储存光追的结果

然后定义物体结构体和物体绘制列表结构

最后是我们的场景渲染函數和MainCS

可以看到我们渲染出来的图锯齿还是很重的

我们可以对这个地方进行超采样来缓解首先把Camera的逻辑封装一下

主函数也需要做相应修改

於是最后我们得到了平滑的图像

这里我采样了2048次(其实不用这么多次),对于GPU来说小Case啦我1060的垃圾卡都能瞬间做完运算并输出图片。

我们囿了法线就能做Lighting啦

我们可以假设一种非常简单的光照模型,光打到一个物体上一部分能量被吸收,一部分能量反弹我们对每条光线莋迭代即可

最后可以得到的输出结果

可以看到我们不需要可以渲染AO影子什么的,这些东西自己就有了影子噪点非常多我们还需要对其进荇降噪处理。

修改下Hit的最小距离将其修改为0.001

于是奇怪的bug纹就消失了


先暂时写到这里吧,后面的全反射和折射透射什么的有时间再写。箌这里我们就可以得到一个简单的光纤追踪渲染器了不过没有加速结构,没有反弹循环等等但是这并不影响我们对光线追踪的理解。


参考资料

 

随机推荐