Voltar ao Blog

Otimização Mobile na Unreal Engine: Fazendo MetaHumans e PCG Rodarem a 60 FPS

Publicado em 19 de junho de 2026
Otimização Mobile na Unreal Engine: Fazendo MetaHumans e PCG Rodarem a 60 FPS

Em resumo

Este artigo aborda a otimização de recursos de próxima geração, como MetaHumans e PCG, para garantir uma taxa constante de 60 FPS em dispositivos mobile na Unreal Engine. Ele detalha técnicas práticas como a limitação de bones por chunk de skinning no DefaultEngine.ini, a substituição de groom strands por hair cards e o pre-bake de grafos de PCG em HISMs. Por fim, apresenta um script helper em C++ para automatizar o processo de spawn e discute a importância de otimizar a camada de rede e o Backend para evitar bugs de sincronização em jogos Multiplayer.

A Barreira Mobile: Trazendo Visuais Next-Gen para Mobile

Seu belo projeto de PC roda a impecáveis 120 FPS na sua workstation de desenvolvimento, mas no momento em que você testa o build mobile, a taxa de quadros despenca para um dígito, o aparelho celular esquenta e a GPU sofre crash devido a um skinning buffer overflow. Recursos high-end como MetaHumans, Procedural Content Generation (PCG) e materiais Substrate parecem incríveis no PC e nos consoles, mas são notórios por colocar dispositivos mobile de joelhos. GPUs e CPUs mobile operam em envelopes térmicos e de consumo de energia altamente restritos, onde a largura de banda de memória é um recurso precioso. Adaptar esses recursos de próxima geração para deploy mobile não é apenas uma questão de marcar uma caixa de configuração; exige um entendimento profundo e sistemático dos limites de bone skinning, estruturas de groom, fluxos de baking de PCG e complexidades de material shading.

O Desafio de GPU Skinning e Limite de Bones

O Gargalo de GPU Skinning no Mobile

Skeletal meshes com skin são divididas em chunks de vértices e bones antes de serem enviadas para a GPU. Cada chunk é processado em um único draw call, e as GPUs mobile têm um limite estrito de hardware para o número de matrizes de bones que podem processar via skinning simultaneamente. Essa limitação é definida pelo número de uniform vectors disponíveis para o vertex shader. O esqueleto padrão de um personagem MetaHuman contém mais de 600 bones, o que excede facilmente os limites mobile e leva a erros de renderização, vertex tearing ou travamentos completos da GPU (GPU hangs).

Para contornar essa restrição de hardware, você deve forçar a engine a particionar os chunks da skeletal mesh de modo que nenhum draw call individual referencie mais do que um número específico de bones. Isso é feito ajustando as configurações de compatibilidade de skinning. Se você não aplicar essa configuração, os modelos de seus personagens falharão ao aplicar o skinning corretamente em dispositivos Android e iOS, exibindo meshes estáticas ou severamente distorcidas.

Configurando o DefaultEngine.ini

Para resolver os limites de bone skinning, você deve modificar o arquivo de configuração DefaultEngine.ini do seu projeto. Localize este arquivo na pasta Config na raiz do seu projeto. Sob a seção [ConsoleVariables], adicione a seguinte linha:

[ConsoleVariables]
Compat.MAX_GPUSKIN_BONES=75

Esta console variable instrui o compilador de shader a limitar o número máximo de bones por chunk de skinning em 75. Esse é um limite de hardware rigoroso para GPUs mobile mais antigas ou intermediárias. Tenha em mente que definir um valor mais baixo força o compilador de skeletal mesh a dividir a mesh em um número maior de chunks. Embora isso resolva a compatibilidade de renderização, mais chunks significam mais draw calls, o que pode transferir o gargalo de performance para a thread de renderização da CPU (CPU render thread).

Reduzindo a Contagem de Bones e Component Stripping

