Ombrage Toon / Cel avec largeur de ligne variable?

15

Je vois quelques grandes approches pour faire de l'ombrage cel:

  1. Duplication et agrandissement du modèle avec des normales inversées (pas une option pour moi)
  2. Approches de filtrage / fragment de Sobel pour la détection des contours
  3. Approches du tampon de gabarit pour la détection des contours
  4. Approches de shaders de géométrie (ou sommet) qui calculent les normales de face et d'arête

Ai-je raison de supposer que l'approche centrée sur la géométrie donne le plus grand contrôle sur l'éclairage et l'épaisseur des lignes, par exemple. pour un terrain où vous pourriez voir la ligne de silhouette d'une colline se fondre progressivement dans une plaine?

Et si je n'avais pas besoin d'un éclairage pixel sur mes surfaces de terrain? (Et je ne le ferai probablement pas, car je prévois d'utiliser un éclairage / ombrage basé sur les sommets ou les texturemap.) Serais-je alors préférable de m'en tenir à l'approche de type géométrique, ou opter plutôt pour une approche espace / fragment d'écran? pour garder les choses plus simples? Si oui, comment pourrais-je obtenir "l'encrage" des collines dans la silhouette de la maille, plutôt que seulement le contour de la maille entière (sans détails "d'encre" à l'intérieur de ce contour? (Contours suggestifs AKA , plis ).

Enfin, est-il possible d'émuler à moindre coût l'approche des normales inversées, en utilisant un shader de géométrie? Ce qui m'inquiète, c'est que je pourrais certainement dupliquer chaque sommet et les mettre à l'échelle en conséquence, mais comment pourrais-je aborder les normales de retournement et la coloration distincte dans le fragment shader?

Ce que je veux - une épaisseur de ligne variable avec des lignes intrusives à l'intérieur de la silhouette ...

entrez la description de l'image ici

Ce que je ne veux pas ...

entrez la description de l'image ici


EDIT: De nouvelles recherches ont révélé ce qui suit ...

Étant donné que le nombre de sommets sur le terrain est énorme, même en tenant compte de la LoD basée sur la distance, ni les normales inversées ni une approche basée sur un nuanceur de géométrie (même avec un tri sélectif) ne seraient une option raisonnable en raison de la complexité de calcul impliquée dans la duplication et la mise à l'échelle de tous sommets téléchargés.

Étant donné que je n'ai pas besoin d'un éclairage par pixel sous la forme d'un ombrage uni sur les surfaces du terrain, il devient également moins prudent de considérer toutes les approches basées sur la face normale - sinon une exigence pour un éclairage de surface correct - car celles-ci sont naturellement assez chers à calculer. Il est cependant vrai qu'ils donnent le meilleur degré de contrôle; par exemple, la possibilité d'ombrer les bords en utilisant des traits "artistiques": Magnifique, mais encore une fois, pas vraiment viable pour un environnement de jeu massivement complexe.

Je préfère éviter les tampons au pochoir car je préfère faire tout le travail dans les shaders. (L'exemple ci-dessus avec le contour rouge a été fait avec un tampon de pochoir - old school.)

Cela laisse des fragments d'espace image shader. La complexité de calcul est réduite au nombre de fragments plutôt qu'au nombre de sommets (dans mon cas, c'est 10 à 100 fois moins d'opérations que je n'aurais à faire dans le shader de géométrie). Cela nécessite plus d'un passage de rendu afin de générer un g-buffer (composé d'un tampon normal et éventuellement d'un tampon de profondeur également) auquel nous pouvons appliquer des filtres de discontinuité (par exemple, opérateur Sobel). La discontinuité de la profondeur est ce qui permet des contours et des plis évocateurs. Mon seul problème avec cette approche est l'incapacité à fournir un contrôle plus fin sur les largeurs de bords encrés, bien qu'avec le bon algorithme dans le fragment shader, je suis sûr que ce serait possible.

La question devient donc plus précise: comment pourrais-je exactement obtenir des largeurs de bord variables, en particulier sur la silhouette extérieure, dans un fragment shader?

Ingénieur
la source
L'épaisseur variable des lignes est une caractéristique de l'approche géométrique. Je ne peux pas l'obtenir efficacement autrement. Puisque vous le souhaitez, je ne considère pas les shaders de pixels comme la "méthode la plus efficace": nombre de pixels à rechercher = (line_thickness * 2 + 1) ^ 2 - 1. Cela signifierait essentiellement que votre shader de post-processus sera beaucoup (estimation approximative: 10x) plus lent si max. la largeur de ligne est égale à 2. Il suffit de laisser tomber le pré-jugement et d'essayer l'approche du shader de géométrie.
snake5
@ snake5 Vous suggérez honnêtement que je passe par 150 millions de sommets par mise à jour de rendu (environ 25 millions avec abattage de vues), dans le GS? Je n'achète pas vraiment ça, mais merci pour la contribution. Reportez-vous à ce que j'ai déclaré ci-dessus sur le nombre de sommets et la complexité. Même avec votre figurine 10x, le fragment shader serait au moins égal, et ferait probablement beaucoup mieux.
Ingénieur
25 millions de sommets (lectures / écritures linéaires) contre 23 millions d'échantillons de texture (lectures en mémoire plutôt mal mises en cache + décompression des données cibles de rendu) @ 1280x720. Soit dit en passant, limiter / réduire le nombre de vertex est beaucoup plus facile. Surtout ces jours-ci, lorsque les grands écrans (1920x1080 +) et les configurations multimoniteurs deviennent très populaires.
snake5
qu'en est-il de l'utilisation du même algorithme que celui générant des contours rouges, puis des lignes d'éclaircissement / de croissance en fonction de leur profondeur?
Ali1S232
@Gajoo, comme je le disais dans la question, il ne s'agit pas seulement de la silhouette. Notez les lignes intrusives derrière l'épaule de la fille dans la première image. Ceux-ci sont fonction de l'approche des normales inversées et agissent comme des contours / plis suggestifs. J'en ai besoin.
Ingénieur

Réponses:

4

J'ai décidé d'aller avec une approche de fragment shader via un filtrage de discontinuité du tampon de profondeur. Les raisons en sont:

  1. Le nombre de sommets dans le monde est très, très élevé en raison des distances de vue immenses, même avec la maille LoD;
  2. Je fais un certain nombre d'autres opérations de shader de fragments, comme le flou DoF qui peuvent bénéficier des mêmes structures (échantillonnage / filtrage gaussien ou box) dans le même passage.

Après l'avoir testé, je dirais que dans les projets futurs, j'opterais plutôt pour une approche basée sur la géométrie, pour des raisons de complexité. La raison en est que (comme d'autres l'ont suggéré dans les commentaires), les approches de shaders de fragments pour la détection des bords peuvent être intensives sur le plan des calculs, en particulier avec les implémentations DoF où le cercle du rayon de confusion, et donc le nombre d'échantillons par fragment, peut être assez élevé. Ceci est heureusement moins préoccupant pour les shaders de contour.

Ingénieur
la source
Je pense que la discontinuité des normales vous donnerait de meilleurs résultats au cas où vous auriez des normales plates. Vous pouvez varier la largeur de ligne en prenant des échantillons plus éloignés.
Grieverheart
@Grieverheart C'est la réponse acceptée, car la méthode que j'utilise fonctionne très bien. Merci.
Ingénieur
0

Il existe en fait une solution générale très simple si vous pouvez utiliser un effet secondaire. La grande chose est, peu importe la hauteur de votre poly-compte. Rendez une carte de profondeur en niveaux de gris, puis créez un point dans la couleur de ligne souhaitée, lorsque le contraste entre deux pixels adjacents est supérieur à une valeur de seuil. Vous pouvez agrandir le point en fonction du contraste et / ou du niveau de lumière de l'image que vous avez rendue.

J'ai inventé l'algorithme toonShader en 2000/2001, avant même ce type français qui a créé cette solution. La mienne était basée sur la géométrie réelle des matériaux. Ensuite, il y a essentiellement deux façons de le faire: 1. regardez les normales, si les deux plans connectés à une ligne sont orientés vers l'extérieur et vers la caméra, restituez cette ligne, vous pouvez ensuite utiliser la profondeur, l'éclairage, etc. comme indices pour les profondeurs de ligne. 2. Regardez la propriété géographique rendue (donc après la transformation de perspective) Prenez chaque segment de ligne, si les sommets des plans de connexion sont du même côté de ce segment de ligne, vous restituez la ligne. Vous pouvez alors faire la même chose pour l'épaisseur de ligne comme dans méthode 1. 3. Vous pouvez faire des combinaisons de ces trois techniques, mais j'ai mentionné la première, parce que vous avez indiqué un énorme nombre de poly.

SnoepGames
la source