Arquitetura e Performance do Plugin Luma AI no Unreal Engine 5: NeRF e 3DGS

Fluxo de Processamento de Luma Fields

Ao importar um arquivo do ecosistema Luma para o Unreal Engine, o plugin inicia um pipeline automatizado através da classe LumaLoader. Este processo dispara sequencialmente as funções CreateLumaMaterialFunction, CreateLumaMaterial e CreateLumaBlueprint para converter os dados brutos em ativos nativos da Engine. A lógica central reside na CreateLumaMaterialFunction. Ela analisa o código HLSL e os parâmetros de campo conitdos no arquivo para gerar uma Material Functon. O componente vital aqui é um nó Custom que executa o LumaRaymarch. Essencialmente, este algoritmo projeta raios dentro do volume delimitador (Bounding Box) do NeRF, consulta a LUT (Look-Up Table) gerada pelo treinamento da rede neural e acumula os valores de RGBA e PDO (Pixel Depth Offset), devolvendo-os ao material para composição final. O plugin gera variantes dessa função para lidar com diferentes cenários, como a presença ou ausência de planos de fundo. Após a criação da função, o CreateLumaMaterial a encapsula em um material mestre que expõe controles de qualidade e parâmetros de recorte. Por fim, o CreateLumaBlueprint vincula esse material a uma Static Mesh, onde a classe pai ALumaField gerencia a propagação de parâmetros de shader via ApplyShaderParams(). ### Implementação de 3D Gaussian Splatting (3DGS)

Diferente do formato proprietário de campos de radiância, o Luma também suporta 3DGS via arquivos .ply. A lógica de integração, situada no LumaSplatLoader, é fundamentalmente distinta. O sistema utiliza a LumaDLLLoader para segmentar a nuvem de pontos em "chunks" (pedaços) espaciais. Cada segmento é processado pela função CreateGaussianChunkLuma, que empacota os atributos de cada Gaussiana (posição, rotação, escala e cor) em quatro texturas de formato Float16. Abaixo, um exemplo da lógica de processamento de dados de posição: ``` // Exemplo de extração de dados espaciais para texturas UTexture2D* MapaPosicao = InicializarTexturaGaussiana(LarguraTxt, AlturaTxt, [&](uint32 IndiceLinear, TArray<FFloat16>& DadosSaida) { if (IndiceLinear >= TotalGaussianas) return 2; // Encerra processamento

    uint32 IdGlobal = (ChunkInfo.Indices != nullptr) ? ChunkInfo.Indices[IndiceLinear] : IndiceLinear;
    const auto& PontoData = ListaGaussiana.At(IdGlobal);

    // Conversão de coordenadas e normalização de escala
    FVector3f PosConvertida = { PontoData.pos.x, -PontoData.pos.z, -PontoData.pos.y };
    FVector4f PosFinal(PosConvertida.X / 32.0f, PosConvertida.Y / 32.0f, PosConvertida.Z / 32.0f, 0.0f);

    DadosSaida.Add(PosFinal.X);
    DadosSaida.Add(PosFinal.Y);
    DadosSaida.Add(PosFinal.Z);
    DadosSaida.Add(PosFinal.W);

    return 1;
}

);


Para a renderização, o plugin utiliza o sistema **Niagara**. Cada chunk de dados é associado a um `NiagaraComponent` baseado no `LumaInteractiveSceneSystem`. O sistema de partículas GPU lê as texturas geradas para instanciar as Gaussianas em tempo real. A configuração dos componentes segue este padrão: ```
void ConfigurarInstanciaNiagara(FGaussianChunk& Chunk, UNiagaraComponent* SistemaNiagara, UMaterialInterface* MatRef)
{
    SistemaNiagara->SetVariableInt(FName("Count"), Chunk.Qtd);
    SistemaNiagara->SetVariableTexture(FName("TexCor"), Chunk.CorTex);
    SistemaNiagara->SetVariableTexture(FName("TexPos"), Chunk.PosTex);
    SistemaNiagara->SetVariableTexture(FName("TexRot"), Chunk.RotTex);
    SistemaNiagara->SetVariableTexture(FName("TexScale"), Chunk.ScaleTex);
    SistemaNiagara->SetVariableMaterial(FName("BaseMaterial"), MatRef);

    FVector LocalizacaoMundo = Chunk.Centro * 100.0f;
    SistemaNiagara->SetVariableVec3(FName("OffsetMundo"), LocalizacaoMundo);
    SistemaNiagara->Activate(true);
}

O plugin alterna entre diferentes materiais para suportar variações de iluminação dinâmica e Anti-Aliasing Temporal (TAA), garantindo flexibilidade visual conforme a necessidade do projeto. ### Controle de Volume e Recorte

A classe LumaScene gerencia a interatividade e o culling. Parâmetros de caixas de recorte (Crop Boxes) são passados diretamente para o sistema Niagara, permitindo isolar partes específicas da cena capturada: ``` void ALumaScene::ApplyShaderParams() const { for(auto& CompNiagara : ComponentesLuma) { if(!CompNiagara) continue;

    CompNiagara->SetVariableVec3(TEXT("GlobalScale"), EscalaSplat);

    // Configuração dinâmica de volumes de recorte
    if(VolumeRecorte)
    {
        CompNiagara->SetVariableObject(TEXT("CropActor"), VolumeRecorte);
        CompNiagara->SetVariableBool(TEXT("UseCrop"), bHabilitarRecorte);
    }
    
    // Suporte a múltiplos volumes de exclusão (Cull Boxes)
    AtualizarVolumesExclusao(CompNiagara);
}

}


### Análise de Performance e Resultados

Abaixo, os resultados observados em testes práticos utilizando uma GPU RTX 3060: **1. Luma Field (NeRF):**Apresenta um alto custo computacional ("GPU Bound"). Em resoluções altas, a taxa de quadros cai drasticamente para cerca de 5 FPS. Isso ocorre porque o shader gerado é extremamente complexo, ultrapassando 2100 instruções de Pixel Shader devido ao processo intensivo de Raymarching por pixel. **2. 3DGS (Gaussian Splatting):**Diferente da abordagem orientada a shader do NeRF, o 3DGS se comporta de forma muito mais eficiente no Unreal Engine. Como não exige Raymarching a cada mudança de perspectiva, a performance saltou para uma média de 60 FPS, aproximando-se do desempenho de uma cena vazia. Além da fluidez, a nitidez visual e o tempo de importação foram significativamente superiores aos do Luma Field, que tende a apresentar um aspecto borrado em distâncias curtas. Em resumo, enquanto o NeRF oferece uma representação volumétrica robusta, o 3DGS via Niagara se mostra a solução mais viável para aplicações em tempo real no Unreal Engine 5 devido ao seu balanço otimizado entre fidelidade visual e consumo de recursos de hardware.

Tags: Unreal Engine 5 NeRF 3D Gaussian Splatting Niagara VFX HLSL

Publicado em 7-25 09:42