Unreal Engine 移动端优化:让 MetaHumans 和 PCG 在 60 FPS 下运行
概要
本文详细探讨了在 Unreal Engine 中针对移动平台优化 MetaHumans、PCG 和 Substrate 材质等次世代特性的实用方案。通过修改 DefaultEngine.ini 限制骨骼 skinning 数量、使用 hair cards 替代 strand 渲染,以及在编辑器中将 PCG 预烘焙为 HISM 实例,可显著降低移动端 GPU 与 CPU 的负载。此外,文章还介绍了利用材质质量开关简化着色模型,以及通过 [horizOn](https://horizon.pm) 解决移动端网络波动和 multiplayer backend 架构难题的最佳实践。
移动端壁垒:将次世代视觉效果带入移动端
您的 PC 项目在开发工作站上能以完美的 120 FPS 流畅运行,但一旦测试移动端打包版本,帧率就会骤降至个位数,目标手机发热严重,且 GPU 会因为 skinning 缓冲区溢出而崩溃。像 MetaHumans、Procedural Content Generation (PCG) 和 Substrate 材质这些高端特性在 PC 和主机上看起来令人惊叹,但它们也是让移动设备“瘫痪”的元凶。移动端 GPU 和 CPU 在极度受限的散热和功耗包络内运行,其中 memory bandwidth 是极度珍贵的资源。将这些次世代特性适配到移动端部署,绝非仅仅勾选一个配置框那么简单;它需要对骨骼 skinning 限制、groom 结构、程序化 baking 工作流以及材质 shader 复杂度有深入且系统的理解。
GPU Skinning 与骨骼限制的挑战
移动端上的 GPU Skinning 瓶颈
在发送到 GPU 之前,带蒙皮的 skeletal meshes 会被拆分为顶点和骨骼的 chunks。每个 chunk 都在单个 draw call 中进行处理,而移动端 GPU 对其能同时进行 skinning 的骨骼矩阵数量有着严格的硬件限制。这一限制由 vertex shader 中可用的 uniform vectors 数量决定。默认的 MetaHuman 角色骨骼包含超过 600 根骨骼,这很容易超出移动端的硬件限制,导致渲染错误、顶点撕裂或 GPU 完全挂起。
为了绕过这一硬件限制,您必须强制引擎对 skeletal mesh chunks 进行分区,使得没有单个 draw call 会引用超过特定数量的骨骼。这可以通过调整 skinning 兼容性设置来实现。如果您不应用此配置,您的角色模型将无法在 Android 和 iOS 设备上正确进行 skinning,从而显示为静态或严重扭曲的网格体(meshes)。
配置 DefaultEngine.ini
要解决骨骼 skinning 限制,您必须修改项目的 DefaultEngine.ini 配置文件。在项目根目录的 Config 文件夹中找到该文件。在 [ConsoleVariables] 部分下,添加以下行:
[ConsoleVariables]
Compat.MAX_GPUSKIN_BONES=75
这个控制台变量指示 shader 编译器将每个 skinning chunk 的最大骨骼数量限制为 75。对于旧款或中端移动端 GPU 来说,这是一个严格的硬件限制。请记住,将此值设置得更低会强制 skeletal mesh 编译器将网格体拆分为更多数量的 chunks。虽然这解决了渲染兼容性问题,但更多的 chunks 意味着更多的 draw calls,这可能会将性能瓶颈转移到您的 CPU render thread 上。
减少骨骼数量与组件剥离(Component Stripping)
对于不需要完整面部表情的背景角色或非玩家角色 (NPC),您应该对骨骼进行精简(strip)。例如,移除手指、脚趾和面部表情骨骼可以将角色的总骨骼数量从 600+ 减少到 75 以下。这使得角色网格体可以作为单个 chunk 进行渲染,而不会增加 draw call 的数量。
如果您要为您的 Multiplayer 游戏部署 Dedicated Server,那么客户端优化只是成功的一半。您还需要通过完全剥离渲染资产来优化服务器端性能。请查看我们关于如何掌握 Unreal Engine 专属服务器资产剥离的逐步指南以减少服务器内存开销并优化 CPU 性能。
优化 MetaHuman 头发:Groom Strands 与 Hair Cards 的较量
Strand 渲染的开销
Epic 的 groom strand 渲染技术能够动态绘制单根发丝曲线。虽然这可以在高端桌面 GPU 上呈现出高度精细的头发,但其开销极其昂贵。Strand 渲染依赖于 compute shader 通道来进行 depth sorting 和 shadow map 生成,这会消耗大量的 pixel fill rate 和 memory bandwidth。
在移动设备上,strand 渲染要么完全不受支持,要么运行开销不可接受——通常仅单个角色的头发就会消耗超过 15ms 的 GPU 时间。移动端 GPU 缺乏在每帧对数十万条独立发丝曲线进行排序和着色所需的原始 memory bandwidth。
实现 Hair Cards
解决方案是将基于 strand 的 grooms 替换为 hair cards。Hair cards 使用平坦、简化的多边形网格体来表示头发,并映射预渲染的头发纹理。这种方法与移动端的 forward render path 高度兼容。
要实现 hair cards,请打开 MetaHuman Creator 并确保为您的角色生成了基于 card 的 groom LOD。在现代移动芯片(如 Apple A15 或 Snapdragon 8 Gen 1)上,将基于 strand 的头发替换为基于 card 的 grooms 可以将单个角色的头发渲染时间从大约 18.2ms 降低到 0.9ms。这笔巨大的开销节省使您能够将渲染预算分配给游戏玩法元素或场景细节。
禁用 post-process animation blueprints
MetaHumans 使用 post-process animation blueprints 来驱动次级骨骼运动、矫正肌肉形状和关节动力学。虽然这在 PC 上能增加逼真的皮肤运动,但它每帧都会在 CPU 上运行复杂的骨骼计算。在移动端,这种 CPU 开销很容易使 game thread 陷入瓶颈。
您可以在移动设备上禁用 post-process animation blueprints,以回收 CPU 周期。这可以通过在 skeletal mesh 组件上设置 bDisablePostProcessAnims = true 来实现。在标准移动端硬件上,禁用这些后处理可以节省高达 4.5ms 的 CPU 帧时间。
针对移动端环境优化 PCG
运行时 PCG 执行的开销
Procedural Content Generation (PCG) 框架允许您通过基于规则和体积散射 static meshes、植被和 actors 来动态丰富您的环境。然而,在移动端 CPU 上运行时执行 PCG graphs 会导致严重的性能卡顿。在关卡加载或玩家生成期间,典型的运行时图表生成会使 game thread 冻结 1.5 到 3 秒。
在 Multiplayer 游戏中,这种卡顿是危险的;它可能导致 packet loss 并触发客户端-服务器状态去同步。为了保持高性能,您必须在编辑器中 pre-bake 您的 PCG graphs。这会在打包游戏之前将程序化实例转换为静态几何体。
逐步 PCG 烘焙工作流
- 选择 PCG 体积:在 Unreal Editor 视口中,选择包含环境元素的 PCG 体积。
- 生成并检查:在体积的细节面板中,点击 Generate 以预览资产的程序化放置。
- 导出为 Actor:在 PCG 实用工具菜单中找到 Export to Actor 选项。
- 选择实例化网格体:选择 Hierarchical Instanced Static Mesh (HISM) 作为目标格式。这组实例针对移动端 GPU 进行了高度优化,因为它可以在单个 GPU draw call 中绘制所有相同的网格体。
- 清除图表:导出后,将 PCG 体积的生成触发器设置为 Editor Only 或清除该体积。这可以防止运行时引擎尝试重新创建该图表。
动态 HISM culling 与 streaming
一旦将 PCG 实例 pre-bake 进 HISM,您就必须配置它们的 culling 距离。HISM 支持逐实例 culling,这意味着超出相机特定距离的实例将不会被绘制。在 HISM 组件的细节面板中设置 Start Cull Distance 和 End Cull Distance。对于移动端,建议将 culling 距离设置为 5000 到 8000 个单位,以将总可见多边形数量控制在移动端 GPU 的预算范围内。
生产级 C++ 优化脚本
为了在运行时自动执行这些优化,您可以编写一个自定义辅助类。以下 C++ 代码演示了如何在移动设备上生成 MetaHuman 时,检查目标平台、以编程方式强制使用低 LOD、禁用 post-process animation blueprints,以及强制 groom 组件使用基于 card 的渲染。您可以在类似 UMetaHumanMobileOptimizer 的类中实现此功能:
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "GroomComponent.h"
#include "MetaHumanMobileOptimizer.generated.h"
UCLASS()
class MYPROJECT_API UMetaHumanMobileOptimizer : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Optimization")
static void OptimizeMetaHumanForMobile(AActor* MetaHumanActor)
{
if (!MetaHumanActor)
{
return;
}
// Apply optimizations exclusively on Android and iOS platforms
#if PLATFORM_ANDROID || PLATFORM_IOS
TArray<USkeletalMeshComponent*> SkeletalComponents;
MetaHumanActor->GetComponents<USkeletalMeshComponent>(SkeletalComponents);
for (USkeletalMeshComponent* MeshComp : SkeletalComponents)
{
if (MeshComp)
{
// Force a low LOD (LOD 3 or 4) to bypass dense meshes
MeshComp->SetMinLOD(3);
MeshComp->SetForcedLOD(3);
// Disable expensive post-process animation blueprints
MeshComp->bDisablePostProcessAnims = true;
// Adjust animation tick rate to only calculate when visible
MeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
// Strip physics asset to avoid CPU collision overhead on cosmetic joints
MeshComp->SetPhysicsAsset(nullptr);
}
}
TArray<UGroomComponent*> GroomComponents;
MetaHumanActor->GetComponents<UGroomComponent>(GroomComponents);
for (UGroomComponent* GroomComp : GroomComponents)
{
if (GroomComp)
{
// Force the groom to use card rendering instead of strands
GroomComp->SetUseCards(true);
}
}
#endif
}
};
此辅助函数可以从您角色的 BeginPlay 事件中调用,或者在生成 MetaHuman actor 后立即调用。通过使用 conditional compilation macros (PLATFORM_ANDROID || PLATFORM_IOS),编译器会从 PC 和主机版本中剥离这些覆盖设置,从而使您能够自动维持跨平台的视觉保真度。
针对移动端 GPU 适配 Substrate 材质
什么是 Substrate?
Substrate 用模块化、多层材质框架取代了传统的 Unreal Engine 着色模型。Substrate 允许开发人员叠加多个 shading slabs(例如,直接在粗糙的金属层上放置一层有光泽的透明涂层)。虽然 Substrate 对于高端电影级资产非常出色,但它给移动端渲染器带来了严重的性能障碍。
移动端 GPU 严重依赖 tiled rendering 架构,其中 GPU 核心与 on-chip frame buffer 之间的内存总线是主要瓶颈。复杂的 Substrate 材质增加了写入 frame buffer 的每像素字节数 (BPP),从而导致 thermal throttling 和帧率下降。
通过质量级别开关管理材质复杂度
为了在移动端保持 Substrate 材质的高效运行,您应该在 Material Editor 中使用 Material Quality Level Switch 和 Feature Level Switch 节点。这些节点允许您根据目标平台简化材质图表。
对于移动端平台,通过绕过复杂的多层 Substrate 混合来简化图表。相反,回退到接近资产视觉风格的单个 slab。通过将材质节点路由到质量开关,您可以将写入带宽从每像素 32 字节减少到标准的每像素 8 字节,从而降低设备运行温度并获得稳定的帧率。
移动端优化的 5 个可行最佳实践
- 将所有 PCG 图表 pre-bake 为 HISM:不要在运行时在客户端执行 PCG 图表。在编辑器开发期间将图表 pre-bake 为 Hierarchical Instanced Static Meshes (HISM),并配置合适的 start/end cull 距离。
- 全局限制骨骼数量:在项目的 DefaultEngine.ini 文件中添加
Compat.MAX_GPUSKIN_BONES=75,以确保 skeletal meshes 在移动端 GPU 上正确渲染,而不会触发缓冲区溢出。 - 排他性地使用基于 card 的 grooms:为移动端配置文件禁用基于 strand 的 grooms。基于 card 的头发渲染可将每个角色的 GPU 帧时间从 18ms 降低到 1ms 以下。
- 利用 Material Quality Switches:在 Substrate 材质中实现 Material Quality Level Switch 节点,以在移动端平台上将复杂的着色层简化为单个 slab,从而降低 GPU 带宽。
- 禁用 post-process animation blueprints:在移动端将角色骨骼组件的
bDisablePostProcessAnims = true,以回收 game thread 上宝贵的 CPU 周期。
Multiplayer 与 backend 的平衡
渲染之外:移动端网络优化
在开发跨平台 Multiplayer 游戏时,客户端优化只是其中的一部分。移动设备经常遇到网络波动,在蜂窝数据(5G/4G)和 Wi-Fi 之间切换。这些波动会引入 packet loss、jitter 和高 latency spikes。
如果您的网络同步代码不够健壮,这些 latency spikes 可能会触发 Unreal Engine multiplayer 同步缺陷,导致 actor replication 失去同步并破坏 world state。在移动端网络限制下管理 multiplayer 状态是一个复杂的问题,需要弹性的网络驱动程序、delta compression 以及 server-authoritative reconciliation。
使用 horizOn 卸载基础设施负担
自行构建和维护一个弹性的 multiplayer backend 是一项巨大的工程。您需要配置 load balancers、管理全球数据库复制、实现 matchmaking 逻辑并处理移动端计费。这项基础设施工作可能需要数月的开发时间,并且需要持续的维护。
通过 horizOn,这些 backend 服务已预先配置好。horizOn 为游戏开发人员开箱即用地提供了会话 matchmaking、低延迟状态同步、数据库持久化和跨平台身份验证。这使您能够专注于优化客户端和打磨游戏循环,而不是管理服务器集群和数据库可扩展性。
结论与后续步骤
针对移动端优化 MetaHumans 和 PCG 等次世代特性,需要对渲染预算、skeletal mesh 编译和材质着色复杂度进行严格控制。通过 pre-baking 程序化资产、限制骨骼数量上限以及使用基于 card 的头发渲染,您可以在掌上设备上提供高保真度的跨平台体验。
准备好扩展您的 multiplayer backend 了吗?免费试用 horizOn 或查看 API docs,了解如何简化您的基础设施,并保持 PC、主机和移动端玩家的同步。
来源:Tutorial: Optimizing Next Gen Features for Mobile Game Development