Texturas

Siga estas práticas recomendadas para otimizar a aparência e o desempenho das texturas no seu jogo Android.

Texturas são um elemento essencial da arte 3D. Os jogos 3D que funcionam bem no maior número de dispositivos contam com arte 3D projetada para aproveitar melhor os processadores gráficos. Este guia destaca as otimizações e as práticas recomendadas para texturas em dispositivos móveis para que seu jogo tenha um melhor desempenho e consumo de bateria, enquanto mantém uma alta qualidade visual.

Partes deste artigo são baseadas em trabalhos disponibilizados pela Arm Limited e protegidos por direitos autorais.

Criar atlas de texturas

Um atlas de texturas é uma textura projetada para conter os dados de imagem de vários objetos gráficos, como malhas em 3D ou sprites em 2D. O atlas de texturas é usado para combinar as imagens de cada objeto, de modo que cada objeto não precise ter uma textura própria.

Malhas que compartilham um atlas de texturas
Figura 1. O destaque em amarelo na cena renderizada (à esquerda) contorna as malhas que compartilham o atlas de texturas (à direita).

Minimizar o número de chamadas de desenho dos frames do jogo é importante para alcançar um desempenho de renderização ideal. Usar a mesma textura em objetos diferentes é um modo de combiná-los em uma única chamada de desenho. Reduzir as chamadas de desenho é especialmente importante para jogos vinculados à CPU, já que cada chamada de desenho gera sobrecarga ao ser processada pelo driver gráfico. Os atlas de texturas também reduzem o número de arquivos de recursos de textura nos dados de tempo de execução do jogo. Centenas ou até milhares de texturas podem ser consolidadas em um número muito menor de arquivos de atlas de texturas.

Planeje o layout do atlas de texturas ao criar malhas em 3D. Se o atlas for criado antes da criação do recurso de malha, ele precisará ser desagrupados com UV após o atlas de texturas. Se o atlas for criado após a criação do recurso de malha, usando ferramentas de combinação ou de criação de atlas em um software de pintura, a ilha UV precisará ser reorganizada de acordo com a textura.

Agrupamento de chamadas de desenho específicas do mecanismo

O mecanismo de jogos do Unity tem um recurso de agrupamento de chamadas de desenho que pode combinar objetos automaticamente. Para que os objetos possam ser agrupados dessa forma, eles precisam compartilhar um material em comum, incluindo a textura, e ser marcados como estáticos.

No Unreal Engine 4, é necessário configurar o agrupamento manualmente. Você pode mesclar objetos em um software 3D antes de importá-los para o Unreal. O Unreal também inclui a ferramenta UE4 Actor Merging, que pode combinar malhas e criar arquivos de atlas de texturas.

Gerar mipmaps

Os mipmaps são versões de resolução mais baixa de uma textura. A coleção de mipmaps de determinada textura é chamada de cadeia de mipmaps. Cada nível de mipmap subsequente em uma cadeia tem uma resolução mais baixa do que o nível anterior. Os mipmaps são usados para implementar o nível de detalhamento (LOD, na sigla em inglês) da textura durante a renderização. Quando uma textura em mipmap é vinculada a um estágio de textura, o hardware gráfico usa o espaço de textura ocupado por um fragmento para escolher um nível na cadeia de mipmap. Ao renderizar uma cena 3D, um objeto que se encontra longe da câmera usará um mipmap de resolução mais baixa do que esse mesmo objeto usaria se estivesse mais perto da câmera.

Uma textura em mipmap usa mais memória se comparada a uma textura que não está em mipmap. Os níveis extras de mipmap fazem com que o consumo de memória de uma textura aumente em 33%. Nos casos em que uma textura é desenhada a uma distância fixa da câmera, gerar mipmaps é um uso desnecessário da memória.

Cadeia de mipmaps com uma resolução de textura básica de 512x512 pixels
Figura 2. Uma cadeia de mipmaps com resolução básica da textura de 512x512 pixels.

O uso correto de mipmaps melhora o desempenho da GPU. A disponibilidade de níveis de mipmap de resolução mais baixa reduz o uso da largura de banda da memória e melhora o armazenamento do cache de textura.

A criação de mipmaps também pode melhorar a qualidade visual, reduzindo o efeito serrilhado nas bordas da textura. O serrilhado nas bordas de uma textura pode ser observado como um efeito trêmulo em áreas mais distantes da câmera.

