【GDC2024】Applied Mesh Analysis:

2024-04-26  本文已影响0人  离原春草

今天要学习的是《漫威蜘蛛侠》在GDC 2024上分享的城市LOD实现策略,重点介绍了其LOD数据的生成,有如下的几个关键信息:

  1. 低级别的LOD数据是基于simplygon生成的,借用了减面跟remesh的能力,当然,中间做了很多细节的处理
  2. 远景模型分为两级LOD,中景的高精度与远景的低精度,高精度是通过减面算法得到,低精度则是通过remesh(类似于只保留一个内壳,减面粒度更狠)得到
  3. 低精度模型是常驻的,而145m以内会进入中高精度模型的加载范围,需要走streaming
  4. 场景采用DrawIndexIndirectMulti方式渲染,可以一次性绘制多个类别的多个实例,在4k场景下,PS5的消耗大约是1.4ms
  5. 玻璃(窗户)需要单独绘制,其加载距离跟绘制方式都走自己的独立一套,耗费可能高于建筑本身
  6. 部分小物件需要做远景展示,这类物件需要人工标注,且通过极致的合批算法来优化性能
Page 1

先来看下建筑合并的基本介绍。

Page 4

大部分建筑都是由若干小部件组成,比如图中所示的建筑由2k个部件组成,部件之间并不能保证联通(?)

Page 5

这里列举了之前几部蜘蛛侠的城市远景生产管线:

  1. 美术同学手工在DCC软件中输出带有LOD的建筑模型
  2. 引擎中保存的城市区域(zone)信息跟上面的建筑模型数据一起输入到Houdini中,完成城市的PCG生成
  3. 输出的成熟数据通过引擎自带的Texture Capture工具来完成Atlas的合并与proxy模型或贴图的生成

整个管线由Houdini驱动,但是其中也会由较多的人力干预。

在145m之后,会切换成远景?

Page 6

此前的工作流中存在一些不统一或者效率低下的地方,针对性的,新管线希望能够达到如下几个目标:

  1. 自动化:
    1.1 减少手工环节的成本
    1.2 在构建服务器上完成LOD的自动更新
  2. 提升整体的品质
    2.1 分为两级LOD:中景采用高分辨率,远景采用低分辨率(近景呢?)
    2.2 对于raytracing也是两级,不过中景采用的是中分辨率,远景采用低分辨率
Page 8

为了提升中景的表现,需要将此前的tile方式改成新增的一级相对高分辨率的LOD

Page 7

新管线的落地存在一些挑战或风险:

  1. 游戏基本上制作完成了,再来动管线需要慎重
  2. 现有的建筑采用的索引是16bit的,顶点数的约束,对新管线也提出了要求
  3. 对内存与磁盘存储空间的约束也会是一个风险
Page 11

首先考虑的就是模型减面

Page 14

Simplygon减面算法已经在项目组的使用中,不过之前用的是非远景的模型LOD减面,这个算法的使用需要经过几次迭代,有如下的几个特点:

  1. 其中有一个步骤是weld(拼接、焊接),即需要人工来完成减面后模型的拼接(?)
  2. 能够得到基本一致的模型跟拓扑结构
  3. 不支持非流形表面(non-manifold)
  4. 每个建筑的第一级LOD就是采用这个算法得到的(?)

简单来说,如果一个几何体可以被展开成一个连续的二维平面,即为Manifold,否则为non-manifold,参考几何】Manifold与QEM

下图中给出的建筑经过这个算法可以优化为原始建筑的1/10的面数。

Page 15

Simplygon还有一个remesh的功能,这个功能可以生成一个全新的mesh:

  1. 与原始模型具有较高相似度
  2. 两者的模型、组件的连接关系并不相关
  3. 可以消除非流形的edge(边)
  4. 计算消耗较高,高模的计算花费时间可能需要超过几个小时
  5. 可以用来生成建筑的第二层远景LOD(面数是第二层的BVH LOD的两倍?)
Page 16

减面功能对于高模有较好的效果,而Remeshing则对低模作用比较好,那中模该怎么得到呢?测试发现两种方案都不能得到令人满意的结果,最终的方法是(?):

  1. 通过Remesh来得到一个相对保守的目标
  2. 之后通过减面来得到令人满意的分辨率
Page 17

这里的问题是,这种做法会跟之前列举的几项引擎的约束相冲突,主要体现在:

  1. 原始模型中法线的缺失在经过减面或者remesh之后会导致效果的异常
  2. Remesh得到的inner shell(?)
  3. 减面之后会存在裂缝

下面对这几个问题做仔细的介绍

Page 18

