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

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


对于这类情况,虚幻编辑器提供了一个流程,可以扫描关卡中静态网格体Actor并确定哪些会被完全遮挡——换句话说,哪些Actor是完全看不到的。一旦找到这些Actor,就可以在图层中隔离它们,或将它们从关卡中删除,或者简化它们的几何体以删除内部细节。
这个过程有时被称为 包壳(jacketing)。
外壳开孔
不过,模型外壳通常不是完全封闭的。这些外壳可能包含缝隙或开孔,尽管用户无法通过它们看清内部细节。例如,在这个引擎中,链条穿过了外壳上的开孔:

这种情况下,你可能仍希望隐藏内部细节。因此,在计算哪些网格体会被遮挡时,包壳算法会填补这些缝隙(比如假设这些缝隙被网格体遮挡)。这样,你仍可以剔除外壳内的零件并获得性能提升——即便外壳不是完全封闭的。
你可以设置希望忽略的开孔尺寸上限(小于该尺寸的开孔都会被视为封闭的)。
为了安全起见,你可能会把开孔的阈值设置为一个非常大的值。然而,这个阈值也用于网格体的目标模式(见下文),以便计算静态网格体中的哪些三角形可以被安全删除。如果你将阈值设得过高,则几何体内部的三角形可能无法被充分简化。请将阈值设置得尽可能接近开孔的实际大小。
目标
你可以将包壳操作的结果应用于以下两种目标:关卡中的静态网格体Actor,或静态网格体资产中的几何体。
关卡目标
对关卡目标使用包壳工具时,工具会对选定的一组静态网格体Actor进行遮挡测试。它会从各个角度分析这些Actor的几何体,确定哪些Actor完全无法看到。稍后,它会列出所有被遮挡的Actor。接着,你可以选择如何处理它们。
在虚幻编辑器UI中,你可以:
-
用一个新的组件标签 包壳隐藏(Jacketing Hidden) 来标记被遮挡的Actor。
- 将被遮挡的Actor放在名为 包壳(Jacketing) 的新图层种。
- 通过关闭 隐藏在游戏中的Actor(Actor Hidden in Game) 设置来隐藏被遮挡的Actor。
- 在关卡中移除被遮挡的Actor。
如果你在蓝图或Python脚本中以关卡目标模式运行该工具,它只返回被遮挡的Actor列表,以便你的脚本确定要采取的操作。
如果你有许多小部件,每个部件都由一个单独的静态网格体Actor表示,并且位于具有相对简单的几何体或外壳中,则关卡目标模式是一个不错的选择。
在关卡目标模式中,包壳工具从不修改任何静态网格体资源。它只运行遮挡测试并确定完全遮挡的Actor。
网格体目标
当你在网格体目标(Mesh Target)模式下使用包壳工具时,它会以三角形为基本单位来处理遮挡。在执行遮挡测试后,它会删除静态网格体资产中的所有被遮挡三角形。这能有效地将遮挡网格体删减成一个空壳,删除其内部细节。
当你的外壳或遮挡物拥有复杂的内表面时,或者有多个Actor(几何体相互重叠)时,这是个合适的方案。重叠区域内的所有几何体都会被充分简化。
为避免减损外观品质,包壳工具采用的识别方法相对保守,只会在确保无误的情况下移除三角形。所有可见三角形都会保持原样不变。包壳工具不会对三角形进行二次三角划分或简化。它只会删除不必要的三角形。
例如在下图中,模型内部有一些复杂的几何体,它们在外部始终看不到。用网格体目标模式运行包壳工具后,所有内部细节会被删除。注意,外壳内表面也被移除了,只留一个有外表面的单面几何体。


你可以在 输出日志(Output Log) 面板中查看包壳工具的结果,包括该工具能够删除的三角形数量:
在网格体目标模式下,包壳工具会修改你的静态网格体资产。如果这些资产在关卡其他地方也有,或者在项目其他关卡中也有,那么这些实例都会被更新成新的效果。
项目设置
要使用包壳工具,你必须启用 多边形编辑(Polygon Editing) 插件。
如果你使用了 建筑、工程与施工 类型的模板项目,该插件可能会默认启用。
多边形编辑 插件只支持64位Windows。
关卡视口中的包壳工具
要在关卡视口中应用包壳工具,请执行以下操作:
- 在关卡中选择要在遮挡测试中考虑的静态网格体Actor。你需要选择构成外部外壳的网格体,以及任何内部的网格体。
-
在视口或 世界大纲视图(World Outliner) 中右键点击所有选中的Actor,选择 包壳(Jacketing)。
-
在 移除被遮挡网格体(Remove occluded meshes) 窗口中,设置遮挡测试的敏感水平以及你要影响的目标。
设置(Setting) 说明(Description) 体素精度(Voxel precision) 控制遮挡测试的灵敏度。对于较小的模型,降低此值,以实现更高的精度。
这个设置直接影响碰撞测试的时间和内存需求。从一个相对较大的值开始,然后降低值,直到达到所需的保真度。
开孔最大直径(Gap max diameter) 设置遮挡测试将考虑填充的遮挡体积中的开孔的最大大小。
请勿将此值设置地过低。详情请参阅上面的开孔部分。
操作关卡(Action Level) 决定工具将使用 关卡(Level) 目标还是 网格体(Mesh) 目标。 操作类型(Action Type) 如果你选择影响关卡目标,还可以使用 操作类型(Action Type) 下拉列表来确定应该如何处理包壳工具确定为完全被遮挡的Actor集。详情请参阅上文的关卡目标。 -
点击 继续(Proceed) 开始遮挡测试。
- 如果你选择了“网格体目标(Mesh Target)”模式,修改后的网格体将被标记为“已修改”。如果要保留更改效果,请在关闭虚幻编辑器前保存它们。
编辑器脚本中的包壳
你可以在蓝图和Python中执行关卡视口(以及世界大纲视图)提供的相同包壳操作。
先决条件: 如果你尚未进行此操作,则需要安装 编辑器脚本工具插件(Editor Scripting Utilities Plugin)。有关详情,请参阅脚本化和自动化编辑器。
要使用这些节点,你的蓝图类必须派生自仅编辑器类,例如 PlacedEditorUtilityBase 类。有关详情,请参阅使用蓝图脚本化编辑器。
你需要使用的主要蓝图节点是 网格体处理(Mesh Processing)> 网格体Actor(Mesh Actor)> 简化装配件(Simplify Assembly)。

你需要为这个节点提供两个输入:
- 包含当前关卡中你希望在遮挡测试期间考虑的所有Actor的数组。
-
为遮挡测试设置参数的 JacketingOptions 对象。要设置这些对象之一,请执行以下操作:
-
点击 我的蓝图(My Blueprint) 面板中的 + 变量(+ Variable) 按钮,为蓝图添加一个新变量。
-
将变量的类型设置为对一个 *网格体特征清除参数对象(Mesh Defeaturing Parameter Object)** 的引用。
-
按住 Control 并将变量拖放到蓝图图表中,以创建获取该变量值的新节点。
-
从新变量节点的输出端口拖动,并从 变量(Variables) 列表中选择需要修改的设置的 设置(Set) 节点。
-
如果你将 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)