Come posso correggere gli artefatti di mappatura UV a zig-zag su una mesh generata che si assottiglia?


10

Sto creando una mesh procedurale basata su una curva e la dimensione della mesh si riduce su tutta la curva come vedi di seguito.

inserisci qui la descrizione dell'immagine

E il problema è che i raggi UV vengono zigzagati al variare delle dimensioni (funziona perfettamente quando le dimensioni della mesh sono le stesse per tutta la curva).

inserisci qui la descrizione dell'immagine

 Vertices.Add(R);
 Vertices.Add(L);
 UVs.Add(new Vector2(0f, (float)id / count));
 UVs.Add(new Vector2(1f, (float)id / count));
 start = Vertices.Count - 4;
          Triangles.Add(start + 0);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 3);

 mesh.vertices = Vertices.ToArray();
         //mesh.normals = normales;
         mesh.uv = UVs.ToArray();
         mesh.triangles = Triangles.ToArray();

Come posso risolvere questo problema?


Cosa sono ide count? Cosa fa UVs.ToArray()? Come stai caricando i vertici e le coordinate delle trame sulla carta?
user1118321

@utente1118321 dal contesto, idè l'indice del segmento corrente su tutte le barre trasversali lungo la striscia, di dove sono presenti countin totale. List<T>.ToArray()restituisce un array tipizzato di tutte le voci nell'elenco (quindi qui, a Vector2[]). Questi buffer di dati sono resi disponibili al sistema di rendering assegnandoli Meshall'istanza meshnelle ultime righe. Ma nulla di tutto questo è la parte particolarmente interessante del codice: come vengono scelti i punti di vertice. Quei dettagli sarebbero più utili da vedere. ;)
DMGregory

Questa era la mia ipotesi, ma ho chiesto chiarimenti perché a volte le ipotesi sono sbagliate. Ho visto qualcosa del genere nel mio codice in passato solo per rendermi conto che in countrealtà significava vertici anziché poligoni, o viceversa. Basta assicurarsi che tutto ciò che pensiamo stia succedendo davvero.
user1118321

Risposte:


13

La tipica mappatura UV è quella che viene definita una trasformazione affine . Ciò significa che la mappatura di ciascun triangolo tra lo spazio 3D e lo spazio texture può includere rotazione, traduzione, ridimensionamento / squash e inclinazione (ovvero qualsiasi cosa che possiamo fare con una moltiplicazione di matrice omogenea)

La cosa sulle trasformazioni affine è che sono uniformi in tutto il loro dominio - la rotazione, la traduzione, la scala e l'inclinazione che applichiamo alla trama vicino al vertice A sono le stesse di quelle che applichiamo vicino al vertice B, all'interno di un singolo triangolo. Le linee che sono parallele in uno spazio saranno mappate a linee parallele nell'altro, senza mai convergere / divergere.

Ma la graduale rastremazione che stai cercando di applicare non è uniforme: sta mappando linee parallele nella trama a linee convergenti sulla trama. Ciò significa che la scala della trama misurata attraverso la banda cambia continuamente mentre ci muoviamo lungo la striscia. Questo è più di quanto le trasformazioni affine della mappatura UV 2D possano rappresentare accuratamente: l'interpolazione delle coordinate uv 2D tra i vertici adiacenti otterrà una scala coerente lungo l'intero bordo, anche il bordo diagonale che dovrebbe ridursi in scala mentre si sposta lungo la striscia. Quella discrepanza è ciò che crea questo zigzag guerriero.

Questo problema si presenta ogni volta che vogliamo mappare un rettangolo su un trapezio - lati paralleli a lati convergenti: non esiste alcuna trasformazione affine che lo faccia, quindi dobbiamo approssimarlo a tratti, portando a cuciture visibili.

Per la maggior parte degli scopi è possibile ridurre al minimo l'effetto aggiungendo più geometria. Aumentare il numero di suddivisioni lungo la lunghezza della striscia e dividere la striscia in due o più segmenti lungo la sua larghezza, con le diagonali dei triangoli disposti a spina di pesce, può rendere l'effetto molto meno evidente. Sarà sempre presente fino a un certo punto, purché utilizziamo trasformazioni affini.

Ma c'è un modo per aggirarlo. Possiamo usare lo stesso trucco che usiamo per il rendering 3D per disegnare trapezoidi in prospettiva dato pareti e pavimenti rettangolari: usiamo coordinate proiettive !

