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.