Exemplo de serrilhado nas bordas de uma textura
Figura 3. Um exemplo de serrilhado nas bordas de uma textura. Imagem renderizada sem mipmaps (à esquerda) e imagem renderizada com mipmaps (à direita). O serrilhado nas bordas da textura pode ser observado dentro do retângulo vermelho na imagem à esquerda.

Detalhes de mipmap específicos do mecanismo

O Unreal Engine 4 exige dimensões de textura que sejam potências de dois (por exemplo, 512x1024, 128x128) para o uso de mipmap. As cadeias de mipmaps não serão geradas se uma ou ambas as dimensões da textura não forem uma potência de dois.

O mecanismo do Unity dimensiona automaticamente texturas com dimensões que não são uma potência de dois para criar mipmaps. Para que isso não aconteça, verifique se as dimensões dos arquivos de textura de origem estão em potência de dois.

Selecionar os modos de filtragem de textura adequados

A filtragem de texturas é um recurso de renderização de hardware que afeta a aparência visual de um triângulo renderizado. O uso adequado da filtragem de textura pode melhorar a qualidade visual de uma cena. Existem vários modos de filtragem de textura, e cada um deles apresenta um equilíbrio diferente entre melhoria de renderização e custo. O custo inclui o tempo de cálculo e o uso de largura de banda de memória. Três tipos de filtragem de textura que geralmente estão disponíveis são: mais próxima (ou de ponto), bilinear e trilinear. A filtragem anisotrópica é um método extra de filtragem de textura que pode ser combinada com a filtragem bilinear ou trilinear.

Mais próxima

O modo de filtragem de textura mais próxima é o mais simples e menos dispendioso. A filtragem mais próxima cria uma amostra de um único texel usando as coordenadas especificadas na textura de origem. Os triângulos renderizados com a filtragem mais próxima têm uma aparência pixelada, ou em blocos, principalmente quando renderizados próximos à câmera.

Bilinear

A filtragem bilinear analisa os quatro texels ao redor das coordenadas especificadas na textura de origem. Esses quatro texels são calculados para determinar a cor da textura do fragmento. A filtragem bilinear resulta em um gradiente mais suave entre os pixels, evitando a aparência pixelada da filtragem mais próxima. Os triângulos renderizados perto da câmera terão uma aparência desfocada, e não pixelada. A filtragem bilinear é mais dispendiosa que a filtragem mais próxima, devido à criação de mais amostras de texel e aos cálculos de valores médios.

Comparação entre filtragem mais próxima e bilinear
Figura 4. Comparação da filtragem de textura mais próxima (à esquerda) e bilinear (à direita).

Trilinear

Ao renderizar uma malha em que a distância dos vértices da câmera varia, diversos níveis de mipmap podem ser selecionados durante a renderização. As mudanças entre dois níveis de mipmap podem resultar em um corte acentuado perceptível no ponto de transição. A filtragem trilinear suaviza essas transições, executando a filtragem bilinear em dois níveis de mipmap diferentes e interpolando os resultados. Por usar vários níveis de mip e interpolação, a filtragem trilinear é mais dispendiosa do que a bilinear do ponto de vista computacional.

Comparação entre filtragem bilinear e trilinear
Figura 5. Comparação da filtragem de textura bilinear (à esquerda) e trilinear (à direita). A região com zoom contrasta a diferença na renderização ao longo de transições de mipmap.

Anisotrópica

A filtragem anisotrópica aumenta a qualidade visual das malhas texturizadas que são renderizadas em um ângulo extremo em relação à câmera. O plano do solo é um exemplo comum desse tipo de malha. A filtragem anisotrópica precisa de texturas em mipmap para funcionar. É possível configurar a proporção ou o nível de filtragem anisotrópica aplicada durante a renderização. O custo da filtragem anisotrópica aumenta à medida que o nível aumenta.

Comparação entre filtragem anisotrópica de 1x e 2x
Figura 6. Comparação entre filtragem anisotrópica bilinear/1x (à esquerda) e filtragem anisotrópica bilinear/2x (à direita)

Estratégia de seleção de modo

A filtragem bilinear geralmente oferece o melhor equilíbrio entre desempenho e qualidade visual. A filtragem trilinear exige uma largura de banda de memória significativamente maior e, por isso, precisa ser usada de forma criteriosa. Em muitos casos, a filtragem bilinear combinada com a filtragem anisotrópica de 2x terá resultado e desempenho melhores que a filtragem anisotrópica de 1x. É extremamente dispendioso aumentar os níveis anisotrópicos para além de 2x. Assim, isso precisa ser feito de forma muito criteriosa, somente para recursos importantes do jogo.