Para personagens de fundo ou personagens não jogáveis (NPCs) que não exigem articulação facial completa, você deve reduzir o esqueleto. Por exemplo, remover os dedos das mãos, dos pés e os bones de expressão facial pode reduzir a contagem total de bones de um personagem de mais de 600 para menos de 75. Isso permite que a mesh do personagem seja renderizada como um único chunk, sem inflar os draw calls.

Se você também estiver fazendo o deploy de Dedicated Servers para o seu jogo Multiplayer, as otimizações do lado do cliente são apenas metade do caminho. Você também precisará otimizar a performance do servidor fazendo o stripping completo de assets de renderização. Confira o nosso guia passo a passo sobre como dominar o asset stripping de Dedicated Servers na Unreal Engine para reduzir o overhead de memória do servidor e otimizar a performance da CPU.

Otimizando Cabelo de MetaHuman: Groom Strands vs. Hair Cards

O Custo de Renderização de Strands

A tecnologia da Epic de renderização de groom strands desenha curvas de cabelo individuais de forma dinâmica. Embora isso produza cabelos altamente detalhados em GPUs desktop high-end, é incrivelmente caro. A renderização de strands depende de passes de compute shader para depth sorting e geração de shadow maps, o que consome uma taxa de preenchimento de pixels (pixel fill rate) e largura de banda de memória substanciais.

Em dispositivos mobile, a renderização de strands não é suportada ou roda a um custo inaceitável — frequentemente consumindo mais de 15ms de tempo de GPU apenas para o cabelo de um único personagem. GPUs mobile carecem da largura de banda de memória bruta necessária para ordenar e sombrear centenas de milhares de curvas de cabelo individuais por frame.

Implementando Hair Cards

A solução é trocar grooms baseados em strands por hair cards. Hair cards representam o cabelo usando meshes poligonais planas e simplificadas, mapeadas com texturas de cabelo pré-renderizadas. Essa abordagem é altamente compatível com o forward render path de dispositivos mobile.

Para implementar hair cards, abra o MetaHuman Creator e certifique-se de gerar os LODs de groom baseados em cards para o seu personagem. Substituir cabelo baseado em strands por grooms baseados em cards reduz o tempo de renderização do cabelo de um único personagem de aproximadamente 18,2ms para 0,9ms em um chip mobile moderno como o Apple A15 ou o Snapdragon 8 Gen 1. Essa enorme economia permite que você aloque o orçamento de renderização para elementos de gameplay ou detalhes do cenário.

Desabilitando Post-Process Anim Blueprints

Os MetaHumans usam post-process animation blueprints para conduzir movimentos secundários de bones, formas corretivas de músculos e dinâmica de articulações. Embora isso adicione um movimento de pele realista no PC, executa cálculos esqueléticos complexos na CPU a cada frame. No mobile, esse overhead de CPU pode facilmente gargalar a game thread.

Você pode desabilitar o post-process animation blueprint em dispositivos mobile para recuperar ciclos de CPU. Isso é feito definindo bDisablePostProcessAnims = true nos componentes de skeletal mesh. Desabilitar esses post-processes economiza até 4,5ms de tempo de frame da CPU em hardwares mobile padrão.

Otimizando PCG para Ambientes Mobile

O Overhead da Execução de PCG em Runtime

O framework de Procedural Content Generation (PCG) permite popular seus ambientes dinamicamente distribuindo static meshes, foliage e actors com base em regras e volumes. No entanto, executar gráficos de PCG em runtime em CPUs mobile causa quedas severas de performance (hitches). Uma geração típica de gráfico em runtime pode congelar a game thread por 1,5 a 3 segundos durante o carregamento da fase (level loading) ou spawn de jogadores.

Em um jogo Multiplayer, esse tipo de travamento é perigoso; ele pode levar a perda de pacotes e acionar a dessincronização do estado client-servidor. Para manter uma alta performance, você deve fazer o pre-bake de seus gráficos de PCG no editor. Isso converte as instâncias procedurais em geometria estática antes de empacotar o seu jogo.

