Voltar para o Blog
Quest Log

Outline na Unity: Contorno com Shader Graph

Baú de tesouro 3D e um personagem 2D lado a lado, ambos com contorno amarelo grosso destacando a silhueta

Unity Shader Graph outline em duas técnicas: contorno de sprite 2D pelo alpha deslocado e inverted hull em 3D, com Renderer Feature e destaque com o mouse.

Ferramenta:Unity

Outline na Unity: Contorno com Shader Graph

Para fazer outline no Unity Shader Graph você tem duas técnicas principais: em sprite 2D, amostrar o alpha da textura deslocado em várias direções e pintar onde aparece pixel vizinho mas não o pixel atual; em objeto 3D, o inverted hull, que desenha uma cópia do modelo inflada e com as faces invertidas, deixando só a borda visível. Uma terceira opção, o contorno de tela cheia por Renderer Feature, entra quando você quer o jogo inteiro com cara de desenho.

Aqui você monta as duas técnicas no URP do Unity 6.3 LTS, liga um destaque de contorno quando o mouse passa sobre um objeto e sai sabendo qual escolher em cada caso.

Técnicas de outline na Unity

TécnicaOnde brilhaCustoProblema clássico
Alpha deslocado (sprite)Personagens e itens 2DBaixo, 4 a 8 amostras de texturaContorno cortado na borda do sprite
Inverted hull (3D)Objeto selecionado, personagem toonDesenha o objeto duas vezesBuraco em arestas duras
Tela cheia (pós-processo)Estilo cartoon em tudoFixo por pixel de telaContorna o que não devia, difícil isolar um objeto

Não existe técnica que vença em tudo. Item coletável num jogo 2D: alpha deslocado. Baú que o jogador pode abrir num jogo 3D: inverted hull. Jogo inteiro com traço de quadrinho: tela cheia.

Outline 2D em sprite com Shader Graph

A ideia: para cada pixel, olhe os vizinhos a uma distância igual à espessura do contorno. Se algum vizinho é opaco e o pixel atual é transparente, esse pixel é contorno.

  1. Create > Shader Graph > URP > Sprite Unlit Shader Graph (ou Sprite Lit, se a cena usa luz 2D). Nomeie SpriteOutline.
  2. Na Blackboard, crie:
    • MainTex (Texture2D) com Reference _MainTex. Com esse nome, o Sprite Renderer entrega o sprite para o shader sozinho.
    • OutlineColor (Color), amarelo.
    • OutlineThickness (Float), 1. É a espessura em texels (pixels da textura).
  3. Adicione o nó Texel Size ligado à MainTex. Ele devolve a largura e a altura de um texel em UV. Multiplique pelo OutlineThickness: esse é o deslocamento.
  4. Faça quatro cópias de Sample Texture 2D, todas com MainTex. Em cada uma, a entrada UV recebe o nó UV somado a um deslocamento diferente: (+x, 0), (-x, 0), (0, +y), (0, -y). Use Vector 2 e Add para montar cada UV.
  5. Pegue o canal A das quatro amostras e combine com Maximum (três nós em cascata). Esse é o "alpha dos vizinhos".
  6. Amostre a textura no UV original. Subtract: alpha dos vizinhos menos alpha original. Saturate no resultado. Isso é a máscara do contorno.
  7. Lerp: A = cor do sprite (RGB da amostra original), B = OutlineColor, T = máscara do contorno. Ligue em Base Color.
  8. Alpha final: Maximum entre o alpha original e a máscara. Ligue em Alpha.

Crie um material com o graph e aplique no Sprite Renderer. Para contorno em 8 direções (cantos mais redondos), acrescente as diagonais. Em pixel art, 4 direções dá exatamente o contorno de 1 pixel que um artista desenharia à mão.

Outline 3D com inverted hull

