Houdini技术体系 过程化地形系统(一):Far Cry5的植被系统分析
背景
在大世界游戏里,植被(biome)是自然环境非常重要的组成部分,虽然UE4里的也有比较不错的地形+植被系统,但相比国外AAA级游戏的效果,还是有不少的差距,简介如下:
- UE4的植被分为(Folige Type)和(Grass Type),都是通过HierarchicalInstancedStaticMeshComponent来进行实例渲染,但生成方式不同
- Folige Type可以带有碰撞信息,通过Foliage Paint tool或Procedural Foliage Volumes 预先生成和放置,再跟随场景读取时加载。
- Grass Type自己不带碰撞,根据所在的Material Layer和设置预生成密度图,在运行时根据摄像机来生成视野周围的Grass。
- 两种方式在摆放上都会有不少的限制
- 当一块地表上出现多种Foliage或Grass时,还是会产生不同类型植被之间的竞争和重叠的情况
- Folige Type在特别是石块,树木,草体都存在的情况下,增减修改都会变的很麻烦
- 布局上还是要人为的控制,一旦场景地形要修改,就要重新各种刷新或配置Foliage Type,Grass Type也要跟着刷Material Layer来改变。
- Procedural Foliage Volumes可以一定程度上减少Paint的工作量,但可控的参数过少。
所以植被的过程化系统无论是表现上,性能上,还是迭代的便利性上,都是非常重要的部分。近两年GDC上,Ubisoft在Tom Clancy’s Ghost Recon: Wildlands和Far Cry 5(后简称FC5)的过程化植被生成上都有不错的分享。这里我把Houdini的植被系统实现分为三部分来分享,在基础管线部分主要是Houdini的植被系统与UE4的植被系统如何做衔接,过程化地形部分本节先会分析FC5的实现机制,下一节介绍如何在Houdini和UE4环境下实现类似功能。
Far Cry 5的植被系统概述
- 植被的分层结构:每个地块有一个生态组(Main Biome),而主生态组又包含若干的子生态组(Sub Biome)。FC5里把一个Sub Biome结构称为Recipes,每个Recipes由代表不同的区域和种类来命名,比如biome_moutiain_forest,代表的是山上的树林。而一个Recipes里则是若干个Species组成,每个Species代表的是Recipes对应区域和生态里一个植物或物件种类的生成。
- Houdini与分层的联系:每个Species其实都是一个Generate Terrain Entities的Houdini HDA节点,把HDA节点一个接一个的连在一起组成Recipes(Sub Biome),而所有的Recipes再连接在一起成为一个Main Biome。每个HDA可以读取地形Mask信息和它的配置参数,输出自己对应的植被的摆放信息,而连在后面的HDA会根据前面HDA留下的信息,通过HDA内置的参数和算法,确定是否会覆盖前一个HDA的生成信息。
- 地形数据信息:根据地形的高度信息,生成不同的Mask信息,以及用户手绘的数据
- 包括Occlusion,Flow,Slope,Curvature,Illumination等通过高度生成的信息,以及海拔,经纬度,风向等人工输入的信息
- 湖泊,道路,栅栏,电线杆,峭壁等其他过程化生成的Mask信息也会被导出保存为2D贴图,确保植被不会生成到这些区域。
每个Speicies对应的Generate Terrain Entities都是同一个HDA节点,只是根据对应不同植被实体有不同的参数设置,除了实体摆放功能外,还可以根据摆放后的实体信息影响地形并输出到地形数据上,以及定义每个Speicies的生存能力(Viability):
- Species的Entities功能 :生成地形上特定种类的实体,包括树,灌木,花草,岩石等等
- Viability : 每种物体在根据区域有自己的生存力,以及对应的生存范围和生存优先度,来避免物件之间重叠和穿插。
- Age:树木有自己的Age(年龄),根据Viability生成SDF的来决定。根据Age来控制树的高低。
- Size : 同一种类的树木有几种不同的Size,小树倾向在外围,大树倾向在中心,同一Size可以这种植被的变化(枯树等)。也可以用Age来替代Viability来控制Size
- Scale:在不同的Size等级之间里通过Scale来平滑过渡。
- Color: 每个植被实例可以根据地形或自身属性来改变的颜色变化,比如通过水体的SDF来控制草体Species的颜色。
- Density:树木密度,可以根据树的大小设置不同密度。
- Rotation:根据地形坡度,水源信息以及风力来改变草体的旋转和方向。
- Transform : 没有特意讲到,应该就是Transform信息
- Group : 同样没有特意讲到,可能就是Houdini的Group信息。
- Species的写回Terrain功能 : 布置的资源也可以影响到地形的信息。
- Terrain Color:地表植被信息可以给予地形贴图一个Tint Color,这个颜色也会通过shader影响到代表上草体的颜色,在地形贴图有限的情况下增加变化。
- Terrain Textures : 输出树根部分的贴图ID,以及混合系数,配置到Terrain Material的Layer Texture上,做到地表和树根的融合效果。
- Terrain Data Output : 可以把Species的数据包装输出,比如把Age或Viability写入到一个属性,再由后面的Species读取。
- Terrain Deformation:植被信息会影响到地形HeightMap的变化,比如树根部分会让周围地表隆起。
整个FC5 Biome的核心就是这个名为Generate Terrain Entities的HDA节点。
- 输出到编辑器:所有实体的信息会以Entity Point Cloud的方式输出到引擎编辑器,每个Point除了位置信息属性,还有对应的实体实例引用,以及前面提到的HDA中生成的类似Size,Scale,Color等信息传送到编辑器,编辑器里再根据这些信息在场景里生成对应的资源。
所以,要实现FC5类似的植被系统,Houdini和UE4里需要做的功能主要有:
- 根据地形的HeightData生成各种Mask信息,以及其他过程化工具生成Mask的回读功能
- 类似Generate Terrain Entities里的HDA功能
- HDA所生成的点云和Terrain信息回读到UE4里改变之前的配置
本节会先对FC5的文档做需求分析,在下节提供具体的Houdini的实现方法。基础篇里涉及如何在UE4里把HDA输出的数据运用到场景里。
地形数据生成
上文提到,过程和生成Biome使用的地形数据主要来源有以下两个部分:
- 在Houdini里基于Heightmap生成
- 游戏编辑器中输出的2D Mask数据
基于Heightmap生成各种特性Mask的功能,在使用WorldMachin做地形时就经常被使用,Houdini作为WM的替代品也有对应的节点。使用这下图这3个节点,或者基于他们的内部实现自己整合一个节点就可以生成Occlusion,Flow,Slope,Curvature,Illumination等信息了。
而像在编辑器里过程化生成的湖泊,电线杆等信息,就是设置一个公共的资源目录保存对应的Mask,在每次编辑器里调用过程化工具生成时,使用Python脚本加载对应Mask图,使用Mask By Object节点生根据场景成Mask,再用Python把过程化场景的信息作为Mask保存起来。或者是把每次过程化生成的信息保存,然后在统一Cook时输出Mask信息到2D贴图里。
总体来说,地形数据的生成和导入,都是比较成熟技术,没有太大的难度,后面还是聚焦在植被的生成方法上。
地形的植被实体生成
下图是FC5的层级结构示意
实际整个Main Biome,就像下图这样一个个的代表特定植被的Species的连接在一起。
每个Species都是一个Generate Terrain Entiies的HDA节点
接下来通过文档里HDA的截图分析需求。下图是HDA的主面板。
图上半部分是植被,地形数据列表和显示选项等,生成参数的配置在下半部分,有Entities和Terrain两个页签。里面有摆放植被和输出地形信息的功能。
- Viability
Entities页签上首先就是Species的Viability的设置,Main Biome就是大量Species节点串连起来的结果,下图的示例是两个Species串联,Viability的值则是从Terrain Attribute Data里读取的,也就是之前生成和导入的Terrain Mask。下图中SpeciesA的Viability是从Occlusion的mask取值,他的Viability值的倍数为1,而SpeciesB是从Flow Mask里做Viability的取值,Viability的值的倍数为2。并使用Ramp图标来做渐变和裁剪效果。最后途中树木的摆放效果也是因为B的Viability值比A的Viability高,所以Mask重合的部分的B树会替换掉A树。
因为SpeciesA和B在树的种类,密度,大小上并不一样,Point Cloud并不会完全重叠在一起,而是类似下图这样的分布关系,所以不能只通过的植被对应的Point的位置信息和Viability来做判断,还需要让每个Entity Point有Viability Radius,下图中,因为B的Viability值比A低,所以当他们的范围有重叠时,B的树就会被A替换掉。
此外,每个Species还有Priority和Priority Radius的设置,计算最终的摆放前,先会用Priority做比较,当Priority一样时再用Viability值做比较。
有些植被还有它特定的生存习性,地形数据除了从Heightmap计算或外部读取外,还需要可以人工的在houdini里控制生成范围,再把多个地形数据的混合出结果,例如下图得到就是在一定海拔高度范围内,沿着flow Line生长的植被Mask。也可以把之前编辑器导出或保存的例如湖泊,道路,悬崖等Mask信息导入Specie的地形数据里作为剔除Mask使用,确保植被不会生成到这些不应该生成的位置上。
通过在每Species的Terrain Attribute里增加不同的数据来混合,就可以得到类似特定位置的Sub Biomes的生成位置信息了。
- Size和Scale
即便是同一个Species里的同一种植被,因为生长环境和时间的不同,也会有大小(Size)的区分。自然界中,通常小树会长在树林的边缘,而大树更倾向在树林的中央。需要用Size来控制不同生长程度的植被的大小,再使用Scale增加一些随机的变化,让整个森林更加自然。下图增加了5级的Size后,虽然有了一定的变化,但还是会有阶梯状的感觉。
在Size级别之间增加了Scale后,阶梯感就消失了。
使用Size和Scale设置,有了不同比例的同种类树木后,就要想办法把合适大小(Age)树木布置到场景里。
- Age
如何在正确的位置摆放对应Size和Scale的植被,是通过让植被的Age与Size对应,来选择摆放多大尺寸的树木。前文也提到,自然的树林是边缘的树龄小,中心的树龄大的分布。通过面板上的Age Max Distance的值,基于Viability的Volume值收缩一定距离后再生成SDF,作为Age使用。再根据Age对Size的Influence和Age Ramp,得到对应树木的Size。
通过Age Ramp对应不同的Size的分布
- Density
通常是使用Houdini的Scatter设置一定的密度值,再将Viability的Mask转为Point Could。但默认的方式转换,每个Point自身没有Size信息,当密度过大,或者Scatter不同Size的植被时,很容易会出现同一种植被相互叠加的情况。所以这里要将Size转算为Density值,Size越大,Density越小。
如下图所示,Size越小,Density越大。同时也可以读取其他的Terrain Data来影响Density值。
- Color
地形信息也会对植被的颜色产生影响,例如读取水的SDF值,根据颜色的Ramp来对水塘附近的植被产生颜色变化。
- Rotation
自然界植被还会受到环境的影响,产生一定的旋转效果。比如在斜坡上的植被会沿着坡度有一定倾斜和旋转,河边的草体会更朝向水的方向,以及会受到风力的影响倾斜等等。这个在Houdini里实现比较简单。
植被对地形的影响
Species HDA的另外一个功能就是可以根据植被来影响地形数据。这些功能和地形制作部分比较相似。
- Terrain Defromation
植被的Mask可以影响到对应地形的高度,这样可以模拟出树根隆起的效果。
- Terrain Texture
可以将与树根部分贴图对应的Terrain Texture ID输出给地形材质,让地形图层部分使用的贴图与树根贴图对应,而做出融合效果。
- Terrain Data Output:
向下图这种在黄松(Ponderosa)周围摆放石块(Rock)的效果,是后面的Rock的Species依赖前面Ponderosa Species的生成结果。
实现方式就是把Ponderosa的viability值输出,再Rock Species里再读取。
根据Ponderosa的Viability Mask Scatter得到Rock的Entity Point。
除了Viability外,也支持Age的输出。
FC5的每平方公里的场景有60w左右颗植被。整个游戏有100平方公里。
- Terrain Color
由于Terrain Texture总数有限制,需要根据通过地形数据生成Tint Color,在地形渲染时让Tint color作用到Terrain Texture上产生地表变化。
同样,在地表上摆放的植被的shader,也可以受到Tint color的影响。
总结
通过植被系统输出下图中数据到编辑器,不但可以自动化的生成植,还可以根据植被信息进一步增强地表渲染效果。
FC5的整个植被系统的需求分析就到这里,除了一些细节功能外,Houdini方面并没有太多的技术难点,下一节,会针对UE4 Houdini Engine对应UE植被系统的管线,以及如何在Houdini里实现相关功能进行展开。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">