Como criar uma floresta no Godot 4 sem derrubar o FPS: MultiMesh, LOD e visibility ranges
Um guia prático de vegetação rápida no Godot 4: orçamento de triângulos, LOD automático de malha vs. visibility ranges, MultiMesh em chunks, transparência das folhas, sombras, occlusion culling e como medir o resultado.

A primeira floresta de um projeto no Godot costuma ficar linda — até você entrar nela. Duzentas árvores depois, o frame rate cai pela metade, o profiler mostra milhares de draw calls e ninguém sabe ao certo qual configuração mexer. Um usuário do fórum do Godot descreveu exatamente isso: um sistema de LOD de grama feito em casa acabou com cerca de 10 FPS e mais de 3.000 draw calls em um pedacinho de terreno.
A boa notícia: o Godot 4 tem todas as ferramentas necessárias para uma floresta grande e rápida. Só é preciso combiná-las do jeito certo. Este guia passa por elas na ordem que importa — orçamento, LOD, instancing, folhas, sombras, culling — e termina com um checklist.
Por que florestas são lentas
Uma árvore é um dos objetos mais caros de um jogo. Ela pesa em quatro lugares ao mesmo tempo:
- Draw calls. Cada árvore tem pelo menos duas malhas (casca e folhas), muitas vezes com materiais diferentes. Cem árvores separadas significam centenas de draw calls antes de você desenhar qualquer outra coisa.
- Triângulos e vértices. Uma árvore detalhada passa fácil dos 40.000 triângulos. Tudo bem para uma árvore de destaque; fatal para uma encosta inteira.
- Overdraw. As folhas são cards com pixels transparentes. A GPU continua sombreando as partes invisíveis, camada sobre camada, para cada card da copa.
- Sombras. Cada árvore é desenhada de novo no shadow map — às vezes várias vezes, uma por cascata de sombra.
Cada técnica abaixo ataca um ou mais desses custos.
Passo 1. Defina o orçamento antes de construir qualquer coisa
O hábito mais útil de todos é decidir quantos triângulos uma árvore pode custar em cada distância antes de modelá-la ou gerá-la. Veja um carvalho real do FoliMesh, como exemplo de uma cadeia pronta para jogo:
| Nível | Distância (exemplo) | Triângulos |
|---|---|---|
| LOD0 | 0–30 m | 14.490 |
| LOD1 | 30–70 m | 3.984 |
| LOD2 | 70–150 m | 1.154 |
| Impostor | 150 m + | 2 |
O importante não são os números exatos, mas o formato da curva: cada nível é aproximadamente 3–4 vezes mais leve que o anterior, e as árvores mais distantes quase não custam nada. No mobile, divida tudo por dois ou três. A maior parte da floresta está longe, então a maior parte da floresta deve ser barata.
A mesma árvore em quatro níveis de detalhe: a silhueta se mantém, a contagem de triângulos cai.
Passo 2. Escolha o mecanismo de LOD: LOD automático de malha ou visibility ranges
O Godot 4 oferece dois sistemas de LOD diferentes, e escolher o errado para árvores é uma fonte comum de dor de cabeça.
LOD automático de malha. Quando você importa uma cena glTF, .blend ou FBX, o Godot gera versões simplificadas de cada malha com o meshoptimizer e alterna entre elas sozinho. Segundo a documentação oficial, a escolha se baseia no ponto da bounding box do nó mais próximo da câmera e em quantos pixels o erro cobriria (Rendering → Mesh LOD → LOD Change → Threshold Pixels, 1 pixel por padrão). Cada nó também tem um LOD Bias.
Isso funciona bem para pedras, construções e props — superfícies contínuas. A copa de uma árvore é diferente: são centenas de cards de folha separados. A simplificação genérica não foi feita para isso, e o resultado pode ser uma copa rala e deformada. A própria documentação recomenda outra ferramenta quando você tem LODs feitos à mão.
Visibility ranges (HLOD). Aqui você mesmo fornece as malhas de LOD e diz a cada uma em que distância ela fica visível. Todo GeometryInstance3D tem:
visibility_range_begin/visibility_range_end— a faixa de distância em que o nó é desenhado (medida até o centro da bounding box);visibility_range_begin_margin/visibility_range_end_margin— uma zona de margem para que o nível não fique piscando quando a câmera para bem na fronteira;visibility_range_fade_mode— Disabled (troca instantânea, a mais rápida), Self ou Dependencies (fade por alpha; força renderização transparente durante a transição, o que custa desempenho).
Para árvores, visibility ranges são a escolha confiável: os LODs são pensados para folhagem, as distâncias ficam sob seu controle e você pode desligar as sombras ou trocar por um impostor no último nível. Uma configuração mínima para uma árvore:
# Tree.gd — filhos: LOD0, LOD1, LOD2 (MeshInstance3D)
extends Node3D
@export var lod_starts := PackedFloat32Array([0.0, 30.0, 70.0])
@export var cull_distance := 150.0
func _ready() -> void:
for i in get_child_count():
var mi := get_child(i) as MeshInstance3D
if mi == null:
continue
mi.visibility_range_begin = lod_starts[i]
mi.visibility_range_end = lod_starts[i + 1] if i + 1 < lod_starts.size() else cull_distance
mi.visibility_range_end_margin = 2.0
mi.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_DISABLED
Se a troca estiver perceptível, primeiro garanta que os LODs mantêm a mesma silhueta — isso importa mais do que qualquer configuração de fade.
Passo 3. Muitas árvores: MultiMesh, mas em chunks
Nós separados dão conta de algumas dezenas de árvores. Para milhares, você precisa de MultiMesh: um único MultiMeshInstance3D desenha qualquer número de cópias de uma malha em um draw call.
Há uma pegadinha que surpreende quase todo mundo. A documentação diz que nós que desenham muitas malhas de uma vez — MultiMeshInstance3D e partículas — desenham todas as instâncias no mesmo nível de LOD, e o culling só consegue remover o nó inteiro, não instâncias individuais. Um mantenedor do Godot confirmou isso na issue #76436: um MultiMesh para o nível inteiro significa nada de LOD por árvore e nada de frustum ou occlusion culling.
A solução são os chunks. Divida o mundo em uma grade e dê a cada célula o seu próprio MultiMesh para cada nível de LOD, cada um com sua visibility range. Chunks distantes mostram o LOD leve, os próximos mostram o detalhado, e os que estão atrás da câmera são descartados por completo.
# ForestChunks.gd — monta MultiMeshes em chunks com visibility ranges
extends Node3D
@export var lod_meshes: Array[Mesh] = [] # LOD0, LOD1, LOD2, impostor
@export var lod_starts := PackedFloat32Array([0.0, 35.0, 80.0, 160.0])
@export var cull_distance := 400.0
@export var chunk_size := 32.0
func build(transforms: Array[Transform3D]) -> void:
var chunks := {}
for t in transforms:
var key := Vector2i(floori(t.origin.x / chunk_size), floori(t.origin.z / chunk_size))
if not chunks.has(key):
chunks[key] = []
chunks[key].append(t)
for key in chunks:
_build_chunk(chunks[key])
func _build_chunk(items: Array) -> void:
for i in lod_meshes.size():
var mm := MultiMesh.new()
mm.transform_format = MultiMesh.TRANSFORM_3D
mm.mesh = lod_meshes[i]
mm.instance_count = items.size()
for k in items.size():
mm.set_instance_transform(k, items[k])
var node := MultiMeshInstance3D.new()
node.multimesh = mm
node.visibility_range_begin = lod_starts[i]
node.visibility_range_end = lod_starts[i + 1] if i + 1 < lod_starts.size() else cull_distance
node.visibility_range_end_margin = 4.0
add_child(node)
Duas regras práticas:
- Tamanho do chunk próximo da distância do LOD0. A distância é medida até o centro do chunk, então um chunk de 100 m mostraria o LOD1 para uma árvore bem ao lado do jogador. Com LOD0 até 35 m, chunks de 24–32 m funcionam bem.
- Mas não pequenos demais. Cada chunk adiciona draw calls (um por LOD por material). Meça: em geral, algumas centenas de chunks na tela ainda vão bem no desktop; no mobile, bem menos.
O MultiMesh não tem colisão. Adicione formas simples só onde o jogador consegue chegar — uma cápsula ou um cilindro por tronco, nunca a copa.
Centenas de árvores instanciadas: a estrutura certa mantém os draw calls quase estáveis enquanto a floresta cresce.
Passo 4. Folhas: alpha scissor, não alpha blending
É nas folhas que a maioria das florestas perde o desempenho — e boa parte da beleza, se a transparência estiver mal configurada.
- Use Alpha Scissor (cutout), não Alpha. No material, defina Transparency → Alpha Scissor. O alpha com blending exige ordenação, quebra a profundidade e as sombras e sai muito mais caro numa copa com centenas de cards sobrepostos.
- Suavize as bordas com alpha to coverage. Com MSAA ativado, defina Alpha Antialiasing → Alpha Edge Clip (ou Blend) para ter bordas de folha suaves sem transparência de verdade.
- Combata o overdraw com o formato do card. Um card quadrado em volta de um galhinho redondo desperdiça muitos pixels. Cards recortados no formato do cluster de folhas sombreiam bem menos pixels invisíveis. Confira com o modo Display Overdraw da viewport: copas em vermelho vivo indicam camadas demais.
- Remova o que ninguém vê. Clusters lá no fundo da copa ficam invisíveis de fora; os LODs distantes devem ser os primeiros a descartá-los.
Passo 5. Sombras: a segunda floresta escondida
Cada árvore que projeta sombra é renderizada de novo para o shadow map. Três configurações trazem a maior parte da economia:
- Limite a distância das sombras.
DirectionalLight3D→ Directional Shadow → Max Distance. Depois de 100–200 m, poucos jogadores notam a falta da sombra de árvores individuais. - Desligue as sombras nos LODs distantes. Defina Cast Shadow → Off no LOD2 e nos impostores (no caso do MultiMesh, no
MultiMeshInstance3Ddaquele nível). - Use menos cascatas em hardware fraco. Duas cascatas em vez de quatro costuma ser uma mudança invisível e uma economia perceptível.
Passo 6. Occlusion culling e vento
O occlusion culling ajuda quando objetos grandes escondem a floresta: morros, penhascos, construções, o próprio terreno. Ative em Project Settings → Rendering → Occlusion Culling, adicione um OccluderInstance3D e faça o bake. As árvores em si são péssimas oclusoras — estão cheias de buracos —, então deixe o terreno fazer o trabalho. Os MultiMeshes em chunks são os que mais se beneficiam: um chunk inteiro escondido é simplesmente pulado.
O vento deve ficar no vertex shader e ler pesos pré-calculados da malha: o tronco quase não se mexe, os galhos grandes balançam devagar, as pontas das folhas tremulam rápido. Calcular isso do zero no shader é caro; ler dois números por vértice sai quase de graça.
Passo 7. Meça no hardware-alvo
Nunca otimize no escuro. O Godot oferece tudo o que você precisa:
- Debugger → Monitors: draw calls no frame, objetos, primitivas (triângulos) no frame.
- Debugger → Visual Profiler: quanto tempo CPU e GPU gastam em cada parte do frame.
- Viewport → Display Overdraw / Wireframe: para onde vão os pixels e os triângulos.
Meça antes e depois de cada mudança, no aparelho mais fraco que você quer suportar — não só no seu PC de desenvolvimento.
Checklist
- Cada árvore tem um orçamento de triângulos por distância, com cada nível 3–4 vezes mais leve.
- As árvores usam visibility ranges com LODs próprios; o LOD automático de malha fica só para pedras e props.
- Milhares de árvores estão divididas em MultiMeshes em chunks, do tamanho aproximado da distância do LOD0.
- As folhas usam Alpha Scissor mais alpha to coverage; os cards acompanham o formato dos clusters.
- A distância das sombras é limitada; LODs distantes e impostores não projetam sombra.
- O occlusion culling é feito com bake a partir do terreno e de objetos grandes.
- O vento é um vertex shader barato que lê pesos por vértice.
- Draw calls e tempo de frame são medidos no aparelho-alvo.
Onde o FoliMesh entra nisso
Criamos o FoliMesh para o nosso próprio jogo de grande escala, então ele gera árvores que já seguem essas regras. O pacote para Godot é uma pasta que você joga no projeto: um GLB com LOD0, LOD1, LOD2… gerados de acordo com os seus orçamentos de triângulos, e uma cena .tscn pronta cujo script configura as visibility ranges de cada nível, um shader de vento (a flexão do tronco e o balanço dos galhos ficam guardados no segundo canal UV da malha) e um StaticBody3D com uma cápsula em volta do tronco para a colisão. Os normal maps já vêm no formato OpenGL que o Godot espera.
Você pode conferir qualquer modelo antes de importá-lo com as nossas ferramentas gratuitas no navegador: o visualizador de LOD, o contador de polígonos e o validador de glTF. Para uma visão geral das ferramentas de árvores, veja a nossa comparação de geradores de árvores 3D.
FAQ
O Godot 4 cria LODs automaticamente? Sim — para cenas glTF, .blend, Collada e FBX importadas, ele gera LODs de malha na importação. Funciona melhor em superfícies contínuas; para árvores, LODs feitos à mão ou gerados, com visibility ranges, dão resultados mais previsíveis.
Por que o LOD não funciona com o meu MultiMesh?
O Godot escolhe um único nível de LOD para o MultiMeshInstance3D inteiro. Divida as instâncias em chunks, cada um com seu próprio nó e sua visibility range.
Quantas árvores o Godot consegue renderizar? Depende do orçamento por árvore, não da engine. Com MultiMeshes em chunks, LODs distantes baratos e impostores, dezenas de milhares de árvores na tela são realistas em hardware de desktop; meça no seu aparelho-alvo.
Alpha Scissor ou Alpha Hash para folhas? Alpha Scissor com alpha to coverage é a escolha mais comum. O Alpha Hash dá bordas mais suaves sem MSAA, mas fica com aspecto granulado e funciona melhor com TAA.

