Texturas e UV Mapping para Jogos Explicado: Desdobramento, Atlas e Otimização

Entenda UV mapping em jogo na prática: como desdobrar UVs, montar atlas de textura e otimizar texturas para performance em Godot, Blender e outras engines.
Texturas e UV Mapping para Jogos Explicado: Desdobramento, Atlas e Otimização
Todo modelo 3D texturizado que você já viu num jogo passou por UV mapping. É o processo que diz pra GPU qual pedaço de uma imagem 2D vai em qual parte de uma malha 3D. Sem isso, sua textura linda de 2048x2048 não tem como "saber" onde está o rosto do personagem e onde está a sola do pé.
O UV mapping em jogo tem uma exigência a mais que em filme ou render parado: precisa ser barato. Cada decisão de desdobramento, tamanho de textura e organização afeta memória de vídeo, draw calls e tempo de carregamento. Esse artigo cobre o caminho completo: o que é UV na prática, como desdobrar sem sofrer, quando usar atlas de textura e o que otimizar antes de shipar.
O que é UV mapping em um jogo
Pensa numa caixa de papelão desmontada. Aquele papelão aberto no chão é o mapa UV da caixa: uma versão plana de um objeto 3D. UV mapping é exatamente isso, só que ao contrário: você desmonta a malha 3D num plano, pinta esse plano (a textura), e a GPU remonta tudo na hora de renderizar.
As letras U e V são só os nomes dos eixos horizontal e vertical desse plano. Não usam X e Y porque essas letras já estão ocupadas pelas coordenadas do espaço 3D. O espaço UV vai de 0 a 1 nos dois eixos, independente da resolução da textura. O canto inferior esquerdo é (0, 0) e o superior direito é (1, 1). Cada vértice da malha guarda, além da posição 3D, uma coordenada UV dizendo qual ponto da textura corresponde a ele.
Dá pra visualizar isso direto no Godot com um shader de três linhas. Aplica num material qualquer e a malha mostra as próprias coordenadas UV como cor:
shader_type spatial;
void fragment() {
// U vira vermelho, V vira verde. Onde for preto, UV é (0,0).
ALBEDO = vec3(UV.x, UV.y, 0.0);
}
Se o gradiente aparecer suave e contínuo, o desdobramento daquela região está saudável. Se aparecer esticado, repetido ou com saltos bruscos onde não devia, você acabou de achar um problema de UV sem abrir o Blender.
Desdobrar UV na prática
Desdobrar (o famoso unwrap) é decidir onde a malha vai ser "cortada" pra abrir plana. Esses cortes se chamam seams (costuras), e a habilidade central do UV mapping é escolher bem onde elas ficam.
Onde colocar as seams
A regra que uso: seam é cicatriz, então esconde onde ninguém olha. Parte interna do braço, parte de trás da cabeça, embaixo do cinto, na divisa entre dois materiais diferentes. Lugares onde uma quebra na textura já seria esperada (a costura de uma calça, a borda de uma chapa de metal) também são ótimos candidatos, porque ali a seam vira detalhe em vez de defeito.
No Blender o fluxo é direto: na workspace UV Editing, em Edit Mode, selecione as arestas onde quer cortar e marque com Edge > Mark Seam (ou Ctrl+E > Mark Seam). Depois selecione tudo com A e rode UV > Unwrap. O Blender abre a malha respeitando seus cortes e mostra o resultado no painel UV.
Dois critérios pra avaliar se o unwrap ficou bom:
- Distorção baixa. Ative o checker pattern (uma textura de xadrez de teste) no material. Os quadrados devem aparecer como quadrados na malha, não como retângulos esticados ou trapézios. Onde o xadrez deforma, a textura final vai deformar igual.
- Poucas ilhas, bem aproveitadas. Cada pedaço solto no mapa UV é uma ilha. Muitas ilhas pequenas desperdiçam espaço (cada uma precisa de margem ao redor) e multiplicam seams visíveis.
Smart UV Project: quando vale
O Blender tem o Smart UV Project, que desdobra tudo automaticamente cortando por ângulo. Pra prototipagem, props que ninguém vai olhar de perto e objetos duros tipo caixas e barris, resolve em dez segundos e está ótimo. Pra personagem, rosto, qualquer superfície orgânica que recebe textura pintada à mão, não vale: ele gera ilhas demais, em lugares imprevisíveis, e você perde o controle das seams. Personagem se desdobra na mão.
Espelhamento e sobreposição
Nem toda sobreposição de UV é erro. Se o personagem é simétrico, dá pra desdobrar só metade e espelhar a outra metade pra mesma região do UV. As duas metades compartilham os mesmos pixels da textura, e você ganha o dobro de resolução efetiva de graça. O custo: qualquer detalhe pintado aparece dos dois lados (uma tatuagem no braço esquerdo aparece no direito também), e essa região não serve pra lightmap, que precisa de UVs únicos. Por isso engines trabalham com dois canais: UV1 pra textura e UV2 pra lightmap. O Godot inclusive gera UV2 automaticamente na importação se você ativar essa opção no mesh.
Texel density: a consistência que separa amador de profissional
Texel density é quantos pixels de textura cobrem cada metro do modelo. Se o rosto do personagem usa metade da textura e o torso inteiro usa um canto, o rosto fica nítido e o torso borrado, e a diferença grita na tela. Mantenha a densidade consistente entre as partes do modelo e entre modelos que aparecem lado a lado no jogo. A exceção é proposital: dá mais densidade pra onde a câmera olha (rosto, mãos em primeira pessoa, a arma do FPS) e menos pra onde ela quase nunca chega (sola do pé, interior de bolso).
Atlas de textura: várias texturas numa imagem só
Um atlas de textura é uma imagem grande que reúne várias texturas menores, com cada objeto apontando pra sua região via UV. Em vez de dez props com dez texturas de 512, você tem um atlas de 2048 com os dez props dentro.
O motivo é performance. Cada vez que a GPU troca de material ou textura entre um objeto e outro, existe um custo de estado, e objetos com materiais diferentes não podem ser agrupados no mesmo draw call. Quando vários objetos compartilham o mesmo atlas e o mesmo material, a engine consegue desenhar todos juntos. Em cena com centenas de props, a diferença é real.
No 2D a mesma lógica vale pra sprites, e o Godot tem um recurso pronto pra isso, o AtlasTexture, que recorta uma região de uma imagem maior:
var atlas := AtlasTexture.new()
atlas.atlas = preload("res://sprites/props_atlas.png")
atlas.region = Rect2(64, 0, 32, 32) # recorta a partir de (64,0), 32x32 pixels
$Sprite2D.texture = atlas
Vários sprites apontando pro mesmo props_atlas.png com regiões diferentes deixam o batching do renderizador 2D trabalhar em paz.
No 3D, uma variação que vale conhecer é a trim sheet: um atlas de detalhes repetíveis (bordas de metal, frisos, parafusos, barras) onde você mapeia faces do modelo direto pras tiras que precisa. É a técnica por trás de muito cenário industrial e sci-fi de jogo grande, porque um único material texturiza dezenas de modelos diferentes.
O cuidado clássico com atlas é o bleeding: quando a GPU gera mipmaps ou filtra a textura, pixels de uma região vizinha vazam pra dentro da sua. A solução é padding, uma margem de segurança de alguns pixels ao redor de cada ilha, preenchida com a cor da borda expandida. Ferramentas de bake e empacotadores de atlas fazem isso por você, só não deixe as ilhas coladas umas nas outras.
Otimização: o que revisar antes de shipar
Textura costuma ser o maior consumidor de memória de vídeo de um jogo. Esses são os pontos que reviso, em ordem de impacto.
Resolução honesta. A pergunta não é "qual o máximo que fica bonito", é "qual o mínimo que ninguém nota". Um prop que ocupa 80 pixels na tela não precisa de textura 2048. Teste baixando a resolução até a degradação aparecer, e fique um passo acima disso.
Potência de dois. Texturas em 256, 512, 1024, 2048. Formatos de compressão de GPU trabalham em blocos e a cadeia de mipmaps se comporta melhor assim. Textura 1000x700 é pedir problema à toa.
Compressão de VRAM ligada. No Godot, o painel de importação de cada textura tem o modo de compressão. VRAM Compressed mantém a textura comprimida na memória da GPU (formatos como S3TC no desktop e ETC2 no mobile), ocupando uma fração do espaço de uma textura crua. É o modo certo pra praticamente toda textura 3D. Lossless fica pra pixel art e UI, onde artefato de compressão aparece.
Mipmaps ligados no 3D. Mipmap é a cadeia de versões reduzidas da textura que a GPU usa conforme o objeto fica distante. Sem mipmap, textura ao longe cintila (aliasing) e ainda custa mais cache. No Godot é um checkbox na importação, e no 3D ele deve estar sempre marcado. No 2D puro com pixel art, desligado.
UV sem desperdício. Espaço vazio no mapa UV é memória paga sem retorno. Depois do unwrap, empacote as ilhas pra preencher o quadrado: o UV > Pack Islands do Blender resolve o grosso, e nos modelos importantes vale ajustar na mão pra dar mais área ao que a câmera vê.
Erros que aparecem em todo primeiro projeto
Textura esticada. Quase sempre é ilha com distorção alta ou face que ficou fora do unwrap. O shader de visualização lá de cima, ou um checker pattern, mostra onde.
Seam gritando na superfície. Ou a seam ficou em lugar exposto demais, ou as duas bordas da costura receberam pinturas que não casam. Reposicionar a seam resolve o primeiro caso; pintar sobre a costura num software 3D (em vez de pintar o mapa plano no Photoshop) resolve o segundo.
Tudo borrado de longe, nítido de perto. Falta de mipmap ou filtro errado. Confere a importação.
Modelo importado sem UV nenhum. Acontece com malha gerada por código ou exportação mal configurada. A textura aparece como uma cor só, porque todos os vértices apontam pro mesmo ponto da textura. O unwrap precisa existir antes da exportação.
Fechando
UV mapping parece assunto de artista, mas é fundação técnica: programador que entende UV debuga problema de textura em minutos e conversa de igual pra igual com o time de arte. O essencial cabe em quatro decisões: seams escondidas em lugar discreto, texel density consistente, atlas onde houver muitos objetos pequenos, e importação com compressão e mipmaps certos.
Se quiser fixar, pega um modelo simples (uma caixa de madeira serve), desdobra na mão no Blender, pinta um checker, aplica o shader de visualização no Godot e compara com o resultado do Smart UV Project. Meia hora de exercício e o conceito gruda de um jeito que leitura nenhuma entrega.


