Voltar para o Blog
Quest Log

Occlusion Culling na Unity: Não Desenhar o Que Não Se Vê

Corte de um prédio 3D com várias salas, apenas as visíveis pela câmera aparecem coloridas e as escondidas atrás das paredes ficam apagadas

Unity occlusion culling na prática: frustum automático, bake com Occluder e Occludee Static, GPU occlusion culling do URP no Unity 6, LOD Group e como medir.

Ferramenta:Unity

Occlusion Culling na Unity: Não Desenhar o Que Não Se Vê

Unity occlusion culling é o sistema que evita desenhar objetos que estão na frente da câmera mas escondidos atrás de outros, como a mobília da sala ao lado. A Unity já faz sozinha o frustum culling (descarta o que está fora do enquadramento); o occlusion culling é um passo extra que você liga com bake em Window > Rendering > Occlusion Culling ou, no URP do Unity 6, com o GPU occlusion culling. Aqui você vê a diferença na prática, aprende quando o bake compensa numa fase de interior, como ligar a alternativa por GPU e onde o LOD Group entra.

O ponto que quase todo tutorial esconde: occlusion culling tem custo. Ligado na cena errada, ele deixa o jogo mais lento, e o único jeito de saber é medindo.

Frustum culling: o que a Unity já faz sozinha

A câmera enxerga uma pirâmide cortada, o frustum, definida pelo campo de visão, pelo plano próximo e pelo plano distante. A cada frame, a Unity testa a caixa delimitadora (bounds) de cada Renderer contra essa pirâmide. Quem está fora não é enviado para a GPU. Isso é automático, não tem opção para ligar e funciona com objeto estático ou dinâmico.

O que o frustum não sabe é se existe algo na frente. Numa casa com dez cômodos, se a câmera aponta para uma parede e atrás dela há três salas no mesmo ângulo, as três salas inteiras são desenhadas. A GPU pinta a mobília, depois pinta a parede por cima. Esse desperdício chama overdraw, e o occlusion culling existe para cortá-lo.

Ajuste também o plano distante da câmera (Clipping Planes > Far). Num jogo de interior, deixar 1000 metros significa testar e possivelmente desenhar coisas a um quilômetro. 100 a 200 metros costumam bastar.

Occlusion culling na Unity: como o bake funciona

No sistema clássico, você faz um bake no editor. A Unity divide a cena em células, calcula a geometria de cada uma e quais células conseguem ver quais outras, e junta células parecidas para economizar dados. Em tempo de execução, cada câmera com Occlusion Culling ligado consulta esses dados e pula o que não é visível da célula onde está.

Pela documentação, o bake funciona melhor quando:

  • O jogo está limitado pela GPU por excesso de overdraw. Se o gargalo é CPU, o custo de consultar os dados pode anular o ganho.
  • A cena tem áreas pequenas e bem separadas por objetos sólidos: salas ligadas por corredores, níveis de masmorra, prédios.
  • Há memória para guardar os dados do bake.

E com estas limitações:

  • Objetos dinâmicos podem ser escondidos, mas não escondem nada.
  • Não serve para cena que gera geometria em tempo de execução (procedural, construção pelo jogador).

Marcar Occluder e Occludee Static

Cada objeto precisa dizer seu papel. No canto superior direito do Inspector, clique na seta ao lado de Static e marque:

  • Occluder Static: o objeto bloqueia a visão. Paredes, pisos, tetos, rochas grandes. Precisa ser opaco e grande.
  • Occludee Static: o objeto pode ser escondido. Mobília, props, decoração.

A maioria das paredes leva os dois. Objetos pequenos (uma caneca, um livro) levam só Occludee: marcar como Occluder só aumenta o bake sem esconder nada. Vidro e grade nunca são Occluder.

Marcar uma fase inteira à mão é chato. Este script de editor marca a seleção de uma vez. Crie em Assets/Editor/OcclusionStaticTools.cs:

using UnityEditor;
using UnityEngine;

public static class OcclusionStaticTools
{
    [MenuItem("Tools/Occlusion/Marcar como Occluder e Occludee")]
    private static void MarkOccluderAndOccludee()
    {
        ApplyFlags(StaticEditorFlags.OccluderStatic | StaticEditorFlags.OccludeeStatic);
    }

    [MenuItem("Tools/Occlusion/Marcar só como Occludee")]
    private static void MarkOccludeeOnly()
    {
        ApplyFlags(StaticEditorFlags.OccludeeStatic);
    }

