Empacotamento de atlas de sprites: espaçamento, potências de dois e vazamento de cor
Um atlas de sprites parece uma ideia simples — colocar todos os sprites em uma grande textura em vez de vários arquivos pequenos —, mas os detalhes de como são empacotados determinam se o jogo renderiza com clareza ou desenvolve bordas coloridas sutis em cada sprite. Veja o que um atlas realmente faz, por que as emendas apresentam vazamento de cor e quais configurações específicas evitam isso.
Por que atlas existem: chamadas de desenho
Toda vez que uma GPU troca o desenho com uma textura pelo desenho com outra, essa troca tem um custo — conceitualmente, uma nova "chamada de desenho". Uma cena com cinquenta sprites com texturas individuais, desenhados de forma ingênua, pode significar cinquenta chamadas separadas, mesmo que cada sprite seja minúsculo. Em hardware móvel, especialmente, o custo das chamadas se acumula rápido o suficiente para afetar a taxa de quadros muito antes de a GPU fazer uma quantidade significativa de trabalho real com pixels.
Um atlas de sprites contorna isso colocando muitos sprites em uma textura compartilhada. Desde que tudo desenhado em um lote use amostras da mesma textura, o renderizador pode reunir esses desenhos em muito menos chamadas — no melhor caso, uma chamada de desenho para todos os sprites que compartilham o atlas e o mesmo material. É por isso que atlas importam mais para jogos com muitos sprites pequenos redesenhados frequentemente (efeitos de partículas, ícones de interface, tilesets) do que para algumas imagens grandes de fundo, em que a quantidade de chamadas de desenho nunca foi o gargalo.
Empacotamento: encaixando sprites em uma textura
Dado um conjunto de sprites de tamanhos diferentes, organizá-los em uma única textura com o mínimo de espaço desperdiçado é uma versão do clássico problema de empacotamento. A maioria das ferramentas de atlas usa uma variante de uma destas duas abordagens:
- Empacotamento em prateleiras: sprites são colocados da esquerda para a direita em uma linha ("prateleira") até um não caber; então começa uma nova prateleira abaixo. Simples e rápido, mas desperdiça espaço quando a altura dos sprites varia muito em uma prateleira.
- Retângulos máximos / empacotamento por guilhotina: o empacotador acompanha as regiões retangulares livres após cada posicionamento e encaixa novos sprites no melhor espaço restante, dividindo retângulos conforme avança. Empacotamento mais denso, mais processamento por sprite colocado e a abordagem padrão na maioria das ferramentas modernas de atlas (TexturePacker, Sprite Atlas do Unity, exportador do Godot) para qualquer quantidade de sprites que não seja muito pequena.
A rotação é uma otimização adicional oferecida por alguns empacotadores: girar um sprite em 90 graus pode fazê-lo caber em uma faixa de espaço restante na qual ele não caberia sem rotação. Isso permite um agrupamento mais compacto, mas acrescenta complexidade à renderização (as coordenadas UV precisam considerar a rotação), por isso normalmente é uma opção a ser ativada, não o padrão.
Você não precisa implementar nada disso — as ferramentas da engine e os empacotadores específicos cuidam do trabalho. O que vale entender é que a densidade de empacotamento e o espaçamento abordado a seguir se contrapõem: empacotar mais justo significa menos espaço de textura desperdiçado, mas o espaçamento entre sprites é o que evita o vazamento de cor descrito abaixo. Um empacotador configurado de forma agressiva demais pode reintroduzir bugs visuais para economizar alguns pontos percentuais da área da textura.
Vazamento de textura: o que é e por que acontece
Vazamento de textura é quando a borda renderizada de um sprite mostra uma faixa de cor do sprite empacotado ao lado no atlas — uma linha colorida fina em um ou mais lados que não tem relação com a arte do próprio sprite. É um dos erros mais comuns específicos de atlas e tem duas causas separadas que são confundidas entre si.
Vazamento de cor pela filtragem. Quando um sprite é desenhado em uma escala diferente de 1:1 ou a câmera o move para uma posição não alinhada a pixels, o filtro de textura da GPU amostra uma pequena vizinhança de texels ao redor de cada ponto, não apenas o mais próximo — mesmo com um filtro point/nearest definido na textura, a filtragem e o uso de mipmaps na etapa de amostragem ainda podem alcançar um pouco além do retângulo UV do sprite. Se a região adjacente do atlas for outro sprite, a cor vizinha vaza para dentro.
Vazamento de cor nos mipmaps. Mipmaps são versões previamente reduzidas da textura completa, usadas quando um sprite é renderizado pequeno na tela. Gerar um mipmap mistura blocos da textura em resolução completa, o que significa que as cores de um sprite podem se misturar ao nível de mipmap de um sprite vizinho antes mesmo de qualquer sprite individual ser desenhado. Por isso, o vazamento de cor às vezes só aparece à distância ou em escala pequena e parece correto quando ampliado.
As duas causas têm a mesma origem: dois sprites sem relação um com o outro ficam imediatamente adjacentes no atlas, sem nada entre eles para absorver o erro de amostragem.