A filtragem de texturas pode representar até metade do consumo total de energia da GPU. Escolher filtros de textura mais simples sempre que possível é uma excelente forma de reduzir as demandas de energia do jogo.

Otimizar os tamanhos das texturas

É importante ter texturas com a menor dimensão possível, sem deixar de atingir a qualidade de imagem desejada. Revise os recursos de textura para verificar se existem texturas desnecessariamente grandes. Esse princípio se aplica tanto a texturas separadas quanto aos atlas de texturas. Se o jogo for compatível com vários dispositivos, abrangendo uma grande variedade de recursos de resolução e desempenho, crie versões de baixa e de alta resolução dos recursos de textura para cada tipo de dispositivo.

Ao renderizar uma malha que usa várias texturas no material, considere reduzir a resolução de algumas texturas específicas. Por exemplo, ao usar uma textura difusa de 1.024x1.024, pode ser possível reduzir o mapa de textura áspera ou metálica para 512x512 sem gerar um grande impacto na qualidade da imagem. Verifique o impacto de todas as tentativas de redimensionamento para garantir que elas não comprometam o nível de qualidade desejado.

Usar o espaço de cores adequado

Muitos pacotes de software usados para a criação de textura são operados e exportados usando o espaço de cores sRGB. Texturas difusas, que são processadas como cores, podem usar o espaço de cores sRGB. Texturas que não são processadas como cores, como mapas de textura metálica, áspera ou normal, não podem ser exportadas usando o espaço de cores sRGB.

As configurações de textura dos mecanismos de jogos incluem um parâmetro para indicar se uma textura usa o espaço de cores sRGB.

Configurações de textura sRGB no Unity e no Unreal Engine 4
Figura 7. Configurações de textura sRGB no Unity (à esquerda) e no Unreal Engine 4 (à direita).

Como os dados de pixel dessas texturas não são usados como dados de cor, o uso do espaço de cores sRGB produzirá elementos visuais incorretos.

Comparação entre a renderização de um mapa metálico de aspereza em um espaço de cores linear e um espaço de cores sRGB
Figura 8. Mapa metálico de aspereza linear (não sRGB) (à esquerda) e mapa metálico de aspereza sRGB (à direita). A aparência dos reflexos à direita está incorreta.

Usar a compactação de texturas

A compactação de texturas é um algoritmo de compactação de imagens aplicado a dados de pixel não compactados, que resulta em uma textura que pode ser descompactada rapidamente por um hardware gráfico durante a renderização. O uso correto da compactação de texturas pode reduzir o uso de memória e aumentar o desempenho, gerando um impacto mínimo na qualidade visual. Existem três algoritmos de compactação de texturas mais conhecidos no Android: ETC1, ETC2 e ASTC. Para jogos modernos, o ASTC geralmente é a melhor opção. O ETC2 é uma alternativa caso o jogo seja destinado a dispositivos não compatíveis com o ASTC.

ETC1

O ETC1 oferece suporte a todos os dispositivos Android. Entretanto, ele oferece suporte apenas para um único modo de dados de cores RGB, de quatro bits por pixel. O ETC1 não oferece suporte para canais alfa. Muitos mecanismos de jogos com suporte ao ETC1 permitem designar uma segunda textura de ETC1, usada para representar os dados do canal Alfa.

ETC2

O ETC2 é compatível com mais de 90% dos dispositivos Android ativos. Dispositivos muito antigos, que não são compatíveis com a API OpenGL ES 3.0, não conseguem usar o ETC2. Comparado ao ETC1, o ETC2 oferece o seguinte:

  • Compatibilidade com canal Alfa, com punchthrough de oito bits e de bit único
  • Versões de sRGB para texturas RGB e RGBA
  • Texturas com um e dois canais, R11 e RG11

ASTC

O ASTC é compatível com mais de 75% dos dispositivos Android ativos. O ASTC tem tamanhos de blocos de compactação configuráveis, que oferecem controle detalhado para que seja possível equilibrar a taxa de compactação em relação à qualidade da imagem de uma determinada textura. Comparado ao ETC2, o ASTC geralmente permite atingir uma qualidade superior, usando o mesmo tamanho de memória, ou uma qualidade semelhante, usando um tamanho de memória menor.

