编者按 在刚过去的SIGGRAPHAsia 2021会议上,腾讯游戏引擎团队在Houdini HIVE分享了立交桥的程序化生成方案,通过Houdini HDA和PCGFlow插件的配合,实现快速在UE4中生成一座完整的城市立交桥系统,下面将详细介绍一下整套立交桥系统的设计和方法,文末点击“阅读原文”可观看分享视频。
作者:Yuling Ji、If Yu、Kelvin Cai
通过对现实世界中立交桥主要结构的分析,我们大致将立交桥拆分为以下几个部分:桥面,护栏,钢架,桥墩。1. 沿曲线放样建模,生成桥面模型,需要实现机动车道,人行道,隔离带,应急车道等不同类型车道的划分,暴露相应材质,并能通过参数手动控制各个车道的开关,分布,宽度,车道数等,同时也需要提供自动控制选项,如能够根据路面的宽度设置,自动适配合适的车道分布等。2. 需要提供车道线,交通标志控制选项,不同类型的交通标志需放置在道路的对应位置,如车道虚线,实线,应急车道线,车道线合并部分等。1. 根据桥面宽度和厚度等属性设置,沿着生成桥面的曲线,在桥面两侧放置预制模型,生成护栏,根据变形的程度,最终生成模型分为Mesh和Instance两种类型。同护栏,沿曲线放置预制模型,并根据预制模型的尺寸和桥面宽度,自动适配钢架组数。整个工具制作的难点之一就是桥面的生成,怎么让桥面看起来更加真实合理,更加符合现实世界中的规则,如各种车道的划分,各类交通标志的分布,交叉路口的实现等,都是我们制作工具时需要考虑的问题。模块拆分完成之后,HDA工具的开发也同样是按照这几个部分分别进行,每个部分都是由单独的HDA生成,每个HDA输入同一套曲线和相应的预制模型,生成对应资产,后面还会对各个部分进行详细的介绍。传统制作方法一般分为两种,一种是全手动的方式制作,使用传统的建模方法,将立交桥的路面,护栏,桥墩等模型资产,按照项目组的需求,逐一手动制作出来。然而,使用此方案一旦涉及修改,相关大量的工作都要重新开始,效率极低,需要大量的人力。还有一种方案是利用自动化和手动相结合的方式来生成立交桥资产,根据立交桥的结构是否相交,将立交桥拆分为两部分进行处理,没有相交部分的模型结构采用自动化的方式生成,根据输入的曲线,通过工具运算输出模型资产,相交部分的模型结构采用人为手动生成,使用建模软件手工制作,最后再将这两部分的资产拼接在一起,生成完整的立交桥系统。然而,使用此方案也存在着一些问题,比如想修改桥面的宽度,根据输入曲线自动化生成的这部分模型资产,桥面的宽度可以根据参数输入而改变,但人为手动制作的交叉部分的模型资产,则需要匹配新的宽度设置重新制作,无法支持任意路面宽度设置的拼接。路面的交叉角度也存在着一样的问题,一旦涉及更改,交叉部分的模型资产就需要重新制作。在实际项目的制作中,各部分资产通常都需要经过很多次的迭代修改,最终才能达到一个比较满意的结果。然而,使用手动或者半自动化的生成方案,都需要经历大量的重复性的工作,很难快速迭代,产生新的结果,这就导致了游戏的开发效率低下,而且浪费人力成本。因此,我们这里引入了程序化生成的方法,用户只需要根据自己的需求,在合适的位置绘制曲线,并提供相应的预制模型,将他们连接到HDA工具的输入端口,经过工具内部指定的规则和算法,加以参数调整辅助,就可以快速地输出对应的立交桥资产,大大提高了生产效率。下图为使用程序化生成的方法,在Unreal引擎中配合PCGFlow插件实现的效果。通过修改曲线上自定义的参数设置,可以得到不同类型的车道效果。通过修改护栏,桥墩预制模型的输入,可以快速生成不同的模型资产。PCGFlow是基于官方插件Houdini Engine开发,并扩展了官方插件的输入输出类型,如提供了Mask、点云PointCloud等数据的输入。同时拥有可视化的管理界面,为实现复杂的HDA节点网络提供了更多可能。由于官方插件HoudiniEngine提供的曲线输入功能操作不便,使用体验极差,UE4中的LandscapeSpline又要依赖于地形系统,这对于没有地形的场景很难使用,基于以上原因我们内部程序开发了全新的PCGSpline工具。1. 操作上类似于UE4的LandscapeSpline,提供控制宽度,切线等功能,但不同于LandscapeSpline的是,PCGSpline工具可以完全脱离地形系统工作。2. 支持自定义参数,可以创建自定义参数模板,并设置默认值(目前支持整型及浮点),当场景中曲线很多的时候,根据需求(如生成河流/道路等,可能需要不同的参数列表),可以创建几套不同的自定义参数模板,然后每条曲线可以快速引用定义好的参数模板,非常方便快速生产。整个立交桥系统拆分为4个HDA工具(桥面、护栏、钢架、桥墩)制作。2. 预制模型:每个HDA工具输入对应的预制模型资产。根据上面介绍的PCGSpline工具和我们内部开发的PCGFlow插件,并结合我们制作的立交桥各部分组件HDA,就可以快速通过曲线生成立交桥整体的模型。1. 创建PCGSpline Actor,并绘制曲线,一个PCGSpline Actor内可绘制多条曲线。2. 创建PCGFlow Actor,导入LandscapeSplinesInputHDA,在Landscape Splines节点中拾取画好的曲线。3. 根据需要,创建自定义参数模板并设置默认值,如下图所示:4. 在Landscape Splines节点中,对拾取到的每条曲线引用上面创建的自定义参数模板,并根据需要,分别调整每条曲线上模板的参数,如车道宽度,数量,应急车道的分布,宽度等属性。5. 将拾取到的这组曲线,分别连接到立交桥各部分组件HDA的输入端口(如有需要,还可以对曲线进行裁切操作,例如某段指定位置不想生成桥墩,可以在曲线输出端连接一个裁切曲线的HDA,再将裁切好的结果输出给生成桥墩的HDA),然后生成最终结果,节点网络及最终结果如下图所示:根据前面的介绍,我们将立交桥拆分为几部分组件,每部分组件都是由单独的HDA生产,基于这样的操作,我们的考虑是:1. 分工协作上比较方便,每个人负责一部分HDA的开发,例如上面的立交桥就是由三个同学并行工作,最后再整合到一起,这样开发效率方面会大大提升。2. 把HDA的各部分功能单独拆分开来,也可以减少单个HDA内的节点数量,这样一来,工具的运行效率和美术人员的制作效率也会得到一部分提升。3. HDA功能拆分的越细,也越利于以后HDA的复用,毕竟开发也需要一定的时间成本,如果能够实现HDA复用,也能更好地减少这部分成本,而且一个功能对应一个工具,这样后期对HDA的维护也更加容易。但这样做的同时也有一些需要注意的地方,比如参与协作的同学一定要积极沟通,前期设计工具的时候,就有必要考虑到最后整合的问题,要正确定义好每个工具的输入和输出,以便各个工具之间能够相互引用数据,公共的属性变量(如长度,宽度等)要统一设置好,我们这里的做法是把公共的属性全部统一放到了曲线上(结合我们上面介绍的PCGSpline工具),各个HDA独有的参数,放到HDA本身调节。通过上面介绍的立交桥案例,我们再将官方插件Houdini Engine和我们内部开发的插件PCGFlow进行一下简单的对比总结。首先,官方插件HE提供的曲线输入功能操作很不方便,而我们PCGFlow则优化了曲线输入这方面的体验,开发了全新的PCGSpline工具,不止操作上方便了很多,同时也支持自定义参数的创建。其次,虽然官方插件HE也支持多个HDA互相嵌套引用,但当场景复杂时,制作人员就很难理清各个HDA之间的关系。但如果使用PCGFlow插件,则能很好地解决这一问题,各个功能的HDA通过节点形式连接在一起,最后再通过整个节点网络来展现他们之间的关系,一目了然。同时通过节点网络这样的形式,可视化的流程界面,也更方便我们把每个HDA的功能拆分的更细,上文中也提到过,这样更方便HDA的复用,节约开发成本。使用PCGFlow插件并配合我们制作的HDA工具,美术同学可能只需要十几分钟的时间,就能快速的生成一个复杂的城市立交桥系统,这也是程序化生成PCG技术相比传统人工手动制作的强大之处,当然,随着近几年来PCG技术越来越受到从业者的肯定和喜爱,除了上面说到的原因之外,还有以下几个因素:1. 游戏本身的发展,随着游戏质量的要求越来越高,游戏世界的地图越做越大,因此,如果还像以前一样人工手动地去做整个场景,就变得越来越不现实,不仅费时费力,对于美术人员来说,也要承担大量重复性的工作,很难专注到艺术创作上。而PCG技术的出现,则能很好地解决这一问题,不仅能大大提高游戏资产的生产速度,而且程序相比人为的出错率也更低。2. PCG技术需要用户按照自己的需求,定制一系列的规则和算法,来生成大量的程序化内容,因此,使用PCG技术生成的内容,能够很容易地做到,整体风格上比较统一,同时各自也能拥有各自的特点,这也更加接近现实世界中的规则。3. PCG工具迭代方便,随着程序化生成内容的越来越多,制作的PCG工具也会越来越全面,把这些工具慢慢地积累起来,不断进行迭代,雪球也越滚越大,最终将会覆盖到大部分场景的使用。虽然,使用PCG技术能够提升效率,降低成本,但无论PCG技术生成的内容质量如何,他都不会是最完美的,有时需要人为去干预,但程序化生成的内容和人工手动的修改很难很好地融合在一起,手动修改的内容会在下次更新时丢失,想要解决这个问题,目前并不是一个特别方便的事情。还有就是PCG工具也需要一定的前期开发成本,因此,并不是所有的内容都适合用PCG技术生成,需要项目团队提前做出规划,哪些内容使用程序化生成,哪些内容使用人工手动生成。为了更好地配合日益流行起来的PCG技术,我们内部开发了上面提到过的PCGFlow插件,有效地解决了程序和手动融合的问题,并且也积累了一部分HDA工具,能够减少使用者的开发成本。如上图所示,我们的路面工具最终可实现各种类型的车道,如双向六车道,双向四车道,双向两车道,单向四车道,单向三车道,单向两车道,一车道等,支持根据参数手动调整车道分布,同时也支持根据桥面宽度自动适配车道类型及数量,为使用者提供更便利的选择。立交桥路面的生成主要分为路面车道的划分和交通标志的分布两个部分:1. 以现实生活中的路面信息为参考,划分出不同类型的车道,如机动车道(单向/双向/一车道/两车道/三车道等)、人行道、隔离带、应急车道等。2. 根据上面划分出来的各个类型的车道信息:包括车道的数量(整型)、车道的宽度(浮点)、车道的开关(布尔值)等数据,将他们作为参数属性输入,生成对应的横截面模型,并拼接在一起。3. 将生成出来的横截面模型沿着输入的曲线放样建模,生成路面模型,并根据车道的类型和数量,赋予不同的材质。1. 这里使用了一张如下图所示的交通标志贴图,首先,创建不同大小的模型片,将需要的各类型的交通标志贴到对应的模型片上,并对每个模型创建一个自定义的名字属性标记。2. 凭借生成桥面的横截面模型和输入的曲线,生成用来摆放交通标志的辅助线,并继承横截面模型上存储的各类型的车道信息属性,调整辅助线上点的分布,再将上面的交通标志模型片,按照不同的车道信息,copy到辅助线上的每个点上。最后,将桥面部分和交通标志结合在一起,通过调整曲线上不同桥面宽度和车道信息的输入,就可以对应生成不同类型的车道效果。1. 创建PCGSpline Actor,并绘制曲线,可为每条曲线设置不同的颜色和宽度值。2. 创建PCGFlow Actor,导入LandscapeSplinesInputHDA,在Landscape Splines节点中拾取画好的曲线,拾取多条曲线需将Spline Operator Type改为Append模式。3. 在拾取到的曲线上,通过Custom Struct创建自定义参数模板并设置默认值。4. 对拾取到的每条曲线都引用上面创建的自定义参数模板,并分别调整每条曲线上模板的参数,如车道宽度,数量,应急车道的分布,宽度等。5. 导入生成桥面的HDA,将曲线的输出结果连接到桥面HDA的输入口,并附上对应的材质,生成最终结果。最终结果及节点网络如下图所示:
从上面的效果中可以看出,使用PCGFlow插件并配合PCGSpline工具,通过调整每条曲线上的自定义参数模板,就可以快速生成各种类型的车道(双向车道/单向车道/一车道/二车道/三车道等等......)也可以根据自定义参数设置,控制应急车道的分布等。总的来说,无论是从生成效率还是最终结果来看,使用上面介绍的流程在项目中实际生产,都有着较大的优势,能为美术制作人员提供更多便利的选择。分叉路部分的处理也是难点之一,不仅需要考虑路面本身结构部分的融合,还要考虑到交通标志部分的合并处理。根据路面曲线的宽度值和坐标位置,将曲线分为两部分处理,一部分为有相交的部分(也就是叉路口部分,如果两条曲线距离较近,两条曲线宽度的平均值和他们之间的距离值相比较,如果在设定的范围之内,则判定这部分曲线相交),另一部分为没有相交的部分。1. 将每条曲线上的桥面宽度参数和相邻曲线比较,宽度值较大的曲线作为主路,宽度值较小的曲线作为支路,并按照上面介绍的桥面车道生成的方法,分别根据每条曲线上的参数设置,生成桥面车道模型。2. 使用主路部分划分出来的各类型车道,分别对各个支路的车道进行切分处理,得到最终交叉口部分支路的车道模型。
3. 使用上面得到的各支路车道模型,对主路部分的应急车道进行切分,生成交叉口部分的主路车道模型,如下图:4. 最后将处理之后的主路和各支路车道模型合并在一起,生成交叉口部分车道资产。1. 根据上面生成的交叉路资产模型,分别生成主路和支路各部分的摆放交通标志的辅助线,并继承车道上的信息,用于后面摆放各类型的交通标志。2. 根据各车道的前进方向信息,判断交汇部分是否转出,并将相应的交通标志copy到辅助线上的每个点上。按照前面介绍的桥面车道和交通标志的生成方法,分别生成对应的模型资产。
将最终处理后的相交和没有相交两部分资产合并在一起,完成整个交叉路口桥面资产的生成。类似前面介绍过的立交桥路面的生成流程:创建PCGSpline Actor绘制曲线>>拾取曲线>>创建自定义参数模板>>引用参数模板并调整参数>>将曲线输入到HDA生成模型。当两条曲线距离较近,判定为有相交的那部分曲线就会生成分叉路口模型。
分别调整每条曲线上的自定义参数,就可以得到不同类型的车道效果。对于美术制作人员来说,工具使用起来必须要方便灵活,易于生产,PCGSpline提供的曲线绘制工具,不仅可以显示每条曲线设置的宽度值,方便观察交叉口部分的情况,还可以精确地控制曲线上每个点的位置和旋转坐标,便于对齐曲线,这对我们生成最终交叉路口的模型,提供了非常方便的操作。沿曲线放置护栏的预制模型,生成桥面两侧的护栏,更改预制模型的形状,可以生成不同类型的护栏资产。输出:立交桥护栏模型(StaticMesh,Instance)1. 根据输入生成桥面的曲线,桥面的宽度值,曲线的切线及up向量,分别向两侧沿着法线和up向量叉乘出来的方向,偏移桥面曲线,得到两侧生成护栏的曲线。2. 将相交部分的曲线提取出来,并根据曲线之间的交点,将曲线切分为多条线段。3. 每段曲线找到和自己相交的曲线,根据定义的道路内部方向,判断是否在道路内部,并清除内部线段。4. 根据各条线段之间的夹角,将得到的曲线分为两部分,并设定切分长度,生成转角部分的线段。5. 将相交部分和没有相交部分的曲线合并在一起,根据预制模型的长度,切分曲线并替换预制模型,生成护栏资产。1. 导入护栏HDA,将生成桥面的曲线连接到护栏HDA的输入端口。护栏HDA不仅可以为上面介绍的立交桥生成两侧护栏,同时还可以制作如围墙,道路中间的栏杆等资产,只需要提供正确的输入(曲线和预制模型),就可以生成对应的资产。1. 根据输入的钢架预制模型的宽度,和桥面的宽度属性,匹配得到铺满整个桥面最接近的钢架组数,并生成辅助线用来帮助后面分布钢架资产。2. 根据上面得到的钢架组数,重新调整钢架预制模型的比例,以便能够使钢架铺满桥面且钢架之间没有交叠。3. 按照调整之后的钢架预制模型长度,切分生成的辅助线,并把切分出来的每段线段替换成钢架预制模型,生成钢架资产。1. 在输入的生成桥面曲线上分布点,根据每条曲线上设置的桥面宽度属性,将合适宽度的桥墩预制模型(这里提供了三种不同宽度的桥墩模型)沿着曲线的方向,copy到曲线的每个点上,生成桥墩资产。2. 根据输入的桥面曲线,参考之前介绍的生成护栏曲线的方法,向左右两侧分别偏移桥面曲线并按照自定义的长度或者高度参数的设置,裁切每条曲线的起始端和末端,裁切出来的线段用于生成垂直墙体结构资产。3. 使用上面裁切出来的线段,配合地面的高度设置,生成垂直墙体结构资产,并和生成的桥墩组合在一起,完成整个桥墩资产的创建。
这里以前面分享的立交桥为案例,简单介绍一下制作Imposter的需求。沿曲线将立交桥整体(包括桥面,护栏,钢架,桥墩)按照一定的长度切分,每个部分使用最低级别的LOD分别进行各自模型顶点的合并,并继承原有的材质贴图,将合并之后的模型做为一个整体输出为Imposter。提供曲线和模型作为该工具的输入,可以增加多个模型输入端口,输入多组模型资产,根据自定义的长度进行切分,输出Imposter。1. 切分曲线,将输入的曲线按照设定的长度切分,切分之后再进行判断,如果曲线有相交部分,将相交部分分离出来单独处理。最终为处理完成的每段曲线保存一个单独的属性。2. 处理输入模型,以制作立交桥的Imposter为例,需要输入桥面,护栏,钢架,桥墩四组模型,分别对输入进来的每组模型进行检测,只保留最低级别的Lod模型或者Imposter组当然,这里需要提前定义好每个工具的输入和输出,方便各个HDA之间数据的相互引用。3. 根据切分出来的每段曲线上存储的属性,切分模型,并将各部分模型的顶点进行合并,继承原有的材质,处理之后的每段模型作为Imposter使用,中心点取每段曲线的中心点。1. 导入生成Imposter的HDA工具,将曲线,桥面,护栏,钢架,桥墩的self输出端口,分别连接到ImposterHDA的输入端。2. 按需要修改输出Imposter的名字,和切分的自定义长度。3. 选择输出Imposter的HDA,右键执行BakeNode Only命令,输出模型。为了匹配项目组的Sublevel方案,程序化生成的内容也同样需要输出Sublevel做配合。PCGFlow插件开发了相应的功能,通过设置unreal_level_path属性,会自动Bake出模型并放置到对应的Sublevel内。输出:设置好Sublevel的模型资产(StaticMesh,Instance)1. 根据模型的世界坐标位置,并按照自定义的尺寸,将模型划分为不同的区域。2. 为不同区域的模型,设置不同的unreal_level_path属性,将输出的模型Bake到对应的Sublevel内。1. 导入设置Sublevel的HDA工具,分别将桥面,护栏,钢架,桥墩的self输出端口连接到Sublevel工具的输入。2. 按需要设置好尺寸和名字,勾选开关会自动Bake出模型到对应的Sublevel内。以上就是程序化生成立交桥系统的全部内容,通过这个例子我们可以看出,虽然相关的PCG工具制作需要一定的开发成本,但只要整个流程建立起来,游戏资产的生产效率将大大提升。因此,使用程序化生成技术,对于需要海量资产的大世界游戏来说,是一个非常不错的选择。