背景
在大世界游戏里,植被(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两个页签。里面有摆放植被和输出地形信息的功能。
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的生成位置信息了。
即使是同一个
Species里的同一种植被,由于生长环境和时间的不一样,也会有大小(Size)的区分。天然界中,一般小树会长在树林的边缘,而大树更倾向在树林的中央。须要用Size来控制不一样生长程度的植被的大小,再使用Scale增长一些随机的变化,让整个森林更加天然。下图增长了5级的Size后,虽然有了必定的变化,但仍是会有阶梯状的感受。
在Size级别之间增长了Scale后,阶梯感就消失了。
使用Size和Scale设置,有了不一样比例的同种类树木后,就要想办法把合适大小(Age)树木布置到场景里。
如何在正确的位置摆放对应Size和Scale的植被,是经过让植被的Age与Size对应,来选择摆放多大尺寸的树木。前文也提到,天然的树林是边缘的树龄小,中心的树龄大的分布。经过面板上的Age Max Distance的值,基于Viability的Volume值收缩必定距离后再生成SDF,做为Age使用。再根据Age对Size的Influence和Age Ramp,获得对应树木的Size。
经过Age Ramp对应不一样的Size的分布
一般是使用Houdini的Scatter设置必定的密度值,再将Viability的Mask转为Point Could。但默认的方式转换,每一个Point自身没有Size信息,当密度过大,或者Scatter不一样Size的植被时,很容易会出现同一种植被相互叠加的状况。因此这里要将Size转算为Density值,Size越大,Density越小。
以下图所示,Size越小,
Density越大。同时也能够读取其余的Terrain Data来影响Density值。
地形信息也会对植被的颜色产生影响,例如读取水的SDF值,根据颜色的Ramp来对水塘附近的植被产生颜色变化。
天然界植被还会受到环境的影响,产生必定的旋转效果。好比在斜坡上的植被会沿着坡度有必定倾斜和旋转,河边的草体会更朝向水的方向,以及会受到风力的影响倾斜等等。这个在Houdini里实现比较简单。
植被对地形的影响
Species HDA的另一个功能就是能够根据植被来影响地形数据。这些功能和地形制做部分比较类似。
植被的Mask能够影响到对应地形的高度,这样能够模拟出树根隆起的效果。
能够将与树根部分贴图对应的Terrain Texture ID输出给地形材质,让地形图层部分使用的贴图与树根贴图对应,而作出融合效果。
向下图这种在黄松(
Ponderosa)周围摆放石块(Rock)的效果,是后面的Rock的Species依赖前面Ponderosa
Species的生成结果。
实现方式就是把
Ponderosa的viability值输出,再Rock Species里再读取。
根据
Ponderosa的Viability Mask Scatter获得Rock的Entity Point。
除了V
iability外,也支持Age的输出。
FC5的每平方千米的场景有60w左右颗植被。整个游戏有100平方千米。
因为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;">算法