Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Descarte por oclusão
Em um motor de renderização 3D, o occlusion culling (descarte por oclusão) é o processo de realizar a remoção de geometrias ocultas.
Nesta página, você aprenderá:
Quais são as vantagens e armadilhas do occlusion culling.
Como configurar o occlusion culling no Godot.
Solução de problemas comuns com a oclusão ambiental (occlusion culling).
Ver também
Você pode ver o funcionamento do descarte por oclusão utilizando o projeto de demonstração Occlusion Culling and Mesh LOD demo project.
Por que utilizar descarte por oclusão
Nesta cena de exemplo com centenas de salas empilhadas umas ao lado das outras, um objeto dinâmico (esfera vermelha) está escondido atrás da parede na sala iluminada (à esquerda da porta):
Cena de exemplo com um layout favorável ao occlusion culling
Com o occlusion culling desativado, todas as salas atrás da sala iluminada precisam ser renderizadas. O objeto dinâmico também precisa ser renderizado:
Cena de exemplo com occlusion culling desativado (wireframe)
Com o occlusion culling ativado, apenas as salas que estão realmente visíveis precisam ser renderizadas. O objeto dinâmico também é ocluído pela parede e, portanto, não precisa mais ser renderizado:
Cena de exemplo com occlusion culling ativado (wireframe)
Como o motor tem menos trabalho a fazer (menos vértices para renderizar e menos chamadas de desenho), o desempenho aumentará desde que existam oportunidades suficientes de occlusion culling na cena. Isso significa que o occlusion culling é mais eficaz em cenas internas (indoor), preferencialmente com muitas salas menores em vez de poucas salas grandes. Combine isso com o Nível de detalhe de malha (LOD) e os Faixas de visibilidade (HLOD) para melhorar ainda mais os ganhos de desempenho.
Nota
Ao usar o renderizador Forward+, o motor já realiza um depth prepass (pré-passagem de profundidade). Isso consiste em renderizar uma versão apenas de profundidade da cena antes de renderizar os materiais reais da cena. Isso é usado para garantir que cada pixel opaco seja sombreado apenas uma vez, reduzindo significativamente o custo de overdraw (sobredesenho).
Os maiores benefícios de desempenho podem ser observados ao usar o renderizador Mobile, pois ele não apresenta um depth prepass por motivos de desempenho. Como resultado, o occlusion culling diminuirá ativamente o overdraw de sombreamento com esse renderizador.
Apesar disso, mesmo ao usar um depth prepass, ainda há um benefício notável no occlusion culling em cenas 3D complexas. No entanto, em cenas com poucas oportunidades de occlusion culling, o occlusion culling pode não valer o esforço adicional de configuração e o uso de CPU.
Como o descarte por oclusão funciona no Godot
Nota
"occluder" (oclusor) refere-se à forma que bloqueia a visão, enquanto "occludee" (ocluído) refere-se ao objeto que está sendo escondido.
No Godot, o occlusion culling funciona rasterizando a geometria do oclusor da cena em um buffer de baixa resolução na CPU. Isso é feito usando a biblioteca de raytracing por software Embree.
O motor então usa esse buffer de baixa resolução para testar a AABB do ocluído contra as formas dos oclusores. A AABB do ocluído deve estar totalmente ocluída pela forma do oclusor para ser descartada (culled).
Como resultado, objetos menores têm mais probabilidade de serem descartados de forma eficaz do que objetos maiores. Oclusores maiores (como paredes) também tendem a ser muito mais eficazes do que oclusores menores (como objetos de decoração/props).
Configurando descarte por oclusão
The first step to using occlusion culling is to enable the Rendering > Occlusion Culling > Use Occlusion Culling project setting. (Make sure the Advanced toggle is enabled in the Project Settings dialog to be able to see it.)
Esta configuração de projeto é aplicada imediatamente, então você não precisa reiniciar o editor.
Depois de ativar a configuração do projeto, você ainda precisa criar alguns oclusores. Por motivos de desempenho, o motor não usa automaticamente toda a geometria visível como base para o occlusion culling. Em vez disso, o motor exige que uma representação simplificada da cena, contendo apenas objetos estáticos, seja gerada (baked).
Existem duas maneiras de configurar oclusores em uma cena:
Gerando oclusores automaticamente (recomendado)
Nota
Apenas nós MeshInstance3D são levados em consideração no processo de geração (baking) de oclusores no momento. Nós MultiMeshInstance3D, GPUParticles3D, CPUParticles3D e nós CSG não são levados em consideração ao gerar oclusores. Se você deseja que estes sejam tratados como oclusores, precisa criar manualmente formas de oclusores que correspondam (aproximadamente) à geometria deles.
Desde o Godot 4.4, os nós CSG podem ser levados em consideração no processo de geração se forem convertidos para uma MeshInstance3D antes de gerar os oclusores.
Esta restrição não se aplica aos ocluídos (occludees). Qualquer tipo de nó que herde de GeometryInstance3D pode ser ocluído.
Depois de ativar a configuração de projeto do occlusion culling mencionada acima, adicione um nó OccluderInstance3D à cena que contém o seu nível 3D.
Selecione o nó OccluderInstance3D e clique em Bake Occluders no topo da viewport do editor 3D. Após a geração, o nó OccluderInstance3D conterá um recurso Occluder3D que armazena uma versão simplificada da geometria do seu nível. Essa geometria de oclusor aparece como linhas de aramado roxas na visão 3D (desde que View Gizmos esteja ativado no menu Perspective). Essa geometria é então usada para fornecer occlusion culling tanto para ocluídos estáticos quanto dinâmicos.
Após a geração, você pode notar que seus objetos dinâmicos (como o jogador, inimigos, etc.) foram incluídos na malha gerada. Para evitar isso, configure a propriedade Bake > Cull Mask no OccluderInstance3D para excluir certas camadas visuais da geração.
Por exemplo, você pode desativar a camada 2 na cull mask e, em seguida, configurar os nós MeshInstance3D dos seus objetos dinâmicos para ficarem localizados na camada visual 2 (em vez da camada 1). Para fazer isso, selecione o nó MeshInstance3D em questão e, na propriedade VisualInstance3D > Layers, desmarque a camada 1 e marque a camada 2. Depois de configurar tanto a cull mask quanto as camadas, gere os oclusores novamente seguindo o processo acima.
Posicionando oclusores manualmente
Esta abordagem é mais adequada para casos de uso especializados, como a criação de oclusão para estruturas de MultiMeshInstance3D ou nós CSG (devido à limitação mencionada anteriormente).
Depois de ativar a configuração de projeto do occlusion culling mencionada acima, adicione um nó OccluderInstance3D à cena que contém o seu nível 3D. Selecione o nó OccluderInstance3D e escolha um tipo de oclusor para adicionar na propriedade Occluder:
QuadOccluder3D (um único plano)
BoxOccluder3D (um cuboide)
SphereOccluder3D (um oclusor em formato de esfera)
PolygonOccluder3D (um polígono 2D com quantos pontos você desejar)
Existe também o ArrayOccluder3D, cujos pontos não podem ser modificados no editor, mas pode ser útil para geração procedural por meio de um script.
Visualizando o descarte por oclusão
Você pode ativar um modo de desenho de depuração para pré-visualizar o que o descarte por oclusão (occlusion culling) está realmente "vendo". No canto superior esquerdo da viewport do editor 3D, clique no botão Perspective (ou Orthogonal, dependendo do modo atual da sua câmera) e escolha Display Advanced… > Occlusion Culling Buffer. Isso exibirá o buffer de baixa resolução que é usado pelo motor para o descarte por oclusão.
No mesmo menu, você também pode ativar View Information (Exibir Informações) e View Frame Time (Exibir Tempo de Quadro) para visualizar o número de chamadas de desenho (draw calls) e primitivas renderizadas (vértices + índices) no canto inferior direito, junto com o número de quadros por segundo renderizados no canto superior direito.
Se você alternar o descarte por oclusão nas configurações do projeto enquanto essas informações são exibidas, poderá ver o quanto o descarte por oclusão melhora o desempenho na sua cena. Note que o benefício de desempenho depende muito do ângulo de visão da câmera do editor 3D, pois o descarte por oclusão só é eficaz se houver oclusores à frente da câmera.
Para alternar o descarte por oclusão em tempo de execução, defina a propriedade use_occlusion_culling na viewport raiz da seguinte forma:
get_tree().root.use_occlusion_culling = true
GetTree().Root.UseOcclusionCulling = true;
Alternar o descarte por oclusão em tempo de execução é útil para comparar o desempenho em um projeto em execução.
Considerações de desempenho
Projete seus níveis para tirar vantagem do descarte por oclusão
Esta é a diretriz mais importante. Um bom design de nível não se trata apenas do que a jogabilidade exige; ele também deve ser construído pensando na oclusão.
Para ambientes internos (indoor), adicione paredes opacas para "quebrar" a linha de visão em intervalos regulares e garantir que não se possa ver uma parte exagerada da cena de uma só vez.
Para cenas abertas grandes, use uma estrutura semelhante a uma pirâmide para a elevação do terreno quando possível. Isso oferece as maiores oportunidades de descarte em comparação com qualquer outro formato de terreno.
Evite mover nós OccluderInstance3D durante o jogo
Isso inclui mover os pais de nós OccluderInstance3D, pois isso fará com que os próprios nós se movam no espaço global, exigindo, portanto, que a BVH seja reconstruída.
Alternar a visibilidade de um OccluderInstance3D (isso ou a visibilidade de um de seus pais) não é tão caro, pois a atualização só precisa acontecer uma vez (em vez de continuamente).
Por exemplo, se você tiver uma porta de correr ou rotatória, pode fazer com que o nó OccluderInstance3D não seja um filho da porta em si (para que o oclusor nunca se mova), mas pode ocultar a visibilidade do OccluderInstance3D assim que a porta começar a abrir. Você pode então exibir o OccluderInstance3D novamente assim que a porta estiver totalmente fechada.
Se você absolutamente precisar mover um nó OccluderInstance3D durante o jogo, use uma forma primitiva de Occluder3D em vez de uma forma gerada (baked) complexa.
Use as formas de oclusores mais simples possíveis
Se você notar baixo desempenho ou travamentos (stuttering) em cenas 3D complexas, pode significar que a CPU está sobrecarregada como resultado da renderização de oclusores detalhados. Selecione o nó OccluderInstance3D, aumente a propriedade Bake > Simplification e gere os oclusores novamente.
Lembre-se de manter o valor de simplificação razoável. Valores muito altos para a geometria do nível podem fazer com que ocorra um descarte por oclusão incorreto, como em Meu ocluído está sendo descartado quando não deveria.
Se isso ainda não resultar em um uso de CPU baixo o suficiente, você pode tentar ajustar a configuração de projeto Rendering > Occlusion Culling > BVH Build Quality e/ou diminuir Rendering > Occlusion Culling > Occlusion Rays Per Thread. Você precisará ativar o botão Advanced na janela de Configurações do Projeto para ver essas opções.
Solução de problemas
Meu ocluído (occludee) não está sendo descartado quando deveria
Do lado do oclusor (occluder):
Primeiro, verifique duas vezes se a propriedade Bake > Cull Mask no OccluderInstance3D está definida para permitir a geração das malhas que você deseja. A camada de visibilidade dos nós MeshInstance3D deve estar presente dentro da cull mask para que a malha seja incluída na geração.
Observe também que a geração de oclusores só leva em consideração malhas com materiais opacos. Superfícies com materiais transparentes não serão incluídas na geração, mesmo que a textura aplicada nelas seja totalmente opaca.
Por fim, lembre-se de que nós MultiMeshInstance3D, GPUParticles3D, CPUParticles3D e nós CSG não são levados em consideração ao gerar oclusores. Como alternativa, você pode adicionar nós OccluderInstance3D para eles manualmente.
Do lado do ocluído (occludee):
Certifique-se de que o Extra Cull Margin esteja definido o mais baixo possível (geralmente deve ser 0.0) e que a opção Ignore Occlusion Culling esteja desativada na seção GeometryInstance3D do objeto.
Além disso, verifique o tamanho da AABB (que é representada por uma caixa laranja ao selecionar o nó). Esta caixa delimitadora alinhada aos eixos deve estar totalmente ocluída pelas formas dos oclusores para que o ocluído seja ocultado.
Meu ocluído está sendo descartado quando não deveria
A causa mais provável para isso é que objetos que foram incluídos na geração do oclusor foram movidos após a geração dos oclusores. Por exemplo, isso pode ocorrer ao mover a geometria do seu nível ou reorganizar o layout dele. Para corrigir isso, selecione o nó OccluderInstance3D e gere os oclusores novamente.
Isso também pode acontecer porque objetos dinâmicos foram incluídos na geração, embora não devessem estar. Use o modo de desenho de depuração do descarte por oclusão para procurar formas de oclusores que não deveriam estar presentes e, em seguida, ajuste a cull mask de geração de acordo.
A última causa possível para isso é uma simplificação de malha excessivamente agressiva durante o processo de geração do oclusor. Selecione o nó OccluderInstance3D, diminua a propriedade Bake > Simplification e gere os oclusores novamente.
Como último recurso, você pode ativar a propriedade Ignore Occlusion Culling no ocluído. Isso anulará as melhorias de desempenho do descarte por oclusão para esse objeto, mas faz sentido fazer isso para objetos que nunca serão descartados (como o modelo de visualização em primeira pessoa/view model).