Inverted hull é o truque mais velho dos jogos toon: desenhe o objeto de novo, um pouco maior, renderizando só as faces de trás e com cor sólida. A cópia fica escondida atrás do original, exceto na silhueta, onde aparece como contorno.

  1. Create > Shader Graph > URP > Unlit Shader Graph. Nomeie OutlineHull.
  2. No Graph Settings, troque Render Face para Back. Essa é a parte "inverted".
  3. Propriedades: OutlineColor (Color) e OutlineWidth (Float, 0,02).
  4. No bloco Vertex, monte: nó Normal Vector (Space: Object) multiplicado por OutlineWidth, somado ao nó Position (Space: Object). Ligue em Position do Vertex.
  5. Ligue OutlineColor em Base Color do Fragment.

Crie o material M_OutlineHull e aplique como segundo material no Mesh Renderer (aumente o tamanho da lista Materials para 2). O primeiro material é o normal do objeto; o segundo desenha o contorno.

Espessura fixa no espaço do objeto fica mais grossa em objeto escalado. Se a sua cena tem objetos de tamanhos muito diferentes, cada material precisa do próprio OutlineWidth, ou você converte o deslocamento para espaço de tela, o que já é um passo além deste post.

Outline por Renderer Feature (pós-processo)

Renderer Feature é um passo extra que o URP executa no pipeline. Dois servem para contorno sem escrever C#:

Render Objects. No asset do Universal Renderer, Add Renderer Feature > Render Objects. Configure:

  • Event: After Rendering Opaques.
  • Filters > Queue: Opaque. Layer Mask: uma layer nova chamada Outlined.
  • Overrides > Override Mode: Material, e aponte para o M_OutlineHull.

Agora qualquer objeto na layer Outlined ganha o contorno automaticamente, sem segundo material. É a base do destaque com mouse da próxima seção.

Full Screen Pass Renderer Feature. Desenha um material sobre a tela inteira. Em Pass Material, o botão New cria um ShaderGraph Fullscreen a partir de um template. Em Requirements, peça Depth e Normal para o shader poder comparar profundidade e normal entre pixels vizinhos e pintar onde há salto grande: isso é detecção de borda, o contorno estilo desenho. Injection Point padrão é After Rendering Post Processing; para o contorno ficar por baixo do bloom, use Before Rendering Post Processing.

Uma nota do Unity 6.3: Renderer Features próprios, escritos em C#, precisam implementar o Render Graph (RecordRenderGraph). O modo de compatibilidade com a API antiga fica escondido atrás de um define e projetos atualizados abrem com Render Graph ligado. Tutorial antigo com Execute e cmd.Blit não vai rodar. Render Objects e Full Screen Pass já estão prontos e evitam esse problema.

Destacar objeto ao passar o mouse

Com o Render Objects configurado para a layer Outlined, destacar um objeto é só trocar a layer dele. Crie a layer Outlined em Project Settings > Tags and Layers.

Arquivo OutlineOnHover.cs (coloque na câmera):

using UnityEngine;
using UnityEngine.InputSystem;

public class OutlineOnHover : MonoBehaviour
{
    [SerializeField] private Camera targetCamera;
    [SerializeField] private LayerMask selectableMask;
    [SerializeField] private string outlineLayerName = "Outlined";
    [SerializeField] private float maxDistance = 100f;

    private int outlineLayer;
    private GameObject current;
    private int originalLayer;

    private void Awake()
    {
        if (targetCamera == null)
        {
            targetCamera = Camera.main;
        }
        outlineLayer = LayerMask.NameToLayer(outlineLayerName);
    }

    private void Update()
    {
        if (Mouse.current == null)
        {
            return;
        }

        Vector2 mousePosition = Mouse.current.position.ReadValue();
        Ray ray = targetCamera.ScreenPointToRay(mousePosition);

        GameObject hitObject = null;
        if (Physics.Raycast(ray, out RaycastHit hit, maxDistance, selectableMask))
        {
            hitObject = hit.collider.gameObject;
        }

        if (hitObject == current)
        {
            return;
        }

        ClearCurrent();

        if (hitObject != null)
        {
            current = hitObject;
            originalLayer = hitObject.layer;
            hitObject.layer = outlineLayer;
        }
    }

    private void OnDisable()
    {
        ClearCurrent();
    }

    private void ClearCurrent()
    {
        if (current != null)
        {
            current.layer = originalLayer;
            current = null;
        }
    }
}