此前的建筑模型的顶点数据是没有顶点法线,也没有法线贴图的,法线数据完全是运行时计算的,而这个导致的问题就在于在减面或者remesh之后就会遇到问题:

  1. 之前平整的区域,减面或remesh后可能不一定依然凭证
  2. 导致faceted的效果(?)
  3. 建筑会出现三角面片化的下次,如下图所示
Page 19

部分未封闭的建筑经过remesh之后则变成了一个封闭的mesh。

Page 20

减面会导致裂缝

Page 21

三角面片模型的分析

Page 23

基本信息

Page 25

对流形的解释

Page 26

Genus表示的是一个模型中包围出了多少个孔洞,可以通过下图中的公式计算得到:

Page 27

对偶模型,将每个面片转换为一个顶点(放在面片中心),相邻的面片会存在一条边来连接对应的两个顶点,得到的mesh就是dual mesh。

Page 28

离散弯曲度,用于衡量曲面的弯曲度,光滑的表面的曲度可能非零。

模型的曲度怎么定义?

  1. 分段定义
  2. 大部分区域的曲度可能是零或者无穷大
  3. 基于mesh所模拟的表面的曲度来定义
Page 29

模型展开,可以通过这个来解决法线缺失的问题。

Page 30

很多时候,着色的细节要比模型的轮廓要重要,尤其是对于远景物件,以及本身就是平整的表面而言。

基于上述结论,我们应该在减面的过程中尽可能的保留法线:

  1. 在减面的时候给法线更大的权重
  2. 在最后的时候舍弃(最终结果不需要法线?)
  3. 可以提升最终的结果
  4. 对remesh没啥作用

关键的方式是对减面后的模型进行展开,从而使得修正后的法线逼近我们想要的法线。

Page 31

展开算法基本介绍:

  1. 对具有相似法线的面片进行聚类
  2. 对每个cluster计算一个目标的平面
  3. 将目标平面跟对应的顶点关联起来(每个顶点最多关联三个平面)
  4. 计算能够满足所有目标平面的新的顶点
Page 32

对偶模型其实是一个无向图:

  1. 每个对偶顶点都对应于原始模型的一个面
  2. 每个对偶边都对应于原始模型中的一对相邻面
  3. 通过对这个图进行遍历就能找到相连面

在实际执行算法的时候,会将对偶模型用edge-face邻接map来存储:每个原始模型的边会存储与之关联的面的list

这种做法在保留遍历的便捷性的同时,还能将流形的数据非常自然的存储下来。

Page 33

基于法线进行聚类,分三步:

  1. 选择某个cluster的种子面,通常会基于面积来选取,取最大的
  2. 对前面的邻接map进行广度优先遍历,放弃那些法线差异大的面,找到法线相近的面,塞入cluster
  3. 重复上述步骤,直到所有的面都处理完成
Page 34

法线的比对:

  1. 永远是跟种子face进行比对
  2. 减面算法,原始顶点的法线数据可以保留
  3. remesh算法则直接使用目标平面的法线
Page 35

通常是基于面积加权来求得目标平面,当然这里也可以做一些复杂的计算。

Page 36

最后,每个cluster只能得到一个目标平面,同时,目标平面不能直接移动,否则会导致面片的割裂,要想移动,就需要同时移动顶点,最后每个顶点最多跟三个面片关联起来。

Page 37

得到目标平面后,接下来就要计算对应的顶点了,通常有如下三种情况:

  1. 顶点只跟一个平面相关,那就将原始顶点投影到新的平面上即可
  2. 顶点与两个平面关联,需要按照图中计算
  3. 顶点与三个平面关联,也是按照图中计算
Page 38

完成展开后,之前的结果就变得好多了

Page 39

接下来看看怎么解决remesh的inne shell问题。

Page 41

先给出基本的思路:先对面片进行分类,分为内部跟外部两类

只使用模型的局部数据是不足够的,比如对于内凹的模型而言,我们不能根据法线来判断内部部,还需要一些全局数据,但数据要从哪里获取呢?

这里给出的方案是通过flood filling算法(对偶模型)来得到相关数据,最终的目标是对于一个相互连接的组件,生成一个连续的inner face set。

Page 42

分类算法思想介绍:通过随机发射的射线来计算(离线):

  1. 先找到一些内部顶点:在俯视角下,在模型的bounding box范围内,通过蒙特卡洛采样算法得到
  2. 再来完成第一轮的face set的归类:对上一步得到的顶点,对每个face投射一条射线,并判断当前face是否可以看见对应的顶点
  3. 最终通过flood-fill完成修复:得到一套连续的inner face跟一套连续的outer face

下面对每一步做展开介绍。

Page 43

内部顶点要怎么得到呢?

