使用几何体脚本和蓝图快速替换Cesium瓦片

本教程将介绍如何通过蓝图技术,快速使用Cesium的地图多边形Actor(Cartographic Polygon Actor)裁剪其流式传输瓦片的一部分。那么要怎么处理空洞呢?我们可以使用几何体脚本(Geometry Script)以程序化方式来填充!

内容及原因

对基于真实世界的虚幻引擎项目来说,Cesium for Unreal是非常实用的插件,可以提供即时的建筑和地形环境。Cesium的一个新功能是可以使用谷歌地球的3D瓦片,这意味着我们可以在虚幻引擎中轻松使用谷歌提供的海量3D数据。

在建筑施工领域,也是我付出最多的领域,这种数据最实际的用途就是为当前正在设计或构建的建筑提供视觉背景。这能让每位参与者清晰地了解到新建筑如何与周围地形相互融合,不需要花大量时间和精力再现或捕获世界的大片区域。在本教程中,我们将假设需要混合多个不同的Datasmith建筑和一个Cesium瓦片集。

Cesium提供了一种简单实用的工具,它可以裁剪或剔除任意被称为地图多边形瓦片集的一部分。你可以按照他们的教程在几分钟内切出一块地。

最近我对自动化和脚本编辑非常感兴趣,一直在寻找机会扩展地图多边形Actor,让它可以在更大的系统中工作。如果我需要裁剪大量地块,将每个地块和一堆元数据相关联,那样管理松散的地图Actor就意味着失去这种机会。以下是我的实验成果:

    1. 一个可以快速拖入场景的自定义Actor,

    2. 自动将自身分配为主瓦片集的剪裁多边形,

    3. 用实心几何体填充裁剪体积,

    4. 包含关联Datasmith导入的相对原点,

    5. 保存之后可以通过脚本引用的标识信息。

构建的自定义工具可以在教程底部下载

图片

项目蓝图下载:

https://epicgames.ent.box.com/s/q42gsui07kj8jg0wm7j8k8msfpt5apzp

Cesium for Unreal:

https://www.unrealengine.com/marketplace/en-US/product/87b0d05800a545d49bf858ef3458c4f7?sessionInvalidated=true

先决条件

本教程不涉及Cesium的初始设置。我将假设你已经拥有一个包含Cesium瓦片集的项目,并且有可以裁剪一部分瓦片集的Cesium地图多边形Actor。这意味着你的瓦片集上将有一个Cesium多边形光栅叠加组件(Cesium PolygonRasterOverlay)和一个关联的材质层键(Material Layer Key)。Cesium文档非常有用,Cesium示例项目也是一个很好的起点。

图片

你还需要启用几何体脚本插件。

获取Cesium Actor

