Toon / cel sombreamento com largura de linha variável?

15

Eu vejo algumas abordagens amplas por aí para fazer cel shading:

  1. Duplicação e ampliação do modelo com normais invertidas (não é uma opção para mim)
  2. Abordagens de filtro / sombreador Sobel para detecção de borda
  3. Abordagens de buffer de estêncil para detecção de bordas
  4. 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 ...

insira a descrição da imagem aqui

O que eu não quero ...

insira a descrição da imagem aqui


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?

Engenheiro
fonte
A espessura variável da linha é uma característica da abordagem geométrica. Não é possível obtê-lo de outra maneira com eficiência. Como você deseja isso, não vejo os sombreadores de pixel como o "método mais eficiente": número de pixels a serem pesquisados ​​= (line_thickness * 2 + 1) ^ 2 - 1. Isso basicamente significa que seu sombreador pós-processo será muito (estimativa de estimativa: 10x) mais lenta se max. a largura da linha é igual a 2. Basta soltar a pré-avaliação e tentar a abordagem do sombreador de geometria.
usar o seguinte código
@ snake5 Você está sinceramente sugerindo que eu percorra 150 milhões de vértices por atualização de renderização (cerca de 25 milhões com seleção de exibição), no GS? Eu realmente não estou comprando isso, mas obrigado pela contribuição. Consulte o que afirmei acima sobre contagens e complexidade de vértices. Mesmo com sua figura de 10x, o shader de fragmento seria pelo menos empatado e provavelmente se sairia muito melhor.
Engenheiro
25 milhões de vértices (leituras / gravações lineares) vs. 23 milhões de amostras de textura (leituras de memória em cache insuficiente + descompressão dos dados de destino de renderização) @ 1280x720. A propósito, limitar / reduzir a contagem de vértices é muito mais fácil. Especialmente hoje em dia, quando as configurações de monitores grandes (1920x1080 +) e multimonitor estão ficando muito populares.
usar o seguinte comando
que tal usar o mesmo algoritmo que gera contornos vermelhos e, em seguida, afinar / aumentar linhas com base em sua profundidade?
Ali1S232
@ Gajoo, como eu estava dizendo na pergunta, não se trata apenas da silhueta. Observe as linhas intrusivas atrás do ombro da garota na primeira imagem. Essa é uma função da abordagem dos normais invertidos e atua como contornos / vincos sugestivos. Eu preciso do mesmo.
Engineer

Respostas:

4

Decidi seguir uma abordagem de shader de fragmento por meio da filtragem de descontinuidade do buffer de profundidade. Razões para isso são:

  1. A contagem de vértices mundiais é muito, muito alta devido às imensas distâncias de visualização, mesmo com malha LoD;
  2. Estou realizando várias outras operações de shader de fragmento, como o desfoque de DoF, que podem se beneficiar das mesmas estruturas (caixa ou amostragem / filtragem gaussiana) na mesma passagem.

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.

Engenheiro
fonte
Eu acho que ir com a descontinuidade dos normais daria a você melhores resultados caso você tivesse normais normais. Você pode variar a largura da linha separando as amostras.
Grieverheart 07/12/12
@Grieverheart Esta é a resposta aceita, porque o método que estou usando funciona muito bem. Obrigado.
Engineer
0

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.

SnoepGames
fonte