Skip to content

Construindo um mundo aberto no navegador, parte 28: Grama até o horizonte e terreno que se oculta

Por Oleg Sidorkin, CTO e cofundador da Cinevva

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

A Parte 27 construiu a ilha e deu a ela um terreno que não parece repetitivo. Esta parte aborda as duas coisas que fazem o terreno parecer habitado, em vez de vazio. O Spike 56 trata da grama, o detalhe da superfície que transforma uma encosta texturizada em um lugar pelo qual você caminharia, e o problema é fazer um campo parecer um campo, em vez de riscos espalhados. O Spike 57 é o oposto de desenhar mais: trata-se de não desenhar o que uma colina já está ocultando, e o resultado interessante é que a forma rápida de fazer esse teste também se revelou a forma correta.

Grama que parece um campo, não confete

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

A decisão central é geométrica. Uma única lâmina fina e afilada ocupa menos de um pixel na maioria dos ângulos de câmera, então meio milhão de lâminas planas parecem confete verde esparso, em vez de um campo. A solução é um tufo de quads cruzados: três quads afilados girados em intervalos de sessenta graus ao redor do eixo vertical local, de modo que, em qualquer direção de visualização, pelo menos um quad fique quase perpendicular à câmera e cada instância cubra aproximadamente três larguras de lâmina de área real na tela. Essa é a diferença entre ver riscos verdes e ver grama.

Um kernel de computação WebGPU posiciona cada tufo uma única vez na inicialização. Ele transforma o índice da instância em cinco fluxos aleatórios descorrelacionados por meio de hash, escolhe uma posição XZ dentro da área, amostra a altura do terreno usando o mesmo FBM empregado pela malha de terreno da CPU (uma adaptação para TSL com mod que preserva o sinal, para que os valores coincidam exatamente e as lâminas fiquem sobre a superfície, em vez de flutuar acima dela), calcula uma normal por diferenças centrais e sorteia largura, altura e matiz para cada tufo. No lado da renderização, há um grafo de vértices TSL que ignora por completo a matriz de instância e escreve diretamente no espaço de recorte: ele redimensiona o quad cruzado unitário, gira o eixo vertical local até a normal do terreno usando a fórmula de Rodrigues e o translada para a posição do tufo. O LOD por distância vem de graça, sem uma etapa de descarte, porque o grafo de vértices multiplica a altura do tufo por 1smoothstep(fadeNear,fadeFar,dist). Assim, tufos distantes se achatam até altura zero e deixam de consumir preenchimento. O vento usa duas oitavas de seno e cosseno sobre as coordenadas XZ do tufo no mundo, somadas ao tempo, aplicadas horizontalmente e moduladas pela fração da altura para manter a base ancorada enquanto as pontas se movem. Uma leve inclinação orientada pela câmera também inclina cada tufo na direção do observador, fazendo com que eles se abram em perspectiva em vez de parecerem cartões planos.

A receita de cores foi tomada emprestada do shader URP de Breath of the Wild criado por NedMakesGames: sombreamento plano, com a cor da lâmina interpolada de um tom da raiz para um tom da ponta ao longo da fração da altura; os tons da raiz e da ponta, por sua vez, são interpolados entre duas paletas pelo valor de matiz de cada tufo. Isso produz o aspecto salpicado em dois tons de um campo de grama real de BotW. A iluminação difusa usa a normal do terreno em relação ao sol, com um piso de luz ambiente, porque as normais individuais dos quads cruzados geram ruído demais para serem sombreadas separadamente em um visual estilizado. O campo inteiro usa uma única chamada de desenho e é posicionado e animado inteiramente na GPU.

Quatro maneiras de descartar o que uma colina está ocultando

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

O Spike 57 compara quatro caminhos de descarte no mesmo mundo procedural transmitido pelo cliente de produção, escalado verticalmente em 1,8 vez para que as colinas realmente ocultem a vegetação. O T0 usa apenas a distância, correspondendo ao que a visibilidade de chunks da versão atual já faz. O T1 acrescenta descarte pelo frustum, o maior ganho barato isolado, removendo tudo que está fora do cone de visão. O T2 acrescenta um teste de horizonte que leva o terreno em conta: ele percorre um raio do olho até cada instância e a rejeita se o mapa de alturas ultrapassar o raio em qualquer ponto do caminho. Assim, uma árvore atrás de uma crista é descartada mesmo que esteja dentro do frustum. O T3 mantém o mesmo teste de horizonte, mas o acelera com uma pirâmide de alturas máximas. Já o T4 transfere todo o teste de distância, frustum e horizonte para um kernel de computação TSL, que grava uma escala de visibilidade por instância, lida pelo material da vegetação para colapsar os vértices ocultos. Um contador que mantém a visibilidade por dois quadros suaviza a cintilação de um único quadro quando um ponto de amostragem cai logo dentro ou fora de um texel enquanto a câmera se move levemente. Ele é propositalmente curto, pois qualquer duração maior mascara erros algorítmicos em vez de corrigi-los.