我们正在制作的这个特殊工具必须是可以放在世界中的Actor。你可以右键单击内容浏览器(Content Browser)中的空白区域,选择蓝图类(Blueprint Class->Actor。我把它命名为“BP_CesiumPlotTool”。

图片

这个Actor要做的第一件事是创建一个地图多边形,并将它分配给“Cesium Tileset”。创建两个变量。一个用于“Cesium3D瓦片集(Cesium 3D Tileset”对象引用,一个用于“Cesium地图多边形(Cesium Cartographic Polygon”对象引用。点击眼睛图标,确保我们可以通过细节面板获取这些变量,以便调试。

图片

第一个要求是引用我们想使用的Cesium瓦片集。打开构造脚本(Construction Script。按Ctrl键,将上一步中创建的Cesium Tileset变量拖到图表中,创建“获取(Get”节点。我们要给这个Actor分配一个瓦片集,但我们只想做一次,并且前提是还没分配任何内容。验证变量是否为空的一种快速方法是,右键单击输出引脚并选择转换为有效的GetConvert to Validated Get。这是替代分支节点的一种简洁方法,可以提供一个额外的验证节点。

图片

Is Not Valid引脚创建一个获取类的ActorGet Actor of Class节点,并将Actor类设为Cesium 3D Tileset。然后按Alt键,将同一个Cesium Tileset变量拖到图表中,创建设置(Set)节点,将它连到返回值(Return Value)。这么做是为了检查是否已经有Cesium瓦片集的引用。如果没有,我们要寻找并获取场景中的第一个瓦片集,保存对它的引用。注意,如果想使用多个瓦片集,就要详细说明如何获取所需的瓦片集。

图片

现在我们有一个瓦片集,我们还想要一个可以裁剪瓦片集的多边形。在事件图表(Event Graph中右键单击,创建一个新的自定义事件,命名为“Spawn Cartographic Polygon(生成地图多边形)”。对于这个事件和本教程中创建的其他事件,确保在右侧的细节面板中启用“在编辑器中调用(Call in Editor)”。这能给我们提供一个可以点击的按钮,之后会用到。

图片

图片

再次拖入Cesium Tileset变量并将它转换为有效的Get。然后,我们可以使用打印字符串(Print String警示用户:如果不定义一个瓦片集,该函数的其余部分将无法工作。

图片

如果有瓦片集,我们将继续生成地图多边形。在这个例子中,我希望每块“地”只有一个多边形,所以我要用之前创建的CartographicPolygon变量再添加一个有效的Get。把它连到前面的IsValid引脚上,连接这两项检查。然后,我们再添加一个打印字符串节点,用来警示用户这里已经有一个多边形,但这次是连接IsValid引脚。

图片

Cesium的地图多边形只能用作Actor,不能用作组件。我们可以在蓝图中使用子项Actor组件,但是在这个例子中我并没有这么做,因为这么做常常伴随着兼容性隐患,Cesium瓦片集需要一个常规的Actor引用。创建一个从类生成ActorSpawn Actor From Class节点,并将类设为CesiumCartographicPolygon。我这里通过右键单击变换(Transform)引脚,对它进行了拆分,目的是为了能单独输入获取Actor位置(Get Actor Location节点(只引用自身)和手动设置0.65的缩放(这个值可根据需求修改)。

图片

接下来的任务是确保它能发挥作用!编译蓝图,然后将它从内容浏览器拖到关卡中。在Actor的细节面板(Details panel中应该有一个“默认(Default”栏,其中包含公开变量和一个用于触发自定义事件(生成地图多边形)的按钮。我们的构造脚本应该已找到相关的Cesium Tileset,并保存了对它的引用。点击“Spawn Cartographic Polygon”按钮将生成相应的Actor并保存对它的引用。如果没有,可以检查一下之前所做的工作。如果有效,暂时先删除两个Actor

图片

图片

为了更加清楚明了,我们可以在生成事件后面使用Actor附加到ActorAttach Actor To Actor,确保多边形嵌套在主地块蓝图(Plot Blueprint)下。记得将规则改为保持场景(Keep World,而不是保持相对(Keep Relative)。

图片

开始剔除

我们创建的地图多边形只有在分配给瓦片集的光栅叠加(Raster Overlay)之后,才能发挥作用。我假设你已经给你的瓦片集添加了一个Cesium多边形光栅叠加组件,并且它正在按预期的方式和你的材质层一起工作。我这里有两个,一个用于定义裁剪区域,另一个用于定义水的边界。注意,Cutout叠加组件排在最前面。

图片

回到“Spawn Cartographic Polygon”事件,我们需要找到这个叠加组件并将多边形插入其中。我们将使用按类获取组件(Get Components by Class在瓦片集中搜索每一个Cesium多边形光栅叠加组件,获取列表最前面的组件,然后将生成的地图多边形添加到这个叠加组件的“多边形(Polygons)”数组中。大致如下:

图片

现在测试可能看不出瓦片的视觉差异,需要刷新,为此我们可以创建一个简单的按钮来实现相关程序。新建一个自定义事件,命名为“Refresh Tileset Culling(刷新瓦片集剔除)”,启用“在编辑器中调用”。在这个事件中,我们将使用Cesium Tileset并调用它的刷新瓦片集(Refresh Tileset函数。

图片

如果编译蓝图并将它拖到关卡中,我们可以点击第一个按钮生成嵌套多边形Actor。

图片

点击新建的“Refresh Tileset Culling”按钮将重新加载所有瓦片,剪除地图多边形中的区域!如果要创建多个这样的地块,你就可以看到这种方法可以大幅加快工作流程,而按以往的工作方式通常需要点击很多次,还要选择Actor和组件。

图片

你可以按自己想要的效果修改样条,然后在Plot Actor上点击刷新。

图片

利用几何体脚本进行填充

地球中心有一个空洞总让人觉得不安。如果你自己有几何体可以代替丢失的部分、填充空洞,一定要这么做。除此之外,还有一种有趣的程序化方法可以解决这个问题,就是使用强大的几何体脚本(Geometry Script)插件,它允许你在编辑器中和在运行时定义和修改网格体。

在我们的蓝图中,我们必须添加一个“动态网格体”类型的新组件。我把它重命名为“PlotMesh”。确保这个组件的缩放值与之前生成的地图多边形Actor一致,在这个例子中是0.65。

图片

图片

图片

我们要给这个网格体添加一种可以自定义的材质,再将它保存到另一个材质接口(Material Interface类型(处理材质和材质实例)的公开变量中。我把它命名为“PlotMeshMaterial”。你可以随意设置一种默认材质。我选择了沥青材质。

新建一个自定义事件,命名为“Update Plot Mesh(更新地块网格体)”,确保启用“在编辑器中调用”。我们要做的第一件事是使用配置材质集(Configure Material Set节点,让动态网格体使用保存在变量中的材质。材质集是一个数组;你可以把它想象成包含0、1等一系列材质槽的贴图。我们只有一种材质,因此我们可以用一个变量创建一个数组。

图片

接下来,我们要从动态网格体组件获取动态网格体(Get Dynamic Mesh。如果你刚开始使用几何体脚本,这部分内容可能会让你感到很困惑,但它很有用。这个组件有很多类似于其他场景组件的属性,但这个动态网格体本身是一个包含顶点和所有几何体的数组,这些几何体可以用于各种目的。参阅几何体脚本文档了解详细解释。

我们要确保每次调用这个事件都在动态网格体上使用重置(Reset函数,这样就不会意外积累垃圾信息。

图片

现在我们有一个干净的slate可以为Plot Mesh定义新的几何体。Cesium地图多边形Actor最厉害的地方在于,它可以利用一个标准样条组件来定义它的边缘。几何体脚本中包含可以使用这个样条组件的函数,例如将样条转换为多边形路径(Convert Spline to Poly Path。除此之外,你还要定义一个额外的结构体,它叫做GeometryScriptSplineSamplingOptions(几何脚本样条采样选项)。在这个例子中,我设置的误差容限为0.1,还把样本间距(Sample Spacing)改成了误差容限(Error Tolerance,以便得到匹配样条的精确边界。

得到的“Poly Path”自动转换为一个“Polygon Vertices(多边形顶点)”数组,用于附加简单挤压多边形(Append Simple Extrude Polygon节点。几何体脚本中有很多“附加”函数,作用基本都是定义原始形状并将它们合并到动态网格体中。这个函数会使用样条形状,从该位置向下或向上绘制一个3D网格体。我将变换位置(Transform Location设为-600,将高度(Height设为598,这样就可以得到一个嵌入地面的网格体,但不会和放在上面的内容发生深度冲突。

图片

定义好这个3D形状后,我们要计算分割法线(Compute Split Normals,确保硬边看起来像硬边。在这里,缩放网格体UVScale Mesh UV有助于我选择的材质,你也可以在你的材质中使用世界对齐纹理(World Aligned Textures)。

图片

再次编译我们的蓝图并检查关卡,现在空洞就填充好了。每次修改样条之后,你都可以点击“Update Plot Mesh”按钮刷新几何体。改变公开的材质预设会立刻反映在网格体上。你可以将自定义事件添加到构造脚本中,让它始终保持最新状态。

图片

图片

最后润色

除了基本功能,我还为Actor添加了其他实用程序。

如果你想删除其中一个Actor并重新开始,可能会遇到一些问题。删除Plot Actor不会删除地图多边形Actor。删除多边形Actor会弹出破坏瓦片集引用的警告。因此,我创建了一个自定义事件叫做“Remove Plot(移除地块)”,它会先从瓦片集的光栅叠加中移除地图多边形,然后删除多边形,再刷新瓦片集和填充空洞,最后销毁自己,不留痕迹。

图片

我还给Plot Name(地块名称)添加了一个公开字符串变量。这意味着我可以创建数百个这样的区域,为每个区域分配唯一标识符。在自动化系列教程中我会沿用这个蓝图,这个名称也将在之后派上用场。

图片

作为这个工具的一部分,我还在蓝图中添加了一个名为“PlotOriginPoint(地块原点)”的公告板组件(Billboard Component。这个公告板可以独立定位,这样就可以将它的世界位置作为将用于相应土地的Datasmith文件的原点。

图片

我还创建了一个公开变量、一个关卡实例(Level Instance)数组,并且还创建了一个名为“Update Plot Origin(更新地块原点)”的自定义事件,它会把所有指定的关卡实例移动到公告板的位置。这么做是为了在需要重新定位导入的建筑时,可以推开公告板,重新放置1个“Actor”,而不是获取和移动从Datasmith导入的10000个Actor。

图片

将几何体与山丘相匹配

从俯视图中可以看到,前面创建的用于生成地块网格体的代码可以将顶点和地图多边形完美对齐。这是一种非常安全可靠的方法,也是本教程使用的默认方法。但现实世界中的大多数地形并不平坦!在倾斜的地方使用这种技术时,前后会产生间隙。

图片

图片

解决这个问题的逻辑稍微有点复杂。在对地图样条进行采样,获得它的形状并按这个形状创建出多边形之后,我们要遍历之前创建的每个顶点,跟踪Z轴上最近的网格体表面。这样可以有效地“收缩包裹”地形。

我还添加了额外的曲面细分和一个选项,这个选项可以平滑生成的网格体,创建更自然的效果。

图片

图片

这种技术对极端地形来说也很有效,但并不完美。增加样条点的数量有利于更好地匹配地形形状,但在非常粗糙的摄影测量中追踪这些点,效果往往不太理想。

图片

下次加载关卡时,可能没有这个动态网格体,或者它可能会尝试收缩包裹到一个尚未完全加载的Cesium瓦片集中。获得满意的地块网格体之后,你就可以使用虚幻引擎5中的建模模式(Modeling Mode,按你想要的方式手动处理这个网格体。如果你想把它保存为永久性资产(强烈推荐!),只需要使用变换中的转换(Convert工具,点击底部的“接受(Accept)”。

图片

自定义模型的默认位置是

 

 Content/_GENERATED/<username>

图片

结语

希望这个小工具能帮助你完成项目,或者给你提供一些灵感,帮助你进一步实现概念。如果你有所收获或者有任何疑问,可以在评论区告诉我,但请注意,我无法保证能持续为这个工具或教程提供支持。

这是最终的蓝图,你可以下载这个蓝图并将它添加到你自己的项目中。这个蓝图是在5.2版本中创建的,但你可以在虚幻引擎5的任意版本中重新构建相应的逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值