Comparação visual dos formatos de compactação de textura usando a mesma imagem de origem
Figura 9. Uma comparação entre imagens: não compactada (à esquerda, com 17 MB), compactada com ETC1 (no centro, com 3 MB) e compactada com ASTC (à direita, com 2, 5 MB).

Velocidade de compactação de texturas

A compactação de texturas poderá demorar muito se o jogo tiver muitas texturas. O ETC e o ASTC oferecem diferentes configurações de qualidade de compactação que podem ser selecionadas. Configurações de qualidade mais alta precisam de mais tempo para compactação. Durante o desenvolvimento, é recomendado diminuir o nível de qualidade, para diminuir o tempo de compactação, e aumentar o nível de qualidade somente antes de criar builds importantes.

Compactação de texturas em mecanismos de jogo

Se você estiver usando um mecanismo de jogo, poderá ser necessário escolher o formato de compactação de textura (ETC ou ASTC) no nível do projeto. Oferecer o máximo de compatibilidade, com suporte a vários formatos de compactação, é mais trabalhoso. O recurso de segmentação de formato de compactação de textura do Google Play Asset Delivery pode ajudar você a incluir vários formatos no jogo e a fornecer apenas o formato ideal para cada dispositivo no momento da instalação.

Desagrupar UVs

Mantenha a ilha UV o mais alinhada possível. Isso ajudará a textura das seguintes formas:

  • Agrupar as ilhas UV será mais fácil, resultando em menor desperdício de espaço.
  • UVs alinhados reduzem o efeito serrilhado nas texturas.
  • Um bom agrupamento de UVs garante uma resolução ideal para a textura.
  • Melhor qualidade na criação de texturas, mesmo que os UVs não estejam perfeitamente alinhados.
Comparação entre uma ilha UV não otimizada e uma ilha UV otimizada
Figura 10. Uma ilha UV não otimizada (à esquerda) e uma ilha UV endireitada/desagrupada (à direita).

Emendas de texturas visíveis resultam em uma aparência ruim. Tente colocar as emendas de UVs em locais em que elas ficarão menos visíveis. Para ajudar a criar mapas normais melhores, divida a ilha UV nas áreas em que as bordas são acentuadas e deixe um pouco de espaço ao redor da ilha.

Evitar detalhes imperceptíveis

Ao criar uma arte, não inclua detalhes que não serão vistos, especialmente em jogos projetados para dispositivos com telas menores. Seria um desperdício criar uma textura extremamente detalhada de 4.096x4.096 para um modelo de cadeira pequena no canto de uma cena, que mal será notada. Em alguns casos, pode ser necessário exagerar nas bordas, aumentando o realce, e no sombreamento para melhorar a percepção das formas.

Textura de menor qualidade usada em um modelo renderizado à distância
Figura 11. Uma textura pequena de 256 x 256 sem excesso de detalhes é usada em um modelo de soldado renderizado à distância.

Detalhes de bake

Os dispositivos móveis têm telas menores e hardwares gráficos menos potentes que computadores ou consoles de jogos. Em vez de calcular efeitos como oclusão de ambiente ou realce especular no momento de execução, considere incorporá-los como bake na textura difusa, quando possível. Isso ajuda no desempenho e garante a visibilidade dos detalhes.

Realces e oclusão de ambiente incorporados como bake em uma textura difusa
Figura 12. Realces e oclusão de ambiente incorporados como bake na textura difusa (à esquerda) e renderizados no jogo (à direita).

Usar tonalidades de cores

Se você conseguir criar sombreadores personalizados e usar malhas com um esquema de cor semelhante ou uniforme, considere usar tonalidades de cores nas malhas aplicáveis. Ao usar as tonalidades de cores, uma escala de cinza será aplicada à textura, ocupando menos espaço de memória que uma textura RGB. Os dados de cores por vértice são aplicados pelo sombreador para colorir a malha. Um método de aplicação de tonalidade alternativo é usar uma máscara RGB e aplicar a textura de acordo com o intervalo de cores da máscara.

Textura em escala de cinza colorida durante a execução
Figura 13. Uma textura em escala de cinza (à esquerda) colorida durante a execução do modelo de pilar (à direita).

Agrupar canais de textura

Ao renderizar materiais com várias texturas, procure oportunidades de agrupar diferentes texturas que usem apenas um canal de cores em uma única textura que use todos os três canais de cores. Dessa forma, você reduzirá o uso de memória e diminuirá o número de operações de amostra de textura executadas pelo sombreador de fragmentos.