Dois detalhes que derrubam esse script na primeira tentativa:

  • O selectableMask precisa incluir a layer original dos objetos e a Outlined. Sem ela, o raycast para de acertar o objeto assim que ele muda de layer e o contorno pisca.
  • O script troca a layer do objeto que tem o collider. Se o mesh está num filho, mova o collider para o mesmo objeto do Mesh Renderer ou troque a layer dos filhos também.

O mouse é lido com o Input System (Mouse.current), que vem ativo nos projetos novos do Unity 6.

Contorno no item, dissolve no inimigo, partícula no golpe: cada efeito é fácil sozinho. O que trava quem aprende sozinho é amarrar isso tudo num jogo que roda, com interação, estado e performance aguentando. No curso de Unity esses efeitos entram dentro de projetos completos, no lugar em que o jogo precisa deles.

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

Erros comuns: contorno cortado no sprite e quebrado em arestas duras

Contorno cortado no sprite. O contorno é desenhado nos pixels transparentes em volta do desenho. Se a arte encosta na borda da textura, não sobra espaço. Deixe pelo menos a espessura do contorno de margem transparente e, no import do sprite, troque Mesh Type para Full Rect. Com Tight, a Unity recorta a malha justa no desenho e corta o contorno junto.

Contorno vazando de um frame para o outro na sprite sheet. Os frames estão colados. Fatie com espaço entre eles ou adicione padding na exportação da sheet.

Contorno 3D quebrado nas quinas. Em aresta dura, os vértices duplicados têm normais diferentes e se afastam em direções diferentes. Soluções: suavizar o shading do modelo, ou gravar normais suavizadas num canal extra (UV ou cor de vértice) e usá-las só no graph do contorno.

Contorno aparecendo na frente do objeto. O Render Face está em Front ou Both. Volte para Back.

Contorno sumindo atrás de outros objetos. É o comportamento normal com teste de profundidade. Se você quer ver o contorno através das paredes, configure Depth Test no Render Objects para passar sempre, e aceite que isso também mostra o contorno em lugares que talvez você não queira.

Material rosa. Graph sem target Universal ou projeto fora do URP.

Qual técnica escolher

  • Jogo 2D com sprite: alpha deslocado. Um material, nenhuma configuração de pipeline.
  • Jogo 3D, destaque de interação: inverted hull via Render Objects e troca de layer.
  • Jogo 3D toon em tudo: Full Screen Pass com detecção de borda por profundidade e normal.
  • Mobile modesto: evite o pós-processo de tela cheia e prefira inverted hull só nos objetos que precisam.

Para aprofundar o lado de game feel, o post sobre game feel e juice mostra por que um contorno que aparece no momento certo comunica interação melhor que um texto na tela. Se você usa as duas engines, compare com o outline em shader na Godot, que resolve o mesmo problema com shader escrito à mão. E para entender nós, propriedades e targets com calma, o guia de Shader Graph na Unity é o ponto de partida.

Perguntas frequentes

Qual a melhor forma de fazer outline na Unity?

Depende do que você quer contornar. Em sprite 2D, um Shader Graph que amostra o alpha deslocado é simples e leve. Em objeto 3D isolado, inverted hull resolve com um material extra. Para contorno em tudo da tela, estilo desenho, use um efeito de tela cheia com Full Screen Pass Renderer Feature.

Por que o outline do meu sprite aparece cortado?

O contorno é desenhado fora do desenho, e o sprite não tem pixels transparentes sobrando em volta. Deixe uma margem transparente de pelo menos a espessura do contorno na arte e troque o Mesh Type do sprite para Full Rect no import.

Por que o outline 3D quebra nas quinas do modelo?

Inverted hull empurra os vértices pela normal. Em aresta dura, cada face tem a própria normal, então os vértices se separam e abrem buracos no contorno. A correção é usar normais suavizadas só para o contorno, gravadas num canal extra da malha, ou suavizar o modelo.

Renderer Feature ainda funciona no Unity 6.3?

Sim. Render Objects e Full Screen Pass seguem disponíveis no URP do Unity 6.3. O que mudou foi a API para escrever Renderer Features próprios: passes customizados precisam implementar o Render Graph, porque o modo de compatibilidade fica escondido por padrão.