OpenGL - Détection des bords

12

Je voudrais charger des maillages arbitraires et dessiner des lignes noires épaisses le long des bords pour obtenir un aspect semblable à un ombrage. J'ai réussi à dessiner une silhouette noire autour des objets en utilisant le tampon de pochoir. Vous pouvez voir le résultat ici:

entrez la description de l'image ici

Mais ce qui manque, ce sont les lignes noires dans l'objet lui-même. J'ai pensé à vérifier les discontinuités normales: vérifier si un pixel voisin a un vecteur normal différent de celui actuel. Si oui, un bord a été trouvé. Malheureusement, je ne sais pas comment je pourrais implémenter cette approche, ni dans OpenGL ni dans GLSL vertex / fragment shader.

Je serais très heureux d'avoir de l'aide concernant cette approche ou toute autre concernant la détection des bords.

Edit: je n'utilise pas de textures pour mes meshs.

Pour être plus précis, je voudrais créer une solution CAD / CAM qui ressemble autant que possible à celle-ci (tirée de Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4 ):

entrez la description de l'image ici

enne87
la source
Je crois que vous devez définir le «bord» plus en détail. En quoi diffère-t-il d'un simple filaire? Dans la réponse cifz, il y a une bonne description du post-traitement de l'espace d'écran, mais à partir de votre question, il est difficile de déterminer s'il est applicable.
Andreas
Eh bien, avec bord, je veux dire les "plis" et les "crêtes" qui forment les solides. Un filaire afficherait toutes les faces du triangle, ce qui n'est pas ce que je veux.
enne87
Ok, exactement ce que j'ai demandé :-) Je générerais un wireframe à partir de ces plis et crêtes. La partie délicate est toujours de déterminer ce qu'est un pli / une crête. Avez-vous une idée de comment faire cela?
Andreas
1
Les programmes de CAO ne le font pas principalement avec un shader. Au lieu de cela, ils connaissent les bords durs du modèle et tracent une ligne au-dessus du maillage pour former cette information.
joojaa
joojaa avez-vous plus d'informations sur cette technique? Que faire en cas de surfaces de forme libre à double courbure? Je ne sais pas non plus ce qui se passe lorsque vous avez un cône ou un cylindre qui est coupé / coupé par quelque chose de forme libre. stackoverflow.com/questions/43795262/…
Dusan Bosnjak 'pailhead'

Réponses:

18

Généralement, la détection des contours se résume à détecter les zones de l'image avec une valeur de gradient élevée.

Dans notre cas, nous pouvons voir grossièrement le gradient comme la dérivée de la fonction d'image, donc l'ampleur du gradient vous donne une information sur la façon dont votre image change localement (en ce qui concerne les pixels / texels voisins).
Maintenant, un bord est comme vous le dites une indication de discontinuité, alors maintenant que nous avons défini le gradient, il est clair que cette information est tout ce dont nous avons besoin. Une fois que nous trouvons le gradient d'une image, il suffit de lui appliquer un seuil pour obtenir une valeur binaire bord / non bord.

Comment trouvez-vous que ce gradient est vraiment ce que vous demandez et je n'ai pas encore répondu :)

Beaucoup de façons! Voici un couple :)

Fonctions de shader intégrées

Hlsl et glsl offrent des fonctions dérivées. Dans GLSL, vous avez dFdx et dFdy qui vous donnent respectivement des informations de gradient dans les directions x et y. En règle générale, ces fonctions sont évaluées dans un bloc de fragments 2x2.
Sauf si vous êtes intéressé par une seule direction, un bon moyen d'avoir un résultat compact qui indique la force du gradient dans la région est une largeur qui ne vous donne rien d'autre que la somme de la valeur absolue de dFdy et dFdy.
Vous êtes susceptible d'être intéressé par un bord sur l'image globale plutôt que sur un canal spécifique, vous pouvez donc vouloir transformer votre fonction d'image en luma. Dans cet esprit, en ce qui concerne la détection des contours, votre shader peut inclure quelque chose du genre:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

Avec un seuil élevé, vous trouverez des bords plus grossiers et vous pourriez en manquer, à l'inverse, avec un seuil bas, vous pourriez détecter de faux bords. Vous devez expérimenter pour trouver le seuil qui correspond le mieux à vos besoins.