Três texturas de canal único combinadas em uma textura multicanal
Figura 14. Três texturas de canal único (à esquerda) são combinadas em uma textura multicanal (à direita). Os dados de oclusão de ambiente são atribuídos ao canal vermelho, o mapa de aspereza/suavidade ao verde e o mapa metálico ao azul.

Ao agrupar texturas, atribua os dados com mais detalhes ao canal verde. Como o olho humano é mais sensível ao verde, o hardware gráfico geralmente atribui mais bits ao canal verde. Por exemplo, um mapa de aspereza/suavidade geralmente possui mais detalhes que um mapa metálico. Dessa forma, é melhor atribuir esse mapa ao canal verde.

Caso você esteja usando apenas dois canais na textura agrupada, considere colocar os dados do canal Alfa, para materiais que usam esse canal, na textura agrupada, em vez da textura difusa. Dependendo do formato da sua textura difusa, isso pode ajudar a reduzir o tamanho ou a aumentar a qualidade visual, já que omite os dados do canal Alfa.

Canal Alfa agrupado com outra textura
Figura 15. Um mapa de opacidade do canal Alfa é agrupado com uma textura, junto de um mapa de aspereza/suavidade e um mapa metálico.

Verifique se as texturas agrupadas estão definidas como um espaço de cores linear RGB, e não sRGB.

Criar mapas normais

O mapeamento normal é uma técnica que proporciona ao modelo 3D uma aparência detalhada, sem precisar de mais recursos geométricos. Recursos como pregas ou parafusos, que podem precisar de muitos triângulos para serem modelados, podem ser simulados usando um mapa normal. O mapeamento normal pode ou não ser a melhor opção, dependendo do estilo de arte e da direção do jogo.

Modelo renderizado com e sem um mapa normal
Figura 16. Um modelo renderizado sem um mapa normal (à esquerda), o mesmo modelo renderizado com um mapa normal (no centro) e a textura do mapa normal (à direita).

Os mapas normais geram custos de desempenho e precisam ser usados com moderação em projetos para dispositivos mais simples. O mapa normal precisa de mais texturas, resultando na criação de mais amostras de texturas e execução de cálculos de sombreador de fragmentos.

Práticas recomendadas para mapas normais

Veja algumas práticas recomendadas para a criação de mapas normais:

Usar um limitador

Um limitador é uma versão maior ou mais abrangente do modelo com poucos polígonos. Ele precisa abranger o modelo com vários polígonos para funcionar bem durante o baking do mapa normal. O limitador é usado para limitar a distância do raycast durante o baking do mapa normal e ajuda a evitar problemas com emendas divididas nesse mapa.

Limitador envolvendo a malha de polígonos baixos
Figura 17. Um limitador ao redor da malha de polígonos baixos.
Modelo renderizado usando um mapa normal com e sem limitador
Figura 18. Um modelo renderizado usando um mapa normal gerado com limitador (à esquerda) em comparação com um modelo renderizado usando um mapa normal gerado sem limitador (à direita).

Correspondência de bake por nome de malha

Caso seu software ofereça suporte para isso, adicione a correspondência de bake por nome da malha. Esse recurso minimiza o problema de projeção errada do mapa normal. Quando estão muito próximos uns dos outros, os objetos podem projetar o mapa normal sobre a face errada. A correspondência por nome de malha garante que o baking seja executado apenas na superfície correta. Para ver mais informações sobre esse recurso no Substance Painter, consulte esta página. Para ver mais informações sobre esse recurso no Marmoset Toolbag, consulte esta página.

Separar a malha

Caso não seja possível aplicar a correspondência por nome de malha durante o baking, considere separar a malha. A separação da malha afasta as partes umas das outras para que o mapa normal não seja projetado sobre a superfície errada. Se o baking estiver sendo usado também para a oclusão do ambiente, poderá ser necessário executar esse bake isoladamente, com uma malha que não tenha sido separada.

Malha separada para baking do mapa normal
Figura 19. Uma malha separada para baking do mapa normal.

Minimizar emendas

O uso de UVs contínuos em bordas definidas resultará em emendas visíveis. Para minimizar esse efeito, divida os UVs de bordas definidas. Como regra geral, ao definir grupos de suavização, é recomendado manter ângulos inferiores a 90 graus. Em triângulos, as emendas de UVs precisam ter um grupo de suavização diferente.