Eu vejo algumas abordagens amplas por aí para fazer cel shading:
- Duplicação e ampliação do modelo com normais invertidas (não é uma opção para mim)
- Abordagens de filtro / sombreador Sobel para detecção de borda
- Abordagens de buffer de estêncil para detecção de bordas
- Abordagens de sombreamento de geometria (ou vértice) que calculam normais de face e aresta
Estou correto ao supor que a abordagem centrada na geometria oferece a maior quantidade de controle sobre a iluminação e a espessura da linha, bem como por exemplo. para terrenos onde você pode ver a linha de silhueta de uma colina se fundindo gradualmente em uma planície?
E se eu não precisasse de iluminação com pixels nas superfícies do meu terreno? (E provavelmente não o planejarei usar iluminação / sombreamento com base em vértices ou em mapas de textura com base em células.) Seria melhor seguir a abordagem do tipo geometria ou optar por uma abordagem de espaço / fragmento de tela? para manter as coisas mais simples? Se sim, como obteria a "tinta" das colinas na silhueta da malha, em vez de apenas o contorno de toda a malha (sem detalhes de "tinta" dentro desse contorno? (AKA , contornos sugestivos , vincos )).
Por fim, é possível emular de maneira barata a abordagem de inversão de normais, usando um sombreador de geometria? Minha preocupação com isso é que eu certamente poderia duplicar todos os vértices e escaloná-los de acordo, mas como eu abordaria os normais invertidos e as cores distintas no shader de fragmentos?
O que eu quero - espessura da linha variável, com linhas intrusivas dentro da silhueta ...
O que eu não quero ...
EDIT: Mais pesquisas mostraram o seguinte ...
Como eu tenho uma grande contagem de vértices no terreno, mesmo considerando o LoD baseado em distância, nem os normais invertidos nem uma abordagem baseada em sombreamento de geometria (mesmo com a seleção de frustum) seriam uma opção sensata devido à pura complexidade computacional envolvida na duplicação e dimensionamento de todos vértices enviados.
Considerando que eu não preciso de iluminação por pixel na forma de sombreamento de tom sólido nas superfícies do terreno, também se torna menos prudente considerar qualquer abordagem baseada na face normal - caso contrário, um requisito para a iluminação correta da superfície - como essas são naturalmente muito caros de calcular. No entanto, é verdade que eles dão o melhor grau de controle; por exemplo, a capacidade de sombrear arestas usando traços "artísticos": bonito, mas, novamente, não é realmente viável para um ambiente de jogo extremamente complexo.
Buffers de estêncil são algo que eu preferiria evitar, pois eu preferiria fazer todo o trabalho em shaders. (O exemplo acima, com o contorno vermelho, foi feito com um buffer de estêncil - old school.)
Isso deixa abordagens de espaço de imagem de shader fragment. A complexidade computacional é reduzida ao número de fragmentos e não ao número de vértices (no meu caso, são 10 a 100 vezes menos operações do que eu teria que fazer no sombreador de geometria). Isso requer mais de uma passagem de renderização para gerar um buffer g (que consiste em um buffer normal e opcionalmente também em um buffer de profundidade) ao qual podemos aplicar filtros de descontinuidade (por exemplo, operador Sobel). A descontinuidade em profundidade é o que permite contornos e vincos sugestivos. Meu único problema com essa abordagem é a incapacidade de fornecer um controle mais refinado sobre larguras de bordas com tinta, embora com o algoritmo correto no shader de fragmento, tenho certeza de que isso seria possível.
Portanto, a pergunta agora se torna mais específica: como exatamente eu obteria larguras de arestas variáveis, particularmente na silhueta externa, em um shader de fragmento?
fonte
Respostas:
Decidi seguir uma abordagem de shader de fragmento por meio da filtragem de descontinuidade do buffer de profundidade. Razões para isso são:
Depois de testá-lo, eu diria que em projetos futuros, optaria por uma abordagem baseada em geometria, por razões de complexidade. A razão é que (como outros sugeriram nos comentários), as abordagens de shader de fragmentos para detecção de arestas podem ser intensamente computacionalmente, particularmente em implementações de DoF em que o raio do círculo de confusão e, portanto, o número de amostras por fragmento, pode ser bastante alto. Felizmente, isso é menos preocupante para os shaders de contorno.
fonte
Na verdade, existe uma solução geral muito fácil se você pode usar um efeito posterior. O melhor é que não importa o quão alto é o seu número de polis. Renderize um mapa de profundidade como escala de cinza e crie um ponto na cor da linha desejada, quando o contraste entre dois pixels adjacentes for maior que um valor limite. Você pode aumentar o ponto de acordo com o contraste e / ou o nível de luz da imagem renderizada.
Eu inventei o algoritmo toonShader em 2000/2001, mesmo antes do francês, que criou essa solução. A mina foi baseada na geometria real dos materiais. Existem basicamente duas maneiras de fazê-lo: 1. observe os normais, se dois planos conectados a uma linha estiverem voltados para a câmera e em direção à câmera, renderize essa linha, você poderá usar a profundidade, a iluminação etc. como pistas para os passos da linha. 2. Observe a geomerty renderizada (portanto, após a transformação da perspectiva) Pegue cada segmento de linha, se os vértices dos planos de conexão estiverem do mesmo lado desse segmento de linha, você renderiza a linha. Você pode fazer o mesmo com a espessura da linha como em método 1. 3. Você pode fazer combinações dessas três técnicas, mas mencionei a primeira, porque você indicou uma enorme contagem de polis.
fonte