查找和删除完全遮挡的网格体

介绍如何移除和简化被关卡对象完全遮挡的几何体,以便提升渲染性能。

Choose your operating system:

Windows

macOS

Linux

想要提升实时3D应用的渲染性能,一种常用手段是降低每帧需要绘制的对象数量。通常,摄像机不会同时拍摄到3D场景中的所有对象。所有被遮挡的对象(因为被其他对象遮挡而无法出现在摄像机视图中的对象),都可以不用被渲染,且不会对最终画面产生影响,同时还能提升性能。

jacketing-banner.png

虚幻引擎提供了多种内置方法来移除被遮挡的网格体,例如剔除摄像机视锥体之外的网格体或摄像机远处的网格体。然而,在某些情况下,比如某个网格体位于另一个网格体的边框中时,虚幻引擎可能无法在运行时高效识别哪些网格体是被遮挡的。这类情况在CAD数据中很常见——这些数据通常包含许多组装零件,其中的很多细小零件都隐藏在外壳中。如果这些零件在渲染时不会显示,那么可以隐藏它们,以便提升渲染性能。假如你将一辆完整的汽车模型导入虚幻引擎,并且用户无法查看汽车外壳内的细节,那么就没有理由花费资源渲染车身内的零件。

例如,下图中的引擎包含542个零件(静态网格体Actor)。然而,其中321个完全位于外壳内,永远不会被摄像机拍摄到。在关卡中移除这类被遮挡物有助于加快渲染剩余的几何体,并且不会影响外观。

Complete engine, 542 Actors

321 fully occluded Actors

对于这类情况,虚幻编辑器提供了一个流程,可以扫描关卡中静态网格体Actor并确定哪些会被完全遮挡——换句话说,哪些Actor是完全看不到的。一旦找到这些Actor,就可以在图层中隔离它们,或将它们从关卡中删除,或者简化它们的几何体以删除内部细节。

这个过程有时被称为 包壳(jacketing)

外壳开孔

不过,模型外壳通常不是完全封闭的。这些外壳可能包含缝隙或开孔,尽管用户无法通过它们看清内部细节。例如,在这个引擎中,链条穿过了外壳上的开孔:

Gaps in the occluding meshes

这种情况下,你可能仍希望隐藏内部细节。因此,在计算哪些网格体会被遮挡时,包壳算法会填补这些缝隙(比如假设这些缝隙被网格体遮挡)。这样,你仍可以剔除外壳内的零件并获得性能提升——即便外壳不是完全封闭的。

你可以设置希望忽略的开孔尺寸上限(小于该尺寸的开孔都会被视为封闭的)。

为了安全起见,你可能会把开孔的阈值设置为一个非常大的值。然而,这个阈值也用于网格体的目标模式(见下文),以便计算静态网格体中的哪些三角形可以被安全删除。如果你将阈值设得过高,则几何体内部的三角形可能无法被充分简化。请将阈值设置得尽可能接近开孔的实际大小。

目标

你可以将包壳操作的结果应用于以下两种目标: 关卡中的静态网格体Actor ,或 静态网格体资产中的几何体

关卡目标

对关卡目标使用包壳工具时,工具会对选定的一组静态网格体Actor进行遮挡测试。它会从各个角度分析这些Actor的几何体,确定哪些Actor完全无法看到。稍后,它会列出所有被遮挡的Actor。接着,你可以选择如何处理它们。

在虚幻编辑器UI中,你可以:

  • 用一个新的组件标签 包壳隐藏(Jacketing Hidden) 来标记被遮挡的Actor。
    Jacketing Hidden tag

  • 将被遮挡的Actor放在名为 包壳(Jacketing) 的新图层种。

  • 通过关闭 隐藏在游戏中的Actor(Actor Hidden in Game) 设置来隐藏被遮挡的Actor。

  • 在关卡中移除被遮挡的Actor。

如果你在蓝图或Python脚本中以关卡目标模式运行该工具,它只返回被遮挡的Actor列表,以便你的脚本确定要采取的操作。

如果你有许多小部件,每个部件都由一个单独的静态网格体Actor表示,并且位于具有相对简单的几何体或外壳中,则关卡目标模式是一个不错的选择。