    [MenuItem("Tools/Occlusion/Marcar como Occluder e Occludee", true)]
    [MenuItem("Tools/Occlusion/Marcar só como Occludee", true)]
    private static bool HasSelection()
    {
        return Selection.gameObjects.Length > 0;
    }

    private static void ApplyFlags(StaticEditorFlags flagsToAdd)
    {
        foreach (GameObject selected in Selection.gameObjects)
        {
            Transform[] children = selected.GetComponentsInChildren<Transform>(true);
            foreach (Transform child in children)
            {
                GameObject target = child.gameObject;
                if (target.GetComponent<Renderer>() == null)
                {
                    continue;
                }

                Undo.RecordObject(target, "Marcar Occlusion Static");
                StaticEditorFlags flags = GameObjectUtility.GetStaticEditorFlags(target);
                GameObjectUtility.SetStaticEditorFlags(target, flags | flagsToAdd);
            }
        }
    }
}

Selecione o objeto pai das paredes, rode Tools > Occlusion > Marcar como Occluder e Occludee; selecione o pai dos props, rode a outra opção. O script desce pelos filhos, só mexe em quem tem Renderer e soma as flags às que já existem, sem apagar Batching Static ou Contribute GI.

Bake e visualização no Scene View

  1. Window > Rendering > Occlusion Culling. A janela tem as abas Object, Bake e Visualization.
  2. Na aba Bake, os parâmetros:
    • Smallest Occluder: o menor objeto, em metros, que pode esconder outros. Valor maior, bake mais rápido e menor, mas paredes finas param de bloquear. Para interior com paredes de 3 m, algo entre 2 e 5 funciona.
    • Smallest Hole: a menor fresta, em metros, pela qual a câmera enxerga. Se há buraco de fechadura ou grade que precisa mostrar o outro lado, diminua.
    • Backface Threshold: remove dados de áreas dominadas por faces de trás. O padrão 100 não remove nada; deixe assim até ter um motivo.
  3. Clique em Bake. Clear apaga os dados; Set Default Parameters volta os valores padrão.
  4. Na aba Visualization, selecione a câmera e mova-a pela Scene view. Os objetos escondidos somem da vista enquanto a janela está aberta.
  5. Confira se a câmera do jogo tem Occlusion Culling ligado (no URP, fica em Rendering, no componente Camera).

Se aparece objeto sumindo quando devia estar visível, aumente a precisão: diminua Smallest Hole ou confira se um vidro foi marcado como Occluder.

Ligar o bake é fácil. Difícil é saber se o seu jogo precisa dele, ou se o problema é outro (sombra, luz, física, script alocando memória por frame). É nesse diagnóstico que muita gente se perde estudando sozinha: aplica a otimização da moda, o FPS não muda, e não sabe ler o Profiler para achar o gargalo de verdade num projeto completo.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

GPU occlusion culling do Unity 6 no URP

O URP do Unity 6 tem uma alternativa sem bake: o GPU occlusion culling. A Unity gera texturas de profundidade a partir das câmeras e luzes, e a GPU usa a profundidade do frame atual e do anterior para descartar objetos escondidos. Como recalcula todo frame, funciona com cena que muda e com objetos que se movem.

Ele depende do GPU Resident Drawer. Passos no Unity 6.3:

  1. Edit > Project Settings > Graphics, em Shader Stripping, mude BatchRendererGroup Variants para Keep All.
  2. No URP Asset ativo, confirme que o SRP Batcher está ligado (se não aparece, mostre as propriedades avançadas).
  3. No mesmo URP Asset, mude GPU Resident Drawer para Instanced Drawing.
  4. No Universal Renderer da lista de renderers, mude o Rendering Path para Forward+.
  5. Ainda no Universal Renderer, ligue GPU Occlusion.

Requisitos e limitações da documentação:

  • Só funciona com Forward+, em APIs gráficas com compute shader (OpenGL ES fica de fora) e em objetos com Mesh Renderer. O resto é desenhado do jeito normal.
  • O build fica mais demorado, porque todas as variantes de shader do BatchRendererGroup entram.
  • Cada objeto é aproximado por uma esfera. Objetos finos e compridos (um cano, uma cerca longa) viram esferas grandes e quase nunca são considerados escondidos.
  • Rende mais quando muitos objetos compartilham o mesmo mesh, a cena tem muita oclusão e os objetos escondidos são pequenos na tela.
  • Se a cena não tem oclusão suficiente, o tempo de render pode aumentar.

