Berechnen von Flächennormalen für generierte Geometrie

12

Ich habe eine Klasse, die eine 3D-Form basierend auf Eingaben aus dem aufrufenden Code generiert. Bei den Eingaben handelt es sich um Dinge wie Länge, Tiefe, Bogen usw. Mein Code generiert die Geometrie perfekt, es treten jedoch Probleme bei der Berechnung der Oberflächennormalen auf. Wenn beleuchtet, hat meine Form eine sehr bizarre Färbung / Textur aufgrund der falschen Oberflächennormalen, die berechnet werden. Nach all meinen Nachforschungen glaube ich, dass meine Mathematik korrekt ist. Es scheint, dass etwas mit meiner Technik oder Methode nicht stimmt.

Wie kann man auf hoher Ebene die Oberflächennormalen für eine generierte Form programmatisch berechnen? Ich verwende Swift / SceneKit unter iOS für meinen Code, aber eine generische Antwort ist in Ordnung.

Ich habe zwei Arrays, die meine Form darstellen. Eine ist eine Reihe von 3D-Punkten, die die Eckpunkte darstellen, aus denen die Form besteht. Das andere Array ist eine Liste der Indizes des ersten Arrays, die die Eckpunkte in Dreiecke abbilden. Ich muss diese Daten nehmen und ein drittes Array generieren, das eine Reihe von Oberflächennormalen ist, die die Beleuchtung der Form unterstützen. (siehe SCNGeometrySourceSemanticNormalin SceneKit` )

Die Liste der Eckpunkte und Indizes ist abhängig von den Eingaben in die Klasse immer unterschiedlich, sodass ich die Oberflächennormalen nicht vorberechnen oder hart codieren kann.

Macinjosh
quelle
Benötigen Sie mehr Kontext. Versuchen Sie, analytische Normalen für eine parametrische Oberfläche zu berechnen? Eine implizite Oberfläche? Oder möchten Sie die Normalen aus einem generischen Dreiecksnetz berechnen? Oder etwas anderes?
Nathan Reed
Danke, ich habe mehr Details hinzugefügt. Um Ihre Frage zu beantworten, muss ich Normalen aus einem generischen Dreiecksnetz berechnen. Es ist jedoch klar, dass das Netz je nach Eingabe unterschiedlich ist. Meine Form ist ein 3D-Pfeil, als Beispiel hier ein Screenshot von zwei verschiedenen Formen (dh radial und linear). Die Klasse ändert die Breite, Tiefe, Länge, den Bogen und den Radius des Netzes wie gewünscht. cl.ly/image/3O0P3X3N3d1d Sie können die seltsame Beleuchtung sehen, die ich mit meinen schlechten Versuchen bekomme , dies zu lösen.
Macinjosh
3
Die Kurzversion lautet: Berechnen Sie jede Vertexnormale als normalisierte Summe der Normalen aller Dreiecke, die sie berühren. Dadurch wird jedoch alles glatt, was für diese Form möglicherweise nicht der Wunsch ist. Ich werde später versuchen, eine vollständige Antwort zu finden.
Nathan Reed
Glatt mache ich!
Macinjosh
4
In den meisten Fällen können Sie die Normalen auch analytisch berechnen, wenn Sie die Scheitelpunktpositionen analytisch berechnen. Bei einer parametrischen Oberfläche sind die Normalen das Kreuzprodukt der beiden Gradientenvektoren. Die Berechnung des Durchschnitts von Dreiecksnormalen ist nur eine Annäherung und führt häufig zu einer viel schlechteren visuellen Qualität. Ich würde eine Antwort posten, aber ich habe bereits ein detailliertes Beispiel auf SO gepostet ( stackoverflow.com/questions/27233820/… ), und ich bin nicht sicher, ob wir hier replizierten Inhalt wünschen.
Reto Koradi

Antworten:

9

Sie wollen einfach keine glatten Ergebnisse. Während die kommentierte Methode von Nathan Reed: "Berechne jeden Scheitelpunkt so, dass er normal ist, summiere sie und normalisiere die Summe", funktioniert sie im Allgemeinen manchmal auf spektakuläre Weise. Aber das ist hier nicht wichtig, wir können diese Methode anwenden, indem wir eine Ablehnungsklausel hinzufügen.

In diesem Fall möchten Sie einfach, dass bestimmte Teile nicht gegen bestimmte andere Teile geglättet werden. Sie möchten selektive harte Kanten. So ist beispielsweise die flache Oberseite und die flache Unterseite von dem Dreiecksstreifen an der Seite getrennt, ebenso wie jede flache Fläche.

Image das wir suchen

Bild 1 : Das gewünschte Ergebnis.

Tatsächlich möchten Sie nur die Scheitelpunkte des gekrümmten Bereichs mitteln. Alle anderen können die Normalen verwenden, die sie nur aus ihrem Dreieck erhalten. Sie sollten sich das Netz also besser als 9 separate Regionen vorstellen, die ohne die anderen behandelt werden.

Mesh und Normalen anzeigen]

Bild 2 : Bild zeigt die Maschenstruktur und die Normalen.

Sie können dies sicherlich automatisch ableiten, indem Sie Normalen, die außerhalb eines bestimmten Winkels liegen, nicht von den Normalen der primären Scheitelpunkte einbeziehen. 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)

Das funktioniert, aber Sie können all dies beim Erstellen einfach vermeiden, da Sie verstehen, dass separate Ebenen unterschiedlich funktionieren. Es müssen also nur die gekrümmten Seiten in normaler Richtung zusammengeführt werden. Und tatsächlich können Sie sie direkt aus der zugrunde liegenden mathematischen Form berechnen.

joojaa
quelle
9

Ich sehe hauptsächlich drei Möglichkeiten, Normalen für eine generierte Form zu berechnen.

Analytische Normalen

In einigen Fällen verfügen Sie über genügend Informationen zur Oberfläche, um die Normalen zu generieren. Zum Beispiel ist die Normale eines Punktes auf einer Kugel trivial zu berechnen. Einfach ausgedrückt, wenn Sie die Ableitung der Funktion kennen, kennen Sie auch das Normale.

Wenn Ihr Fall so eng ist, dass Sie analytische Normalen verwenden können, erzielen Sie wahrscheinlich das beste Ergebnis in Bezug auf die Genauigkeit. Die Technik ist jedoch nicht sehr gut skalierbar: Wenn Sie auch Fälle behandeln müssen, in denen Sie keine analytischen Normalen verwenden können, ist es möglicherweise einfacher, die Technik, die den allgemeinen Fall behandelt, beizubehalten und den analytischen Fall insgesamt zu löschen.

Vertexnormalen

Das Kreuzprodukt zweier Vektoren ergibt einen Vektor senkrecht zu der Ebene, zu der sie gehören. Die Normalität eines Dreiecks zu erhalten ist also ganz einfach:

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

Darüber hinaus ist im obigen Beispiel die Länge des Querprodukts proportional zur Fläche innerhalb von abc . So kann die geglättete Normale an einem Scheitelpunkt, der von mehreren Dreiecken geteilt wird, berechnet werden, indem die Kreuzprodukte summiert und als letzter Schritt normalisiert werden, wodurch jedes Dreieck mit seiner Fläche gewichtet wird.

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);
}

Wenn Sie mit Quads arbeiten, gibt es einen schönen Trick können Sie verwenden: für ein Quad - abcd , Gebrauch crossProduct(c - a, d - b)und es wird behandeln schön Fälle , in denen das Quad in der Tat ist ein Dreieck.

Iñigo quilez schrieb ein paar kurze Artikel zum Thema: Kluge Normalisierung eines Netzes sowie Normal und Fläche von n-seitigen Polygonen .

Normalen aus partiellen Ableitungen

Aus den partiellen Ableitungen können im Fragment-Shader Normalen berechnet werden. Die Mathematik dahinter ist dieselbe, außer dass diesmal im Bildschirmbereich gearbeitet wird. Dieser Artikel von Angelo Pesce beschreibt die Technik: Normale ohne Normale .

Julien Guertault
quelle
1
Es gibt einen vierten Weg, Künstler lieferte Normalen;)
Joojaa
@joojaa: Ich nehme an, Sie beziehen sich auf normale Karten? Ich habe noch nie etwas anderes von manuell erstellten Normalen gehört.
Julien Guertault
1
Nein, manuell erstellte Normalen. Es kommt manchmal vor, dass Ihr Künstler mehr darüber weiß, wie sich die Normalen verhalten sollten als die Modelle des Programmierers. Für die Berechnungsmaschinen ist es manchmal etwas problematisch, wenn sie davon ausgehen, dass Normalen aus zugrunde liegenden Berechnungen stammen. Aber sicher passiert es und Sie sparen viel Zeit bei der mathematischen Modellierung.
Joojaa
1
Diese werden manchmal als "explizite Normalen" bezeichnet (3ds Max- und Maya-Terminologie).
Dusan Bosnjak 'Pailhead'