在关卡目标模式中,包壳工具从不修改任何静态网格体资源。它只运行遮挡测试并确定完全遮挡的Actor。

网格体目标

当你在网格体目标(Mesh Target)模式下使用包壳工具时,它会以三角形为基本单位来处理遮挡。在执行遮挡测试后,它会删除静态网格体资产中的所有被遮挡三角形。这能有效地将遮挡网格体删减成一个空壳,删除其内部细节。

当你的外壳或遮挡物拥有复杂的内表面时,或者有多个Actor(几何体相互重叠)时,这是个合适的方案。重叠区域内的所有几何体都会被充分简化。

为避免减损外观品质,包壳工具采用的识别方法相对保守,只会在确保无误的情况下移除三角形。所有可见三角形都会保持原样不变。包壳工具不会对三角形进行二次三角划分或简化。它只会删除不必要的三角形。

例如在下图中,模型内部有一些复杂的几何体,它们在外部始终看不到。用网格体目标模式运行包壳工具后,所有内部细节会被删除。注意,外壳内表面也被移除了,只留一个有外表面的单面几何体。

包含大量内部细节的零件

"包壳"后的效果

你可以在 输出日志(Output Log) 面板中查看包壳工具的结果,包括该工具能够删除的三角形数量:

在网格体目标模式下,包壳工具会修改你的静态网格体资产。如果这些资产在关卡其他地方也有,或者在项目其他关卡中也有,那么这些实例都会被更新成新的效果。

项目设置

要使用包壳工具,你必须启用 多边形编辑(Polygon Editing) 插件。

如果你使用了 建筑、工程与施工 类型的模板项目,该插件可能会默认启用。

多边形编辑 插件只支持64位Windows。

关卡视口中的包壳工具

要在关卡视口中应用包壳工具,请执行以下操作:

  1. 在关卡中选择要在遮挡测试中考虑的静态网格体Actor。你需要选择构成外部外壳的网格体,以及任何内部的网格体。

  2. 在视口或 世界大纲视图(World Outliner) 中右键点击所有选中的Actor,选择 包壳(Jacketing) 情境菜单中的包壳

  3. 移除被遮挡网格体(Remove occluded meshes) 窗口中,设置遮挡测试的敏感水平以及你要影响的目标。 包壳设置

    设置(Setting)

    说明(Description)

    体素精度(Voxel precision)

    控制遮挡测试的灵敏度。对于较小的模型,降低此值,以实现更高的精度。

    这个设置直接影响碰撞测试的时间和内存需求。从一个相对较大的值开始,然后降低值,直到达到所需的保真度。

    开孔最大直径(Gap max diameter)

    设置遮挡测试将考虑填充的遮挡体积中的开孔的最大大小。

    请勿将此值设置地过低。详情请参阅上面的 开孔 部分。

    操作关卡(Action Level)

    决定工具将使用 关卡(Level) 目标还是 网格体(Mesh) 目标。

    操作类型(Action Type)

    如果你选择影响关卡目标,还可以使用 操作类型(Action Type) 下拉列表来确定应该如何处理包壳工具确定为完全被遮挡的Actor集。详情请参阅上文的 关卡目标

  4. 点击 继续(Proceed) 开始遮挡测试。 继续

  5. 如果你选择了"网格体目标(Mesh Target)"模式,修改后的网格体将被标记为"已修改"。如果要保留更改效果,请在关闭虚幻编辑器前保存它们。

编辑器脚本中的包壳

你可以在蓝图和Python中执行关卡视口(以及世界大纲视图)提供的相同包壳操作。

先决条件: 如果你尚未进行此操作,则需要安装 编辑器脚本工具插件(Editor Scripting Utilities Plugin) 。有关详情,请参阅 脚本化和自动化编辑器

选择实现方法:

Blueprints

Python

要使用这些节点,你的蓝图类必须派生自仅编辑器类,例如 PlacedEditorUtilityBase 类。有关详情,请参阅 使用蓝图脚本化编辑器

你需要使用的主要蓝图节点是 网格体处理(Mesh Processing)> 网格体Actor(Mesh Actor)> 简化装配件(Simplify Assembly)

简化装配件(Simplify Assembly)节点