Para conferir, use o overlay Rendering Statistics e o Rendering Debugger (Window > Analysis > Rendering Debugger).

Bake ou GPU? Bake para fase estática de interior em hardware que não roda Forward+ com compute (mobile de entrada, por exemplo). GPU para cena grande com muitas instâncias repetidas e hardware de PC ou console atual. Os dois podem coexistir; teste cada um isolado.

LOD Group para objetos distantes

Occlusion culling esconde o que está atrás. O LOD Group simplifica o que está longe. São problemas diferentes, e uma cena aberta precisa mais do segundo.

  1. Adicione LOD Group ao objeto pai.
  2. Na barra de níveis, clique em LOD 0 e adicione o Renderer do modelo completo; LOD 1, a versão com metade dos triângulos; LOD 2, uma versão bem simples.
  3. Arraste as divisões da barra para definir em que porcentagem da altura da tela cada nível entra. O Inspector mostra a distância equivalente em metros.
  4. A última faixa, Culled, some com o objeto. Uma pedra pequena pode desaparecer a 3% da tela sem ninguém notar.
  5. Fade Mode em Cross Fade suaviza a troca, ao custo de desenhar os dois níveis durante a transição.

Se o modelo vem com malhas nomeadas _LOD0, _LOD1, _LOD2, a Unity monta o LOD Group sozinha na importação. E no Unity 6.3 existe também o Mesh LOD: no Import Settings do modelo, seção de LODs, ligue Generate Mesh LODs e a Unity gera versões simplificadas dentro do mesmo mesh (o modelo precisa ter pelo menos 256 triângulos). É o caminho mais rápido para props que não têm LOD feito à mão.

Quando NÃO vale: mundo aberto e cenas pequenas

  • Mundo aberto com terreno e céu. Pouca coisa esconde outra; colinas são baixas e árvores não são Occluder. Gaste o esforço em LOD, distância de desenho e densidade de vegetação.
  • Cena pequena. Uma arena que cabe inteira na tela não tem o que esconder.
  • Jogo 2D. O frustum basta.
  • Geometria procedural ou construída pelo jogador. O bake não acompanha. Considere o GPU occlusion culling.
  • Gargalo de CPU. Se o Profiler mostra scripts ou física como vilão, occlusion não vai mudar nada.

Medir o ganho no Profiler

Medida mínima:

  1. Escolha um ponto da fase onde a câmera vê uma parede com várias salas atrás. Grave a posição.
  2. Abra Window > Analysis > Profiler e entre no Play com o occlusion desligado na câmera.
  3. Anote o tempo de CPU e GPU do frame (módulo GPU Usage, se a plataforma suportar) e o número de batches no Rendering.
  4. Ligue o occlusion e repita no mesmo ponto.
  5. Faça o teste também num ponto aberto, onde quase nada fica escondido, para ver o custo puro.

Se o ganho no ponto fechado é pequeno e o custo no aberto é visível, desligue. Otimização boa aparece no número, não na intenção. O post sobre Profiler na Unity explica como ler cada módulo, e o guia de otimização de performance para 60 FPS mostra os outros suspeitos. Se você também trabalha com Godot, o post de otimizar FPS no Godot faz o mesmo raciocínio na outra engine.

Perguntas frequentes

Qual a diferença entre frustum culling e occlusion culling na Unity?

Frustum culling descarta o que está fora do campo de visão da câmera e é automático. Occlusion culling descarta o que está dentro do campo de visão, mas escondido atrás de outro objeto, como a sala atrás de uma parede. Esse segundo precisa de bake ou do GPU occlusion culling do URP.

Occlusion culling funciona com objetos que se movem?

No sistema com bake, objetos dinâmicos podem ser escondidos (occludee), mas não escondem nada (occluder). Só geometria estática marcada como Occluder Static bloqueia a visão. O GPU occlusion culling do URP calcula a cada frame com o depth buffer, então não depende de bake.

O que é LOD Group na Unity?

É um componente que troca o modelo por versões mais simples conforme o objeto fica menor na tela, e pode sumir com ele de vez no nível Culled. Reduz triângulos de objetos distantes, o que occlusion culling não faz.

Occlusion culling sempre deixa o jogo mais rápido?

Não. Ele tem custo de CPU (bake) ou de GPU (versão do URP) e só compensa quando muita coisa fica escondida. Em cena pequena ou aberta, pode deixar mais lento. Meça no Profiler antes e depois.