il modo migliore per calcolare le normali dei vertici dall'elenco di un triangolo


8

ciao sono un principiante completo in informatica computazionale, quindi scusate se è una risposta stupida. sto cercando di creare un semplice motore 3d da zero, più per scopi educativi che per un uso reale.

per ora computo solo le facce normali. in questo modo:

ho un oggetto Surface con all'interno di un elenco di triangoli. calcolo le normali all'interno della classe Triangle, in questo modo:

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
}

e quando si costruisce la superficie:

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

e penso che questo sia il modo migliore per farlo .. non è vero?

ora .. funziona, ok. ma la luce non è levigata. sto cercando di calcolare anche i normali dei vertici. (sto testando il mio motore con superfici tubolari, quindi ho quasi tutti i vertici condivisi con più di un triangolo)

ho trovato questo semplice algoritmo: vertice flipcode normale ma .. ehi questo algoritmo ha ... complessità esponenziale? (se la mia memoria non fallisce il mio background di informatica ..) (comunque .. ha 3 loop nidificati .. non credo sia il modo migliore per farlo ..)

qualche suggerimento?


Che lingua è questa? Mi sembra che tsia il risultato di computeFaceNormals(che non restituisce nulla), non un triangolo.

è pseudocodice:) hai ragione nel vero codice, ho anche "restituito questo", scusa se l'ho modificato!
nkint,

Risposte:


6

"Best" è piuttosto soggettivo: implicherà la valutazione di ciò che è necessario dall'algoritmo rispetto a input, complessità di runtime e altre proprietà.

Detto questo, sia il tuo approccio che l'approccio FlipCode collegato sono ragionevoli in termini di produzione di risultati utilizzabili (potresti semplicemente copiare le tue "normali del viso" su ciascun vertice, se non condividi istanze di vertici reali tra triangoli, cosa che non sono chiara dal tuo codice). Altre tecniche includono la ponderazione del contributo di ciascuna faccia normale per la dimensione dell'angolo realizzato con ciascuna faccia condivisa dal vertice.

Hai ragione a dire che l'approccio FlipCode appare non ottimale come scritto, ma potrebbe essere poco specificato: non è chiaro se intende suggerire o meno il secondo ciclo di attraversare tutte le facce nel modello, rispetto alla manciata di facce che condividono il vertice in questione. Se disponi delle informazioni di adiacenza per ridurre lo spazio di ricerca del secondo ciclo, diventa meno preoccupante. Ovviamente potresti non avere queste informazioni di adiacenza - questo è una specie di cosa intendo considerando quali input hai a disposizione per il tuo algoritmo.

Se non vuoi solo copiare la faccia normale nei vertici o stai condividendo i vertici e non vuoi dividerli, potresti:

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

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

Questo presuppone che ogni triangolo faccia effettivamente riferimento a ciascun vertice anziché memorizzarne una copia - non so quale lingua stai usando, quindi non so se sia così o no.


scusate l'inglese non è la mia prima lingua, quindi forse ho spiegato male quello che voglio .. non ho ancora normali vertici! ho modificato un po 'la mia risposta, spero che ora sia più chiaro
nkint

Va bene, ho capito che non avevi le normali dei vertici. Lo pseudocodice che ho fornito li calcolerà per te impostando prima ogni vertice su normale (0,0,0) e quindi riassumendo le normali faccia per ogni faccia che il vertice tocca (e, naturalmente, ri-normalizzando).

5
Non dovresti normalizzare le normali in questo modo. Dovresti normalizzarti dopo tutte le somme in un'altra foreach. Se ad esempio ci sono 10 triangoli con lo stesso normale, ma l'ultimo è con diverso (il risultato normale dovrebbe essere quasi uguale alla normale da 10 triangoli), allora avrai questo, quando sommi l'ultimo: imposta il vertice nromal = normalizza (( 1,0,0) + (0,0,1)) e cioè (0,5,0,0,5), che è errato. Spero di non averti confuso.
Zacharmarz,

7

Josh Petrie ha ragione. Se si desidera calcolare le normali dei vertici, è necessario ponderare il contributo del triangolo in base all'angolo. Ma puoi usare una soluzione ingenua e semplicemente sommare tutte le normali da tutte le facce attorno al vertice.

Quindi basta calcolare tutte le normali del viso e normalizzarle. Quindi impostare tutte le normali dei vertici su zero. Quindi per ogni faccia (triangolo) aggiungi la sua normale a tutti i suoi vertici. Quindi normalizza tutte le normali dei vertici. Ed è fatto.

Non è troppo corretto ma potrebbe essere sufficiente.

E il tuo normale calcolo del viso sopra - è corretto ma dovresti sapere da che parte stanno le teste normali. Potrebbe andare su o giù. Dipende dal prodotto incrociato - AxB non è lo stesso di BxA - ti dà il vettore opposto.


Un buon punto per quanto riguarda il prodotto incrociato.

2

Supponiamo di avere un cubo composto da 6 rettangoli. Sappiamo già che il vertice normale per un cubo non è la somma normalizzata delle facce di connessione a causa del bordo tagliente. Se costruisci una mappa del valore del vertice ed è diversa per i vertici di un cubo, finisci con 3 normali per ogni vertice.

Tenendo presente il punto precedente, è così che calcolo i normali dei vertici (l'ho testato e lo sto usando).

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

Ogni faccia tiene traccia delle normali dei vertici da usare poiché due facce possono condividere un vertice non significa che la normale del vertice sarà la stessa per ogni faccia.

Supponiamo che stiamo provando a calcolare il vertice normale per vert durante il rendering di face2 .

vert è condiviso da face0 , face1 , face2 , face3 e face4 . Tutte le facce sopra sono vicine in ordine e face0 e face4 si collegano per formare un loop.

Il vertice normale per vert è la somma di tutta la catena vicina connessa a face2 normalizzata. La catena si interrompe se l'angolo tra due facce vicine è superiore a 0,8 radianti (angolo == arccos (crossProduct (faceA.surface_normal, faceB.surface_normal))).

se l'angolo tra face0 e face1 è maggiore di 0,8 radianti e l'angolo tra face3 e face4 è superiore a 0,8 radianti rispetto al vertice normale per vert quando il rendering di face2 è normalizzato ( surfnormal1 + surfnormal2 + surfnormal3 ).

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.