你需要为这个节点提供两个输入:

  • 包含当前关卡中你希望在遮挡测试期间考虑的所有Actor的数组。

  • 为遮挡测试设置参数的 JacketingOptions 对象。要设置这些对象之一,请执行以下操作:

    1. 点击 我的蓝图(My Blueprint) 面板中的 + 变量(+ Variable) 按钮,为蓝图添加一个新变量。

      添加变量

    2. 将变量的类型设置为对一个 *网格体特征清除参数对象(Mesh Defeaturing Parameter Object)** 的引用。

      包壳选项对象引用

    3. 按住 Control 并将变量拖放到蓝图图表中,以创建获取该变量值的新节点。

      拖动并放置变量

    4. 从新变量节点的输出端口拖动,并从 变量(Variables) 列表中选择需要修改的设置的 设置(Set) 节点。

      Drag right for the Jacketing Options API

如果你将 JacketingOptions 设置为使用关卡目标模式,则 在网格体Actor上应用包壳(Apply Jacketing on Mesh Actors) 节点将返回一个数组,其中包含从所有角度都无法看到的所有静态网格体Actor。然后,你可以遍历这个列表来处理Actor。

例如,下面的蓝图图表收集了关卡中的所有静态网格体Actor,使用关卡目标运行包壳遮挡测试,并在视口和世界大纲视图中选择这些Actor。

你可以通过调用"unreal.MeshProcessingLibrary.apply_jacketing_on_mesh_actors()"函数在当前关卡的任何一组静态网格体Actor上运行遮挡测试和包壳流程。你将需要向此函数传递两个参数:

  • 包含当前关卡中你希望在遮挡测试期间考虑的所有Actor的数组。

  • 为遮挡测试设置参数的 unreal.JacketingOptions 对象。通过调用"unreal.JacketingOptions()"创建该对象的新实例,然后设置要调整的属性。

如果将 unreal.JacketingOptions.target 设置为 unreal.JacketingTarget.LEVEL ,此函数返回它认为完全被遮挡的所有网格体的数组。你可以处理这个列表,对它们执行任何操作。

# 获取关卡中的Actor列表 -- 在本例中是
# 用户已在视口中选择的那些Actor。
actors = unreal.EditorLevelLibrary.get_selected_level_actors()

# 创建一个新对象来保存包壳选项。
options = unreal.JacketingOptions()

# 设置体素网格的分辨率(以厘米为单位)。
options.accuracy = 0.2

# 设置可以考虑填充的最大开孔,单位为厘米。
options.merge_distance = 3

# 要采取操作的目标:unreal.JacketingTarget.LEVEL 或 unreal.JacketingTarget.MESH
options.target = unreal.JacketingTarget.LEVEL

# 执行包壳操作。
# 作用于关卡目标会使函数返回被遮挡的Actor列表。
occluded = unreal.MeshProcessingLibrary.apply_jacketing_on_mesh_actors(actors, options)

# 对被遮挡的Actor执行一些操作。
# 例如,这个循环只是简单地从关卡上删除每个被遮挡的Actor。
for a in occluded:
a.destroy_actor()

如果将"unreal.JacketingOptions.target"属性设置为"unreal.JacketingTarget.MESH",则函数不返回值。它只会删除它认为从外部不可见的任何三角形。

例如:

# 获取关卡中的Actor列表 -- 在本例中是
# 用户已在视口中选择的那些Actor。
actors = unreal.EditorLevelLibrary.get_selected_level_actors()

# 创建一个新对象来保存包壳选项。
options = unreal.JacketingOptions()

# 设置体素网格的分辨率(以厘米为单位)。
options.accuracy = 0.2

# 设置可以考虑填充的最大开孔,单位为厘米。
options.merge_distance = 3

# 要采取操作的目标:unreal.JacketingTarget.LEVEL 或 unreal.JacketingTarget.MESH
options.target = unreal.JacketingTarget.MESH

# 执行包壳操作。
# 作用于网格体目标会使函数将更改直接应用到
# 从外部不可见的静态网格体资源的几何体。

unreal.MeshProcessingLibrary.apply_jacketing_on_mesh_actors(actors, options)

欢迎帮助改进虚幻引擎文档!请告诉我们该如何更好地为您服务。
填写问卷调查
取消