Skip to content

Construindo um mundo aberto no navegador, parte 5: Orçando os recursos visuais

Por Oleg Sidorkin, Diretor de Tecnologia e Cofundador da Cinevva

Chegou agora? Consulte o guia da série. Ele explica o que é um spike e reúne links para todas as partes.

Este foi o capítulo em que a ambição visual encontrou a aritmética.

Dividimos o custo de renderização em spikes separados porque resultados agrupados são difíceis de diagnosticar. Se você ativa tudo de uma vez, só descobre que o frame está lento. Não descobre qual recurso consumiu o orçamento.

O Spike 7 teve como foco a densidade da vegetação e o custo da animação. A abordagem foi distribuir a vegetação em tempo de execução usando mapas de densidade de 32x32 para cada chunk do terreno, alimentando grandes conjuntos de InstancedMesh. Cada lâmina de grama e agrupamento de arbustos recebeu animação de vento no vertex shader, controlada por uma textura de ruído em movimento. O principal número que monitoramos não foi a quantidade de triângulos, mas o overhead das draw calls e o throughput de vértices em GPUs intermediárias. Descobrimos que agrupar as instâncias em menos meshes era mais importante do que reduzir a quantidade de polígonos de cada lâmina.

Abrir o Spike 7 em uma nova aba ↗ · Ver código-fonte

O Spike 8 aumentou a complexidade dos materiais do terreno. Mistura de múltiplas camadas ponderada pelo ângulo de inclinação e pela altitude, projeção triplanar opcional para faces de penhascos e normal maps por camada. O shader fazia splatting com base na inclinação usando quatro camadas de textura, cada uma exigindo uma amostra difusa e uma amostra de normal. Isso representa 4×2=8 leituras de textura por fragmento antes mesmo de acrescentar qualquer iluminação, e a quantidade cresce linearmente com o número de camadas, portanto uma quinta camada significa 10 leituras para cada pixel. Fizemos o profiling especificamente em GPUs Intel integradas para encontrar o limite mínimo. A conclusão foi que a projeção triplanar em superfícies verticais valia o custo, mas adicionar uma quinta camada de splat não.

Abrir o Spike 8 em uma nova aba ↗ · Ver código-fonte

O Spike 9 concentrou-se no custo dos mapas de sombras em cascata sob uma carga realista de terreno e objetos. CSM com três cascatas era a configuração de referência. Testamos especificamente com o sol em ângulos baixos, pois é quando a pressão sobre as cascatas é maior. A cascata distante cobre uma enorme fatia do frustum, e o detalhamento das sombras é determinado pela densidade de texels, ρR2A (um mapa de sombras com resolução R estendido sobre uma área de terreno A). Usar um único mapa para toda a distância de visão torna A enorme e faz ρ despencar. As cascatas resolvem isso dividindo o frustum para que cada fatia receba seu próprio mapa de R×R sobre um A pequeno, mantendo ρ aproximadamente constante do plano mais próximo ao mais distante. Medimos a diferença no tempo de GPU entre duas e quatro cascatas e, depois, entre resoluções de mapa de sombras de 1024 e 2048. O resultado foi que três cascatas em 1024 nos proporcionaram sombras de contato aceitáveis perto da câmera sem ultrapassar 2 ms de tempo de GPU no nosso hardware-alvo.

Abrir o Spike 9 em uma nova aba ↗ · Ver código-fonte

A parte difícil dessa fase foi manter a disciplina de produto. Alguns efeitos ficaram excelentes e, mesmo assim, precisaram ser limitados porque consumiam uma parcela grande demais do orçamento de frame em relação ao seu impacto visual.

Nossa regra ficou simples. Um recurso só avança se puder justificar seu custo com dados medidos de tempo de frame.

Parece óbvio. Não é algo comum em ciclos rápidos de prototipagem, nos quais todos estão empolgados com o próximo ganho visual. Manter essa regra desde o início tornou muito mais claras as decisões de arquitetura sobre clipmaps e zonas volumétricas mais adiante, pois já conhecíamos o custo individual de cada recurso que disputava os mesmos 16 ms.

Na parte 6, chegamos à primeira grande mudança de arquitetura do terreno com os geometry clipmaps.

Tecnologias mencionadas neste capítulo

InstancedMesh e vegetação na GPU. O InstancedMesh do Three.js renderiza N cópias da mesma geometria com uma única draw call. Para a vegetação, um mapa de densidade (32x32 por chunk) controla a distribuição, em tempo de execução, de lâminas de grama e agrupamentos de arbustos em buffers de instâncias. A animação do vento é executada no vertex shader usando uma textura de ruído em movimento. Em grande escala, o ComputeInstanceCulling do WebGPU elimina instâncias fora da tela e distantes antes da rasterização, enquanto o IndirectBatchedMesh reúne vários tipos de vegetação em um único buffer, desenhado com multi-draw indirect. Consulte nosso guia de paisagens sobre culling de vegetação na GPU.

Mapeamento triplanar. Texturas com mapeamento UV convencional ficam esticadas em encostas íngremes porque as coordenadas UV são comprimidas. O mapeamento triplanar projeta texturas ao longo dos três eixos (X, Y, Z) e faz a mistura com base na normal da superfície. Faces de penhascos recebem a projeção X ou Z (sem esticar), enquanto terrenos planos recebem a projeção Y. A mistura é suave e automática, sem exigir abertura de UV. Para terrenos PBR, os mesmos pesos de mistura são aplicados aos canais de albedo, normal, rugosidade e oclusão de ambiente. Consulte os detalhes do mapeamento triplanar.

Splatting de materiais baseado em inclinação e altitude. Em vez de splat maps pintados à mão, os materiais são atribuídos proceduralmente no fragment shader com base nas propriedades do terreno. Áreas planas em baixa altitude recebem grama, encostas íngremes recebem rocha, áreas de grande altitude recebem neve (apenas em superfícies planas o suficiente para permitir o acúmulo) e áreas próximas ao nível do mar recebem areia. As transições usam smoothstep para produzir uma mistura suave. Em nossa implementação, cada chunk do terreno avalia quatro camadas de textura, com amostras difusas e de normal por camada, totalizando oito leituras de textura por fragmento antes da iluminação. Consulte a atribuição de materiais por inclinação e altitude.

Mapas de Sombras em Cascata (CSM). O CSM divide o frustum de visão da câmera em 3 a 4 faixas de distância (cascatas). Cada cascata renderiza um mapa de sombras da perspectiva do sol com uma resolução adequada à sua distância. Cascatas próximas recebem sombras em alta resolução (sombras de contato detalhadas sob árvores e edifícios), enquanto cascatas distantes recebem resolução mais baixa (sombras amplas de montanhas). O shader do terreno amostra todas as cascatas e seleciona a adequada para cada fragmento. Custo de desempenho: 3 a 4 cascatas em 1024x1024 acrescentam cerca de 0,5 a 1 ms à renderização dos mapas de sombras, além de aproximadamente 0,2 a 0,3 ms para a amostragem. Consulte sombras para terrenos.


Parte 5 de 12.
Anterior: Parte 4 — Streaming antes de terrenos sofisticados
Próxima: Parte 6 — Os clipmaps mudaram o rumo da história
Guia da série: /pt-BR/blog/2026-02-25-open-world-browser-series-guide