La raison pour laquelle ces fonctions fonctionnent mérite d'être mentionnée mais je n'ai pas le temps pour cela maintenant, je suis susceptible de mettre à jour cette réponse plus tard :)

Post-traitement de l'espace d'écran

Vous pourriez devenir plus sophistiqué que cela, maintenant le domaine de la détection des bords dans le traitement d'image est immense. Je pourrais vous citer des dizaines de bonnes façons de détecter la détection des contours en fonction de vos besoins, mais restons simples pour l'instant, si vous êtes intéressé, je peux vous citer plus d'options!

L'idée serait donc similaire à celle ci-dessus, à la différence près que vous pourriez regarder un voisinage plus large et utiliser un ensemble de poids sur les échantillons environnants si vous le souhaitez. En règle générale, vous exécutez une convolution sur votre image avec un noyau qui vous donne en conséquence une bonne information de dégradé.
Un choix très courant est le noyau Sobel

                                   entrez la description de l'image ici

Ce qui vous donne respectivement des gradients dans les directions x et y:

                                  À partir d'un vieux pdf que j'ai écrit il y a longtemps.

Vous pouvez obtenir la valeur unique du dégradé sous la formeGradientMagnitude=(Gradientx)2+(Gradienty)2

Ensuite, vous pouvez définir le seuil de la même manière que je l'ai mentionné ci-dessus.

Ce noyau, comme vous pouvez le voir, donne plus de poids au pixel central, il calcule donc efficacement le dégradé + un peu de lissage, ce qui aide traditionnellement (souvent l'image est floue gaussienne pour éliminer les petits bords).

Ce qui précède fonctionne assez bien, mais si vous n'aimez pas le lissage, vous pouvez utiliser les noyaux Prewitt:

                                                   entrez la description de l'image ici

(Notez que je suis pressé, j'écrirai bientôt le texte formaté approprié au lieu des images!)

Vraiment, il y a beaucoup plus de noyaux et de techniques pour trouver la détection des bords d'une manière processus d'image y plutôt que des graphiques en temps réel, j'ai donc exclu des méthodes plus compliquées (jeu de mots non prévu) car probablement vous seriez très bien avec les fonctions dFdx / y .

cifz
la source
Très belle explication cifz, mais que se passe-t-il si aucun gradient n'est visible dans une certaine situation? Par exemple, il n'y a pas de source lumineuse à l'arrière du cube et donc aucun dégradé n'est visible. Ensuite, ce processus de détection de bord basé sur l'image que vous décrivez ne fonctionnerait pas, ai-je raison?
enne87
Si vous utilisez un rendu différé, vous pouvez faire de même, mais sur le tampon normal ou encore, si vous avez un pré-passe, vous pouvez appliquer l'algorithme à la profondeur. Pourtant, vous avez raison, une approche de l'espace d'écran peut ne pas être idéale dans tous les cas :) La solution viable dépend beaucoup de votre budget pour cet effet, de la complexité de votre scène.
cifz
Potentiellement, si cela est vraiment central pour votre jeu, une méthode très simple, mais potentiellement pénible, serait de calculer un gradient sur vos normales voisines pour chaque sommet (hors ligne au moment du chargement) et de passer ce facteur comme attribut de sommet supplémentaire.
cifz
Merci encore cifz pour votre aide. Eh bien, ce que je veux réaliser, c'est développer une solution CAD / CAM qui ressemble à ceci: youtube.com/watch?v=-qTJZtYUDB4 Et j'aimerais vraiment savoir comment ils ont réussi à rendre tous les bords noirs, les plis et les crêtes. cifz, pensez-vous en tant que spécialiste de la programmation graphique qu'ils ont atteint ce look en utilisant l'une de vos approches d'espace d'écran? Ou peut-être en calculant un gradient sur les normales voisines? Je veux vraiment savoir comment cela peut être fait. Merci encore!
enne87
1
Ne payez personne, commencez à lire quelques tutoriels en ligne, achetez des livres et étudiez dur :) Ce sera très gratifiant à la fin!
cifz
3

Juste au cas où quiconque elso aurait également besoin de détecter les bords: voici un bel article sur la façon d'afficher un filaire et cet article explique comment afficher uniquement les bords.

enne87
la source