Come calcolare le normali alla superficie per la geometria generata


12

Ho una classe che genera una forma 3D basata sugli input dal codice chiamante. Gli input sono cose come lunghezza, profondità, arco, ecc. Il mio codice genera perfettamente la geometria, tuttavia sto riscontrando problemi nel calcolo delle normali alla superficie. Quando è acceso, la mia forma ha una colorazione / trama molto bizzarra dalle normali superfici errate che vengono calcolate. Da tutte le mie ricerche credo che la mia matematica sia corretta, sembra che qualcosa non vada per la mia tecnica o metodo.

Ad alto livello come si fa a calcolare programmaticamente le normali di superficie per una forma generata? Sto usando Swift / SceneKit su iOS per il mio codice ma una risposta generica va bene.

Ho due array che rappresentano la mia forma. Uno è una matrice di punti 3D che rappresenta i vertici che compongono la forma. L'altro array è un elenco di indici del primo array che mappano i vertici in triangoli. Ho bisogno di prendere quei dati e generare un terzo array che è un insieme di normali di superficie che aiutano l'illuminazione della forma. (vedi SCNGeometrySourceSemanticNormalin SceneKit` )

L'elenco di vertici e indici è sempre diverso a seconda degli input della classe, quindi non posso pre-calcolare o codificare le normali di superficie.


Hai bisogno di più contesto. Stai cercando di calcolare le normali analitiche per una superficie parametrica? Una superficie implicita? Oppure vuoi calcolare le normali da una mesh triangolare generica? O qualcos'altro?
Nathan Reed,

Grazie, ho aggiunto maggiori dettagli. Per rispondere alla tua domanda, devo calcolare le normali da una mesh triangolare generica. Sebbene sia chiaro che la mesh è diversa a seconda degli input. La mia forma è una freccia 3D, come esempio qui è uno screenshot di esso 2 diverse forme (cioè radiale e lineare). La classe modifica larghezza, profondità, lunghezza, arco e raggio della mesh come richiesto. cl.ly/image/3O0P3X3N3d1d Puoi vedere la strana illuminazione che sto ottenendo con i miei scarsi tentativi di risolverlo.
macinjosh,

3
La versione breve è: calcola ogni vertice normale come la somma normalizzata delle normali di tutti i triangoli che la toccano. Tuttavia, questo renderà tutto liscio, che potrebbe non essere quello che vuoi per questa forma. Proverò ad espandermi in una risposta completa più tardi.
Nathan Reed,

Smooth è quello che sto cercando!
macinjosh,

4
Nella maggior parte dei casi, se si calcolano analiticamente le posizioni dei vertici, è anche possibile calcolare analiticamente le normali. Per una superficie parametrica, le normali sono il prodotto incrociato dei due vettori di gradiente. Il calcolo della media delle normali triangolari è solo un'approssimazione e spesso si traduce in una qualità visivamente molto più scarsa. Vorrei pubblicare una risposta, ma ho già pubblicato un esempio dettagliato su SO ( stackoverflow.com/questions/27233820/… ) e non sono sicuro se desideriamo contenuti replicati qui.
Reto Koradi,

Risposte:


10

Semplicemente non vuoi risultati completamente fluidi. Mentre il metodo commentato da Nathan Reed: "Calcola ogni vertice per affrontare il normale, sommalo, normalizza la somma", generalmente funziona a volte fallisce in modo spettacolare. Ma qui non ha importanza, possiamo usare quel metodo aggiungendo una clausola di rifiuto.

In questo caso si desidera semplicemente che alcune parti non vengano levigate rispetto ad altre parti. Volete bordi duri selettivi. Ad esempio, la parte superiore e inferiore piatta sono separate dalla striscia triangolare sul lato, così come ogni area piatta.

Immagine che stiamo cercando

Immagine 1 : il risultato desiderato.

In effetti, vuoi solo fare una media dei vertici dell'area curva, tutti gli altri possono usare il normale che ottengono dal loro triangolo da soli. Quindi è meglio pensare alla mesh come a 9 regioni separate che vengono gestite senza le altre.

Mostra mesh e normali]

Immagine 2 : immagine che mostra la struttura della mesh e le normali.

Puoi certamente dedurlo automaticamente non includendo le normali al di fuori di un certo angolo dai vertici primari normali. pseudocodice:

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

Funziona, ma puoi semplicemente evitare tutto questo al momento della creazione perché capisci che i piani separati funzionano in modo diverso. Quindi solo i lati curvi hanno bisogno della fusione della direzione normale. E infatti puoi semplicemente calularli direttamente dalla forma matematica sottostante.


10

Vedo principalmente tre modi per calcolare le normali per una forma generata.

Normali analitici

In alcuni casi hai abbastanza informazioni sulla superficie per generare le normali. Ad esempio, la normale di qualsiasi punto su una sfera è banale da calcolare. In parole povere, quando conosci la derivata della funzione, conosci anche il normale.

Se il tuo caso è abbastanza stretto da permetterti di usare le normali analitiche, probabilmente daranno il miglior risultato in termini di precisione. La tecnica non si ridimensiona troppo bene: se è necessario gestire anche casi in cui non è possibile utilizzare le normali analitiche, potrebbe essere più semplice mantenere la tecnica che gestisce il caso generale e eliminare del tutto quella analitica.

Vertex normali

Il prodotto incrociato di due vettori fornisce un vettore perpendicolare al piano di appartenenza. Quindi ottenere il normale di un triangolo è semplice:

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

Inoltre, nell'esempio sopra, la lunghezza del prodotto trasversale è proporzionale all'area all'interno di abc . Quindi la normale levigata su un vertice condiviso da più triangoli può essere calcolata sommando i prodotti della croce e normalizzando come ultimo passo, ponderando così ciascun triangolo per la sua area.

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

Se stai lavorando con i quad, c'è un bel trucco che puoi usare: per un quad abcd , usa crossProduct(c - a, d - b)e gestirà bene i casi in cui il quad è in realtà un triangolo.

Iñigo quilez ha scritto alcuni brevi articoli sull'argomento: normalizzazione intelligente di una mesh e poligoni normali e di area .

Normali da derivati ​​parziali

Le normali possono essere calcolate nello shader di frammenti dai derivati ​​parziali. La matematica dietro è la stessa, tranne che questa volta è fatta nello spazio dello schermo. Questo articolo di Angelo Pesce descrive la tecnica: normali senza normali .


1
C'è un quarto modo, l'artista ha fornito normali;)
joojaa,

@joojaa: suppongo che ti riferisci alle mappe normali? Altrimenti non ho mai sentito parlare di normali create manualmente.
Julien Guertault,

1
No, normali creati manualmente. A volte capita che il tuo artista sappia di più su come dovrebbero comportarsi le normali rispetto ai modelli dei programmatori. A volte è un po 'problematico per i motori di calcolo se ritengono che le normali provengano da calcoli sottostanti. Ma certamente succede e risparmi molto tempo nella modellazione matematica.
joojaa,

1
Talvolta vengono definiti "normali esplicite" (terminologia 3ds max e maya).
Dusan Bosnjak 'secchio'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.