Fluxo de Trabalho Passo a Passo para Baking de PCG

  1. Selecionar o PCG Volume: Na viewport do Unreal Editor, selecione o volume de PCG que contém os elementos do seu ambiente.
  2. Gerar e Inspecionar: No painel de detalhes (details panel) do volume, clique em Generate para pré-visualizar o posicionamento procedural dos seus assets.
  3. Exportar para Actor: Localize a opção Export to Actor no menu de utilitários do PCG.
  4. Escolher Instanced Meshes: Selecione Hierarchical Instanced Static Mesh (HISM) como o formato de destino. Esse grupo de instâncias é altamente otimizado para GPUs mobile, pois desenha todas as meshes idênticas em um único draw call de GPU.
  5. Limpar o Gráfico: Após a exportação, defina o gatilho de geração do volume de PCG para Editor Only ou limpe o volume. Isso evita que a engine em runtime tente recriar o gráfico.

Culling Dinâmico e Streaming de HISM

Depois de fazer o bake das suas instâncias de PCG em HISMs, você deve configurar as distâncias de culling deles. HISMs suportam culling por instância, o que significa que instâncias além de uma certa distância da câmera não serão renderizadas. Defina a Start Cull Distance e a End Cull Distance no details panel dos seus componentes HISM. Para dispositivos mobile, uma distância de culling de 5000 a 8000 unidades é recomendada para manter a contagem total de polígonos visíveis dentro do orçamento da GPU mobile.

Script de Otimização C++ para Produção

Para automatizar essas otimizações em runtime, você pode escrever uma classe helper personalizada. O código C++ a seguir demonstra como verificar a plataforma de destino, forçar programaticamente LODs baixos, desabilitar post-process animation blueprints e forçar os componentes de groom a usarem renderização baseada em cards ao spawnar um MetaHuman em um dispositivo mobile. Você pode implementar isso em uma classe como 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
    }
};

Esta função helper pode ser chamada a partir do evento BeginPlay do seu personagem ou imediatamente após spawnar um actor do MetaHuman. Ao utilizar macros de compilação condicional (PLATFORM_ANDROID || PLATFORM_IOS), o compilador remove essas substituições das builds de PC e console, permitindo que você mantenha a fidelidade visual multiplataforma automaticamente.

Adaptando Materiais Substrate para GPUs Mobile

O que é Substrate?

O Substrate substitui o modelo de sombreamento tradicional da Unreal Engine por um framework de materiais modular e multicamadas. O Substrate permite que os desenvolvedores empilhem múltiplas fatias de sombreamento (shading slabs) (por exemplo, colocando uma camada brilhante de verniz [glossy clear coat] diretamente sobre uma camada de metal áspero). Embora o Substrate seja excelente para assets cinematográficos high-end, ele apresenta um sério obstáculo de performance para os renderizadores mobile.

As GPUs mobile dependem fortemente de arquiteturas de renderização baseadas em tiles (tiled rendering architectures), onde o barramento de memória entre o núcleo da GPU e o frame buffer integrado (on-chip frame buffer) é um grande gargalo. Materiais Substrate complexos aumentam o número de bytes por pixel (BPP) gravados no frame buffer, causando estrangulamento térmico (thermal throttling) e quedas na taxa de quadros (frame rate drops).

Gerenciando a Complexidade de Materiais via Quality Level Switches

Para manter os materiais Substrate performáticos no mobile, você deve usar os nós Material Quality Level Switch e Feature Level Switch dentro do Material Editor. Esses nós permitem simplificar o material graph com base na plataforma de destino.

Para plataformas mobile, simplifique o gráfico ignorando misturas complexas de Substrate com múltiplas camadas. Em vez disso, use uma única fatia (single slab) que se aproxime do estilo visual do seu asset. Ao direcionar os nós de material por meio de um quality switch, você pode reduzir a largura de banda de gravação de 32 bytes por pixel para um padrão de 8 bytes por pixel, resultando em um dispositivo que roda mais frio e com taxas de quadros estáveis.

