meilleure façon de calculer les normales de sommet à partir de la liste d'un triangle

8

salut je suis un débutant complet en informatique, donc désolé si c'est une réponse stupide. J'essaie de créer un moteur 3D simple à partir de zéro, plus à des fins éducatives que pour une utilisation réelle.

pour l'instant je ne calcule que les normales de face. de cette façon:

j'ai un objet Surface avec l'intérieur d'une liste de Triangle. je calcule les normales à l'intérieur de la classe Triangle, de cette façon:

triangle.computeFaceNormals() {
    Vec3D u = v1.sub(v3)
    Vec3D v = v1.sub(v2)
    Vec3D normal = Vec3D.cross(u,v)
    normal.normalized()
    this.n1 = this.n2 = this.n3 = normal
}

et lors de la construction de la surface:

t = new Triangle(v1,v2,v3)
t.computeFaceNormals()
surface.addTriangle(t)

et je pense que c'est la meilleure façon de le faire .. n'est-ce pas?

maintenant .. cela fonctionne, ok. mais léger ce n'est pas lissé. j'essaie de calculer également des normales de sommet. (Je teste mon moteur avec des surfaces tubolaires donc j'ai presque tous les sommets partagés avec plus d'un triangle)

j'ai trouvé cet algorithme simple: flipcode vertex normal mais .. hei cet algorithme a .. une complexité exponentielle? (si ma mémoire n'échoue pas mon expérience en informatique ..) (en passant .. il a 3 boucles imbriquées .. je ne pense pas que ce soit la meilleure façon de le faire ..)

toute suggestion?

nkint
la source
Quelle langue est-ce? Il me semble que tc'est le résultat de computeFaceNormals(qui ne renvoie rien), pas un triangle.
c'est du pseudocode:) vous avez raison en vrai code j'ai aussi "retourner ça", désolé j'ai édité!
nkint

Réponses:

6

"Meilleur" est assez subjectif - cela impliquera de peser ce dont vous avez besoin de l'algorithme par rapport à ses entrées, sa complexité d'exécution et d'autres propriétés.

Cela dit, à la fois votre approche et l'approche FlipCode liée sont raisonnables en termes de production de résultats utilisables (vous pouvez tout simplement copier vos `` normales de visage '' sur chaque sommet, si vous ne partagez pas les instances de vertex réelles entre les triangles, ce que je ne suis pas clair à partir de votre code). D'autres techniques incluent la pondération de la contribution de chaque face normale par la taille de l'angle fait avec chaque face partagée par le sommet.

Vous avez raison en ce que l'approche FlipCode semble sous-optimale telle qu'elle est écrite, mais elle pourrait simplement être mal spécifiée: il est un peu difficile de savoir si elle a l'intention de suggérer que la deuxième boucle traverse toutes les faces du modèle, par rapport à la poignée de faces qui partagent la sommet en question. Si vous disposez des informations d'adjacence pour réduire l'espace de recherche de la deuxième boucle, cela devient moins un problème. Bien sûr, vous ne disposez peut-être pas de ces informations d'adjacence - c'est en quelque sorte ce que je veux dire en considérant les entrées dont vous disposez pour votre algorithme.

Si vous ne souhaitez pas simplement copier la face normale dans les sommets, ou si vous partagez des sommets et ne souhaitez pas les diviser, vous pouvez:

foreach vertex:
   set vertex normal to (0,0,0)

foreach triangle:
   foreach vertex on that triangle:
      set vertex normal = normalize( vertex normal + face normal )

Cela suppose que chaque triangle fait référence à chaque sommet plutôt que d'en stocker une copie - je ne sais pas quelle langue vous utilisez, donc je ne sais pas si c'est le cas ou non.


la source
désolé l'anglais n'est pas ma première langue donc mayebe j'ai expliqué de mauvaise façon ce que je veux .. je n'ai pas encore de normales de vertex! j'ai un peu modifié ma réponse, j'espère maintenant que je suis plus clair
nkint
C'est bon, j'ai compris que vous n'aviez pas de normales de vertex. Le pseudocode que j'ai fourni les calculera pour vous en définissant d'abord la normale de chaque sommet à (0,0,0), puis en additionnant les normales de face pour chaque face que le sommet touche (et en normalisant, bien sûr).
5
Vous ne devriez pas normaliser les normales de cette façon. Vous devriez normaliser après toutes les sommations dans un autre foreach. S'il y a par exemple 10 triangles avec la même normale, mais que le dernier est différent (le résultat normal devrait être presque égal à la normale de 10 triangles), alors vous l'aurez, lors de la sommation du dernier: set vertex nromal = normalize (( 1,0,0) + (0,0,1)) et c'est-à-dire (0,5,0,0,5), ce qui est faux. J'espère que je ne vous ai pas confondu.
zacharmarz
7

Josh Petrie a raison. Si vous souhaitez calculer des normales de sommet, vous devez pondérer la contribution du triangle par l'angle. Mais vous pouvez utiliser une solution naïve et additionner simplement toutes les normales de toutes les faces autour du sommet.

Il vous suffit donc de calculer toutes les normales de face et de les normaliser. Réglez ensuite toutes les normales de sommet à zéro. Ensuite, pour chaque face (triangle), ajoutez sa normale à tous ses sommets. Normalisez ensuite toutes les normales de sommet. Et c'est fait.

Ce n'est pas trop correct mais ça pourrait être suffisant.

Et votre visage calcul normal ci-dessus - c'est correct mais vous devez savoir de quel côté les têtes normales. Il pourrait monter ou descendre. Cela dépend du produit croisé - AxB n'est pas identique à BxA - il vous donne un vecteur opposé.

zacharmarz
la source
Bon point concernant le produit croisé.
2

Supposons que nous avons un cube composé de 6 rectangles. Nous savons déjà que le sommet normal d'un cube n'est pas la somme normalisée des faces de connexion en raison de l'arête vive. Si vous créez une carte de valeur de sommet et que ses normales de sommet sont différentes pour un cube, vous vous retrouvez avec 3 normales pour chaque sommet.

En gardant à l'esprit le point ci-dessus, voici comment je calcule les normales des sommets (je l'ai testé et je l'utilise).

class Face{
    uint vert_indices[3];
    vec3 surface_normal;
    vec3 vertex_normal[3];
}

Chaque face garde une trace des normales de sommet à utiliser, car deux faces peuvent partager un sommet ne signifie pas que la normale de sommet sera la même pour chaque face.

Supposons que nous essayons de calculer le sommet normal pour vert lors du rendu de face2 .

vert est partagé par face0 , face1 , face2 , face3 et face4 . Toutes les faces ci-dessus sont voisines dans l'ordre et face0 et face4 se connectent pour former une boucle.

Le sommet normal pour vert est la somme de toute la chaîne voisine connectée à face2 normalisée. La chaîne s'arrête si l'angle entre deux faces voisines est supérieur à 0,8 radian (angle == arccos (crossProduct (faceA.surface_normal, faceB.surface_normal)))).

si l'angle entre face0 et face1 est supérieur à 0,8 radian et l'angle entre face3 et face4 est supérieur à 0,8 radian par rapport au sommet normal pour vert lorsque le rendu de face2 est normalisé ( surfnormal1 + surfnormal2 + surfnormal3 ).

Fredrique Samuels
la source