Je vois quelques grandes approches pour faire de l'ombrage cel:
- Duplication et agrandissement du modèle avec des normales inversées (pas une option pour moi)
- Approches de filtrage / fragment de Sobel pour la détection des contours
- Approches du tampon de gabarit pour la détection des contours
- 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 ...
Ce que je ne veux pas ...
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?
la source
Réponses:
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:
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.
la source
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.
la source