5 Práticas Acionáveis de Otimização Mobile

  1. Fazer pre-bake de todos os gráficos de PCG em HISMs: Não execute gráficos de PCG no cliente em runtime. Faça o pre-bake dos gráficos em Hierarchical Instanced Static Meshes (HISMs) durante o desenvolvimento no editor e configure as distâncias adequadas de start/end cull.
  2. Restringir a contagem de bones globalmente: Adicione Compat.MAX_GPUSKIN_BONES=75 ao arquivo DefaultEngine.ini do seu projeto para garantir que as skeletal meshes sejam renderizadas corretamente em GPUs mobile sem disparar buffer overflows.
  3. Usar exclusivamente grooms baseados em cards: Desabilite grooms baseados em strands para perfis mobile. A renderização de cabelo baseada em cards reduz o tempo de frame da GPU de 18ms para menos de 1ms por personagem.
  4. Aproveitar os Material Quality Switches: Implemente o nó Material Quality Level Switch em seus materiais Substrate para simplificar camadas de sombreamento complexas em uma única fatia (single slab) nas plataformas mobile, reduzindo a largura de banda da GPU.
  5. Desabilitar post-process animation blueprints: Defina bDisablePostProcessAnims = true nos componentes skeletal de seus personagens no mobile para recuperar ciclos valiosos de CPU na game thread.

A Equação do Multiplayer e Backend

Além da Renderização: Otimização de Rede Mobile

Ao desenvolver jogos Multiplayer multiplataforma, as otimizações no lado do cliente são apenas uma parte da equação. Dispositivos mobile frequentemente sofrem com flutuações de rede, alternando entre dados móveis (5G/4G) e Wi-Fi. Essas flutuações introduzem perda de pacotes (packet loss), jitter e altos picos de latência.

Se o seu código de sincronização de rede não for robusto, esses picos de latência podem desencadear o bug de sincronização multiplayer da Unreal Engine, onde a replicação de actors sai de sincronia e quebra o estado do mundo. Gerenciar estados de Multiplayer sob as restrições de redes móveis é um problema complexo que exige network drivers resilientes, compressão delta (delta compression) e reconciliação autoritativa do servidor (server-authoritative reconciliation).

Aliviando a Infraestrutura com horizOn

Construir e manter um Backend Multiplayer resiliente por conta própria é um esforço de engenharia gigantesco. Você precisaria provisionar load balancers, gerenciar replicação global de banco de dados, implementar lógica de Matchmaking e lidar com faturamento mobile. Este trabalho de infraestrutura pode levar meses de desenvolvimento e exigir manutenção contínua.

Com o horizOn, esses serviços de Backend já vêm pré-configurados. O horizOn oferece aos desenvolvedores de jogos Matchmaking de sessão, sincronização de estado de baixa latência, persistência de banco de dados e autenticação multiplataforma prontos para uso (out of the box). Isso permite que você foque na otimização de seus clientes e no polimento do seu game loop, em vez de gerenciar clusters de servidores e escalabilidade de banco de dados.

Conclusão e Próximos Passos

Otimizar recursos de próxima geração como MetaHumans e PCG para mobile exige um controle rigoroso sobre orçamentos de renderização, compilação de skeletal meshes e complexidade de material shading. Ao realizar o pre-bake de seus assets procedurais, restringir limites de contagem de bones e usar a renderização de cabelo baseada em cards, você pode entregar experiências multiplataforma de alta fidelidade em dispositivos portáteis.

Pronto para escalar seu Backend Multiplayer? Experimente o horizOn gratuitamente ou confira a documentação da API para ver como você pode simplificar sua infraestrutura e manter seus jogadores sincronizados no PC, console e mobile.


Fonte: Tutorial: Optimizing Next Gen Features for Mobile Game Development