Comment calculer les normales de surface pour la géométrie générée

12

J'ai une classe qui génère une forme 3D basée sur les entrées du code appelant. Les entrées sont des choses comme la longueur, la profondeur, l'arc, etc. Mon code génère parfaitement la géométrie, mais je rencontre des problèmes lors du calcul des normales de surface. Lorsqu'elle est allumée, ma forme a une coloration / texture très bizarre provenant des normales de surface incorrectes qui sont calculées. De toutes mes recherches, je crois que mes calculs sont corrects, il semble que quelque chose ne va pas avec ma technique ou ma méthode.

À un niveau élevé, comment procéder pour calculer par programme les normales de surface d'une forme générée? J'utilise Swift / SceneKit sur iOS pour mon code mais une réponse générique est très bien.

J'ai deux tableaux qui représentent ma forme. L'un est un tableau de points 3D qui représente les sommets qui composent la forme. L'autre tableau est une liste d'index du premier tableau qui mappent les sommets en triangles. J'ai besoin de prendre ces données et de générer un troisième tableau qui est un ensemble de normales de surface qui aident à l'éclairage de la forme. (voir SCNGeometrySourceSemanticNormaldans SceneKit` )

La liste des sommets et des index est toujours différente en fonction des entrées de la classe, donc je ne peux pas pré-calculer ou coder en dur les normales de surface.

macinjosh
la source
Besoin de plus de contexte. Essayez-vous de calculer des normales analytiques pour une surface paramétrique? Une surface implicite? Ou voulez-vous calculer les normales à partir d'un maillage triangulaire générique? Ou autre chose?
Nathan Reed du
Merci, j'ai ajouté plus de détails. Pour répondre à votre question, je dois calculer les normales à partir d'un maillage triangulaire générique. Mais pour être clair, le maillage est différent selon les entrées. Ma forme est une flèche 3D, comme exemple, voici une capture d'écran de 2 formes différentes (c'est-à-dire radiale et linéaire). La classe modifie la largeur, la profondeur, la longueur, l'arc et le rayon du maillage comme demandé. cl.ly/image/3O0P3X3N3d1d Vous pouvez voir l'éclairage étrange que j'obtiens avec mes mauvaises tentatives pour résoudre ce problème.
macinjosh
3
La version courte est: calculer chaque sommet normal comme la somme normalisée des normales de tous les triangles qui le touchent. Cependant, cela rendra tout lisse, ce qui peut ne pas être ce que vous voulez pour cette forme. Je vais essayer de développer une réponse complète plus tard.
Nathan Reed du
Lisse est ce que je veux!
macinjosh
4
Dans la plupart des cas, si vous calculez analytiquement les positions des sommets, vous pouvez également calculer les normales analytiquement. Pour une surface paramétrique, les normales sont le produit croisé des deux vecteurs de gradient. Le calcul de la moyenne des normales triangulaires n'est qu'une approximation et se traduit souvent par une qualité visuellement beaucoup moins bonne. Je publierais une réponse, mais j'ai déjà publié un exemple détaillé sur SO ( stackoverflow.com/questions/27233820/… ), et je ne suis pas sûr si nous voulons du contenu répliqué ici.
Reto Koradi

Réponses:

10

Vous ne voulez tout simplement pas de résultats parfaitement fluides. Alors que la méthode commentée de Nathan Reed: "Calculer chaque sommet pour faire face à la normale, les additionner, normaliser la somme", fonctionne généralement, il échoue parfois de façon spectaculaire. Mais cela n'a pas d'importance ici, nous pouvons utiliser cette méthode en y ajoutant une clause de rejet.

Dans ce cas, vous souhaitez simplement que certaines pièces ne soient pas lissées contre certaines autres pièces. Vous voulez des bords durs sélectifs. Ainsi, par exemple, le haut et le bas plats sont séparés de la bande triangulaire sur le côté, tout comme chaque zone plate.

Image que nous recherchons

Image 1 : Le résultat souhaité.

En effet, vous voulez uniquement faire la moyenne des sommets de la zone courbe, tous les autres peuvent utiliser la normale qu'ils obtiennent à partir de leur triangle seul. Il vaut donc mieux considérer le maillage comme 9 régions distinctes qui sont gérées sans les autres.

Affichage du maillage et des normales]

Image 2 : Image montrant la structure du maillage et les normales.

Vous pouvez certainement déduire cela automatiquement en n'incluant pas les normales qui sont en dehors d'un certain angle par rapport à la normale des sommets primaires. Pseudocode:

For vertex in faceVertex:
    normal = vertex.normal
    For adjVertex in adjacentVertices:
        if anglebetween(vertex.normal, adjVertex.normal )  < treshold:
            normal += adjVertex.normal
    normal = normalize(normal)

Cela fonctionne, mais vous pouvez simplement éviter tout cela au moment de la création, car vous comprenez que les plans séparés fonctionnent différemment. Ainsi, seuls les côtés incurvés doivent fusionner dans le sens normal. Et en fait, vous pouvez simplement les calculer directement à partir de la forme mathématique sous-jacente.

joojaa
la source
10

Je vois principalement trois façons de calculer des normales pour une forme générée.

Normales analytiques

Dans certains cas, vous disposez de suffisamment d'informations sur la surface pour générer les normales. Par exemple, la normale de tout point sur une sphère est triviale à calculer. En termes simples, lorsque vous connaissez la dérivée de la fonction, vous connaissez également la normale.

Si votre cas est suffisamment étroit pour vous permettre d'utiliser des normales analytiques, elles donneront probablement le meilleur résultat en termes de précision. Cependant, la technique n'est pas trop évolutive: si vous devez également gérer des cas où vous ne pouvez pas utiliser de normales analytiques, il peut être plus facile de conserver la technique qui gère le cas général et de supprimer complètement le cas analytique.

Normales de sommet

Le produit croisé de deux vecteurs donne un vecteur perpendiculaire au plan auquel ils appartiennent. Il est donc simple d'obtenir la normale d'un triangle:

vec3 computeNormal(vec3 a, vec3 b, vec3 c)
{
    return normalize(crossProduct(b - a, c - a));
}

De plus, dans l'exemple ci-dessus, la longueur du produit croisé est proportionnelle à la zone à l'intérieur de abc . Ainsi, la normale lissée à un sommet partagé par plusieurs triangles peut être calculée en additionnant les produits croisés et en normalisant comme dernière étape, pondérant ainsi chaque triangle par sa surface.

vec3 computeNormal(vertex a)
{
    vec3 sum = vec3(0, 0, 0);
    list<vertex> adjacentVertices = getAdjacentVertices(a);
    for (int i = 1; i < adjacentVertices; ++i)
    {
        vec3 b = adjacentVertices[i - 1];
        vec3 c = adjacentVertices[i];
        sum += crossProduct(b - a, c - a);
    }
    if (norm(sum) == 0)
    {
        // Degenerate case
        return sum;
    }
    return normalize(sum);
}

Si vous travaillez avec des quads, il y a une bonne astuce que vous pouvez utiliser: pour un quad abcd , utilisez crossProduct(c - a, d - b)et il gérera bien les cas où le quad est en fait un triangle.

Iñigo quilez a écrit quelques courts articles sur le sujet: normalisation intelligente d'un maillage , et normale et aire de polygones à n côtés .

Normales dérivées partielles

Les normales peuvent être calculées dans le fragment shader à partir des dérivées partielles. Le calcul derrière est le même, sauf que cette fois, cela se fait dans l'espace écran. Cet article d'Angelo Pesce décrit la technique: des normales sans normales .

Julien Guertault
la source
1
Il y a une quatrième façon, les normales fournies par l'artiste;)
joojaa
@joojaa: Je suppose que vous faites référence à des cartes normales? Je n'ai jamais entendu parler de normales créées manuellement autrement.
Julien Guertault
1
Non, normales créées manuellement. Il arrive parfois que votre artiste en sache plus sur le comportement des normales que les modèles programmeurs. C'est parfois un peu problématique pour les moteurs de calcul s'ils supposent que les normales proviennent de calculs sous-jacents. Mais cela arrive certainement et vous gagnez beaucoup de temps dans la modélisation mathématique.
joojaa
1
Celles-ci sont parfois appelées «normales explicites» (terminologie 3ds max et maya).
Dusan Bosnjak 'pailhead'