É na pirâmide que o spike mostra seu valor, e a lição está em fazer o teste corresponder ao que realmente aparece na tela. O terreno renderizado é uma malha de triângulos com vértices em uma grade fixa de dois metros, e o rasterizador interpola linearmente entre os vértices. Portanto, a altura real renderizada dentro de qualquer retângulo é o máximo dos vértices contidos nele, nunca o pico do ruído contínuo entre eles. Se a pirâmide de oclusão amostrar o ruído em uma grade mais fina, ela encontrará picos fantasmas que a malha nunca exibe e começará a bloquear raios que a câmera claramente consegue atravessar. Por isso, os texels da base da pirâmide ficam exatamente sobre a grade de vértices, cada um armazenando o máximo de seus quatro vértices de canto, enquanto os níveis superiores usam reduções máximas 2x2 convencionais. Isso a torna exata em relação à malha renderizada. Para o teste de raio a cada passo, o código deliberadamente faz amostragem pontual da malha com interpolação bilinear, em vez de consultar a pirâmide, pois uma consulta AABB menor que um texel retorna o máximo do texel inteiro e aumenta a altura em vários metros nas cristas íngremes — exatamente o tipo de oclusão excessiva que ocultaria objetos visíveis.

O resultado surpreendente é que o T2, a referência de força bruta, é o que está errado. Como o T2 faz amostragem pontual diretamente no ruído contínuo, ele detecta picos entre vértices da malha que não são renderizados. Assim, ele causa uma pequena oclusão excessiva e oculta vegetação que o jogador realmente consegue ver. O caminho da pirâmide é mais rápido, graças à redução AABB de O(logN) para testes no nível dos chunks, e também mais correto geometricamente, pois só pode retornar alturas que a malha de fato exibe. Esse é o ponto central do spike: a estrutura acelerada não é uma concessão de qualidade aceita em troca de velocidade; é a versão que corresponde à realidade. O descarte de chunks executa um teste de cinco pontos por chunk — os quatro cantos superiores mais o centro na altura máxima do chunk — e exclui da coleção de oclusores a área ocupada pelo próprio chunk, para que ele nunca oclua a si mesmo. O caminho recomendado para produção é o T4: enviar os metadados das instâncias e o campo de alturas para buffers de armazenamento e executar o mesmo loop em um shader de computação com desenho indireto, já que o renderizador está migrando para WebGPU de qualquer maneira.

Tecnologia mencionada neste capítulo

Grama de quads cruzados na GPU. Três quads afilados girados em intervalos de sessenta graus por tufo garantem um quad quase perpendicular a partir de qualquer ângulo. Assim, meio milhão de instâncias parecem um campo, em vez de confete menor que um pixel. Um kernel de computação posiciona cada tufo (altura do terreno FBM amostrada com o mesmo mod que preserva o sinal usado pela malha da CPU, normal por diferenças centrais, tamanho e matiz por tufo), e um grafo de vértices TSL ignora a matriz de instância para redimensionar, girar até a normal pela fórmula de Rodrigues, transladar e aplicar vento modulado pela altura. O LOD por distância é gratuito: os tufos colapsam até altura zero por 1smoothstep(fadeNear,fadeFar,dist). A cor segue a receita de BotW de NedMakesGames, com um gradiente da raiz à ponta sobre duas paletas misturadas por matiz. Consulte LOD controlado pela GPU.

Descarte por oclusão no horizonte do terreno. Quatro caminhos comparados no mundo de produção: distância; distância mais frustum; mais um teste de raio no mapa de alturas que rejeita instâncias atrás de uma crista; mais uma pirâmide de alturas máximas que acelera o teste; e uma adaptação para computação TSL. A pirâmide faz amostragens exatamente na grade de vértices de dois metros usada para a tesselação da malha (texel da base = máximo dos quatro vértices de canto, seguido por reduções máximas 2x2 nos níveis superiores), de modo que ela retorna apenas alturas que o rasterizador realmente exibe.

Acelerado e correto, não uma troca. A amostragem pontual do ruído contínuo — o caminho T2 de força bruta — encontra picos entre vértices da malha que nunca são renderizados, causando oclusão excessiva de vegetação visível. A pirâmide baseada na grade de vértices é mais rápida — redução AABB de O(logN) — e geometricamente exata. Por isso, os testes de raio a cada passo fazem amostragem bilinear da malha, em vez de consultar o máximo por texel da pirâmide, que aumentaria as alturas em metros nas cristas íngremes. O descarte de chunks usa um teste de cinco pontos e exclui a área ocupada pelo próprio chunk para impedir a auto-oclusão. O caminho de produção envia os metadados e o campo de alturas para buffers de armazenamento, permitindo o descarte por shader de computação com desenho indireto.


Parte 28 de 29. Anterior: Parte 27 — Uma ilha criada com ruído e terreno que parece terreno Próxima: Parte 29 — Um controlador, qualquer corpo Guia da série: /pt-BR/blog/2026-02-25-open-world-browser-series-guide