Tessitura affine: Esempio di normale tessitura affine con artefatti a zig-zag

Texturing proiettivo: Esempio di textring proiettivo che corregge gli artefatti

Per fare ciò, dobbiamo aggiungere una terza coordinata uv (uvw) e modificare i nostri shader.

Dato un fattore di scala in ogni punto (diciamo, uguale all'ampiezza della tua striscia in quel punto), puoi costruire la coordinata uvw proiettiva 3D dalle tue normali coordinate uv 2D in questo modo:

Vector3 uv3 = ((Vector3)uv2) * scale;
uv3.z = scale;

Per applicare queste coordinate uvw 3D alla tua mesh, dovrai usare il Mesh.SetUVs di sovraccarico Vector3 (int channel, List uvs)

E assicurati di cambiare la struttura di input del tuo shader per aspettarti una coordinata di texture 3D (mostrata qui usando lo shader predefinito non illuminato):

struct appdata
{
    float4 vertex : POSITION;
    float3 uv : TEXCOORD0; // Change float2 to float 3.
};
// Also do this for the uv sent from the vertex shader to the fragment shader.

Dovrai anche tagliare la macro TRANSFORM_TEX nello shader di vertice, poiché si aspetta un uv 2D:

// o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv = v.uv;
// If you define a float4 with the special name _MainTex_ST, 
// you can get the same effect the macro had by doing this:
o.uv.xy = o.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

Infine, per tornare a una coordinata di trama 2D per il campionamento di trama, ti basta dividere per la terza coordinata nel tuo shader di frammento :

float2 uv = i.uv.xy / i.uv.z;

Da quando abbiamo creato questa coordinata uvw 3D dalla nostra coordinata 2D desiderata moltiplicandola per lo stesso numero, le due operazioni si annullano e torniamo alla coordinata 2D desiderata originale, ma ora con interpolazione non lineare tra i vertici. : D

È importante eseguire questa divisione per frammento e non nello shader di vertice. Se è fatto per vertice, allora torniamo ad interpolare linearmente le coordinate risultanti lungo ciascun bordo e abbiamo perso la non linearità che stavamo cercando di introdurre con le coordinate proiettive!


Ciao, sto lottando con lo stesso problema da un po 'di tempo ormai, e questo sembra esattamente quello che mi sta succedendo. L'unica differenza è che sto lavorando in Threejs e non in unità. Siamo riusciti a risolvere il problema aumentando il numero di triangoli, ma vorremmo comunque provare una trama proiettiva. Hai idea di come iniziare a implementarlo in Threejs? Grazie!
Dživo Jelić,

1
Praticamente allo stesso modo. È necessaria una terza coordinata di trama per memorizzare il divisore, quindi eseguire la divisione nello shader di frammento. Se hai avuto problemi ad applicare questo al tuo caso, porre una nuova domanda ti permetterà di includere un esempio di codice, che mostra come stai disegnando la tua mesh finora, su cui le risposte possono basarsi.
DMGregory

Devo memorizzare il divisore come terza coordinata (e a causa di quella struttura di modifica e tagliare la macro TRANSFORM_TEX, che non so come fare in trejs) o posso semplicemente passare il divisore come attributo allo shader di vertici e poi da lì come varianti allo shader di frammenti dove potrebbe essere usato per la divisione?
Dživo Jelić,

TRANSFORM_TEX è una macro Unity. Non ce l'hai in three.js, quindi non c'è niente da tagliare. Finché le coordinate uv interpolate e i divisori raggiungono lo shader di frammenti, non importa davvero come li hai portati lì. Hai riscontrato qualche problema nel fornirlo come attributo / variabile fino ad ora?
DMGregory

Non ho ancora provato perché non volevo perdere tempo fino a quando non ho verificato che è possibile. Solo un'ultima domanda, il divisore verrà anche interpolato una volta raggiunto lo shader di frammento, giusto? Vuoi dire che sul pixel tra due vertici sarà un valore interpolato e non un valore originale? Ho difficoltà a avvolgere la testa perché moltiplicare i raggi UV e poi dividerli aiuta, ma prenderò le tue parole e lo proverò. : leggermente_smiling_face: Grazie mille del tuo aiuto!
Dživo Jelić,
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.