对于每个采样点,基于这个点向水平方向投射四条射线(不考虑上下),只要其中有三条射线跟面片相交,就认为这个顶点是内部的。

通过蒙特卡洛算法按照上面的思路来摸索出inner face的大致区域

Page 44

再来看怎么获取到inner face set。

对每个interior point,会朝每个inner shell face发射射线,为了识别屋顶,还需要朝上方投射设下。

之后基于命中结果来判断inner face,这里得到的结果不用十分精确。

Page 45

flood-fill算法的主要实现思路就是对dual mesh数据进行遍历,最终得到一个inner shell跟一个outer shell。

Page 48

最后还可以通过一个数值来对结果进行确认,最终的inner shell的面积占比大约是50%。

Page 49

再来看下怎么解决减面后的模型破裂问题。

Page 50

这里有一个观察是,remesh得到的inner shell是能够嵌入到原始模型的内部的,且没有裂缝。

Page 51

这里的思路就是:

  1. 先减面
  2. 再remesh
  3. 将remesh结果叠加到减面结果上来修复裂缝
Page 52

这里对上面思路的细节做了一些补充。

Page 53

不过remesh的inner shell可能不一定存在,因为原始mesh可能是封闭的,这种情况需要做一下退化处理:

  1. 将remesh的结果沿着法线做一下收缩
  2. 对反面进行翻转

退化方案是有一些瑕疵的,因此优先backstop方案。

Page 54

backstop方案对于一些非建筑的模型,就不太适用了,比如桥梁等模型。

Page 55

这里给了一个算法来检测对应的模型是否适用于backstop方案。

Page 56

再来看下bonus问题怎么解决,即实现法线贴图的自动检测。

Page 57

整个城市有接近10k个大型物件,少数物件需要弯曲效果(需要法线数据),而人力的调整就非常低效了。

Page 58

这里给了一个新的离散曲度的定义。

Page 60

并基于上述定义来计算面片与cluster的曲度。

Page 61

这里给出一个判断表面是否为曲面的算法:

  1. 对每个面片,计算其曲度跟视觉重要性
  2. 对dual mesh进行遍历,找到在一定曲度范围内的cluster face
  3. 对每个cluster,计算一些相关的其他属性
  4. 找到最重要的cluster,如果重要性高于阈值,就认为需要一个法线贴图。
Page 62 Page 63 Page 64 Page 65 Page 66

最后将所有的环节放到构建机上

Page 67 Page 68

远景LOD的Streaming细节:

  1. 低精度的LOD是常驻的
  2. 145m以内需要相对高精度的模型,这些数据会走streaming
  3. RT BVH数据也同样需要Streaming
Page 69

一些性能上的优化方案:

  1. 远景的大部分建筑基本上可以通过一个shader完成(类似于HLOD?)
  2. 通过DrawIndexIndirectMulti接口实现多个(类)建筑的一次性渲染
  3. 这个场景在4k下只需要1.4ms,1080P则只需要1ms(PS5)
Page 70

蜘蛛侠中由于有Goo(一种粘稠物质)的存在,所以会破坏一个shader覆盖全场景的远景的预想。

Page 72

建筑的窗户需要特殊的构造方式:

  1. 材质特殊
  2. 需要RT反射
  3. 需要RT的interior细节
  4. 其他的一些特殊渲染效果

这里的做法是将窗户的模型跟建筑分开来处理:

  1. 窗户使用最低级的常规LOD
  2. 材质的渲染距离会相对远一点,最少到600m,对于尺寸较大的建筑,这个距离还会更远
  3. 窗户的渲染消耗可能高过建筑本身,在4k上会去到2~5ms。
Page 73 Page 74 Page 76 Page 77 Page 78 Page 79 Page 80 Page 81 Page 82 Page 83

LOD的切换分为两类,一类是从常规模型的最低级LOD到远景模型的最高级LOD,第二种是远景模型内部的LOD切换。

常规模型到远景LOD,采用的是已有的算法

  1. 远景LOD是常驻的,需要先绘制
  2. 之后通过dither的方式添加常规模型的LOD
  3. 通过stencil来控制两者的显隐比例

远景LOD内部的切换,则是在shader上完成

Page 84

这里对小物件做一下专门介绍:

  1. 部分小物件需要较远的视距,比如树木等
  2. 这部分小物件也不适合前面的远景物件生成算法
  3. 最终通过一套完全独立的方案来覆盖:
    3.1 这类需要远景的小物件需要人工标注
    3.2 在场景中是常驻的
    3.3 渲染距离相对较远
    3.4 通过extrem instancing方案来提升性能
Page 85
上一篇下一篇

猜你喜欢

热点阅读