Corrigindo o vazamento de cor: espaçamento e extrusão
A solução padrão é espaçamento — deixar alguns pixels transparentes (ou de borda duplicada) entre todos os sprites do atlas, para que qualquer amostragem fora do limite caia no espaço, não no conteúdo real do sprite vizinho. Um ou dois pixels de espaçamento bastam na maioria dos casos em resolução original; se o sprite for ampliado significativamente durante a renderização ou houver mipmaps ativados, mais espaçamento dá uma margem maior de segurança.
O espaçamento transparente simples resolve o problema de vazamento do sprite vizinho, mas introduz outro menor: na extremidade do sprite, a filtragem pode misturar a própria cor da borda com o espaço transparente, produzindo um leve escurecimento ou halo exatamente no limite do sprite, em vez de uma borda totalmente limpa. Extrusão (às vezes chamada de extensão de borda) corrige isso preenchendo o espaçamento com uma cópia dos próprios pixels da borda do sprite, estendida para fora, em vez de deixá-lo transparente. O problema dos vizinhos é resolvido da mesma forma — ainda há um espaço entre sprites distintos —, mas a borda do próprio sprite se mistura com mais pixels dele mesmo, em vez de com transparência vazia.
A maioria das ferramentas de atlas (TexturePacker, empacotador Sprite Atlas do Unity, exportação de atlas do Godot) oferece o tamanho do espaçamento e um controle de extrusão diretamente nas configurações. Raramente há motivo para deixar o espaçamento em zero; o custo de espaço de textura de um ou dois pixels por borda de sprite é pequeno comparado ao custo de depurar artefatos intermitentes nas emendas que só aparecem em certos níveis de zoom.
Potências de dois ainda importam?
Historicamente, as GPUs exigiam dimensões de textura em potências de dois (256, 512, 1024, 2048…) para dar suporte eficiente a mipmapping e certos modos de repetição, e texturas fora desses tamanhos falhavam ou usavam um caminho de renderização mais lento. GPUs e APIs modernas (OpenGL ES 3+, Metal, Vulkan, DirectX 11+) aceitam nativamente texturas fora de potências de dois sem penalidade significativa de desempenho para renderização 2D básica, então a exigência rígida praticamente desapareceu nos dispositivos atuais.
Ainda importa em situações específicas:
- Mipmapping. Algumas plataformas e APIs antigas ainda exigem dimensões em potências de dois para gerar corretamente uma cadeia completa de mipmaps. Se seu atlas usa mipmaps, confira a restrição real da plataforma de destino em vez de presumir.
- Hardware antigo ou limitado. Dispositivos móveis básicos, contextos WebGL 1 e alguns sistemas embarcados ainda se beneficiam de dimensões em potências de dois ou as exigem.
- Alinhamento de memória e formatos de compressão. Formatos de compressão em blocos (muito usados em dispositivos móveis) comprimem em blocos de tamanho fixo e às vezes completam dimensões incompatíveis até o próximo tamanho válido de qualquer forma. Por isso, escolher desde o início um tamanho em potência de dois evita que esse preenchimento silencioso consuma memória sem necessidade.
Na prática: se o destino é um computador atual, console ou hardware móvel moderno com um fluxo 2D padrão, dimensões de atlas que não sejam potências de dois funcionam bem. Se o destino é WebGL 1, dispositivos móveis antigos ou qualquer sistema embarcado, potências de dois ainda são a opção padrão mais segura e custam pouco — a maioria dos empacotadores arredonda automaticamente o tamanho do atlas para a próxima potência de dois quando a configuração está ativada.
Corte do espaço vazio e pivôs
Corte do espaço vazio significa que o empacotador recorta cada sprite até seu conteúdo opaco real antes de colocá-lo no atlas, descartando a margem transparente ao redor. Isso quase sempre melhora a densidade de empacotamento — um sprite com muito preenchimento transparente ao redor de um personagem pequeno ocupa muito menos espaço no atlas aparado do que sem aparar.
O detalhe é que aparar muda o tamanho efetivo e o deslocamento do sprite, o que importa se a lógica do jogo ou o sistema de animação depende de todos os quadros terem as mesmas dimensões e o mesmo pivô em relação à tela de imagem original. Um quadro de ciclo de caminhada em que o personagem se inclina para a esquerda tem limites opacos diferentes de um em que se inclina para a direita; aparados independentemente, os dois quadros ficam com tamanhos diferentes, e alternar entre eles sem compensação muda a posição aparente do personagem de um quadro para outro.
Toda ferramenta de atlas que permite aparar também registra o tamanho original, sem corte, e o deslocamento junto com os dados aparados justamente para resolver isso — a engine renderiza o sprite aparado na posição correta dentro dos limites do quadro original, então o pivô permanece visualmente consistente, mesmo que menos espaço de textura seja usado para armazenar pixels transparentes. O Sprite Atlas do Unity e o importador do Godot cuidam disso automaticamente, desde que o corte seja ativado pela ferramenta, e não feito à mão antes de os sprites chegarem ao empacotador; aparar os sprites manualmente antes do empacotamento descarta os metadados de deslocamento de que a engine precisa para reposicioná-los corretamente.
Um atlas ou vários?
Colocar tudo em um único mega-atlas não é automaticamente melhor. Algumas razões práticas para dividir em vários atlas:
- Permanência em memória. Se uma fase usa apenas parte dos seus sprites, carregar um atlas por fase ou cena evita manter na memória da GPU dados de sprites não utilizados em conteúdo do qual o jogador não está próximo.
- Limites de tamanho de textura. O hardware impõe uma dimensão máxima de textura (normalmente 4096 ou 8192 em dispositivos modernos, menor em hardware móvel antigo). Um conjunto de sprites grande o suficiente não cabe em um único atlas, independentemente da eficiência do empacotamento.
- Frequência de atualização. Sprites que mudam com frequência (uma interface com iterações rápidas) se beneficiam de um atlas próprio, separado da arte estável e raramente alterada, para que uma pequena mudança não exija reconstruir e reenviar o atlas inteiro.
- Diferenças de material. Sprites que precisam de shaders ou modos de mesclagem diferentes não podem ser processados no mesmo lote, independentemente do atlas, então não há vantagem de empacotamento em combiná-los.
Um bom padrão é agrupar por contexto de uso — um atlas para os tiles de cenário de uma fase, um para os personagens e um para a interface — e só juntar mais se a análise de desempenho mostrar que as chamadas de desenho realmente são um gargalo no hardware de destino.
Perguntas frequentes
De quanto espaçamento eu realmente preciso?
Um ou dois pixels atendem à maioria dos casos na escala nativa de exibição com mipmaps desativados. Se os sprites são ampliados bastante durante a execução ou se mipmaps estão ativados, aumente esse valor — não há um número correto universal, e vale testar seu atlas nas escalas e nos níveis de zoom que o jogo realmente usa.
Por que o vazamento só aparece em certos níveis de zoom?
Esse padrão indica vazamento de mipmap, não vazamento de filtragem na resolução base — o nível específico de mipmap amostrado nessa distância foi criado a partir de uma mistura que atravessou o limite de um sprite. Extrusão e espaçamento adequado resolvem as duas causas, mas, se o problema for específico do mipmap, confirme que o espaçamento é largo o bastante para resistir à redução por vários níveis de mipmap, não apenas na textura base.
Preciso me preocupar com isso se não uso mipmaps nem redimensiono sprites?
Menos, mas não deixa de importar. Mesmo com renderização pixel-perfect em 1:1 e filtragem point, posições subpixel da câmera ou posicionamento não inteiro de sprites podem fazer a GPU amostrar um pixel fracionário que atravessa um limite. Um pouco de espaçamento é uma proteção barata mesmo em uma configuração 2D totalmente pixel-perfect.
Aparar afeta hitboxes ou formas de colisão?
Não se as formas de colisão forem definidas independentemente, o que é a configuração comum. Se o projeto deduz automaticamente os limites de colisão pelas dimensões do sprite, confira se ele lê o tamanho aparado ou o original sem corte — usar diretamente o tamanho aparado pode reduzir inesperadamente uma hitbox em quadros com muita margem transparente.
Criando sprites para empacotar em um atlas?
O Sprite Gen combina imagens individuais em uma folha, distribuindo-as em linhas com o espaçamento definido — o empacotamento em prateleiras descrito acima, em vez de uma variante de retângulos máximos, o que basta para a maioria dos conjuntos de ícones e quadros. Se seus sprites de origem ainda estão em uma folha combinada que precisa ser separada primeiro, o Recortador de folhas de sprites a divide em quadros individuais — ambos funcionam inteiramente no navegador, sem enviar nada.
Abrir Gerador de sprites