OpenGL - Rilevamento dei bordi


12

Vorrei caricare mesh arbitrarie e tracciare spesse linee nere lungo i bordi per ottenere un aspetto simile a quello dei toni. Sono riuscito a disegnare una sagoma nera attorno agli oggetti usando il buffer dello stencil. Puoi vedere il risultato qui:

inserisci qui la descrizione dell'immagine

Ma ciò che manca sono le linee nere nell'oggetto stesso. Ho pensato di verificare le normali discontinuità: verificare se un pixel vicino ha un vettore normale diverso da quello attuale. Se sì, è stato trovato un vantaggio. Sfortunatamente, non ho idea di come potrei implementare questo approccio, né in OpenGL né in shader di vertici / frammenti GLSL.

Sarei molto felice di ricevere aiuto in merito a questo approccio o qualsiasi altro per quanto riguarda il rilevamento dei bordi.

Modifica: non utilizzo trame per le mie maglie.

Per essere più precisi, vorrei creare una soluzione CAD / CAM che assomigli il più possibile a questa (presa da Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4 ):

inserisci qui la descrizione dell'immagine


Credo che sia necessario definire il "bordo" più in dettaglio. In cosa differisce da un semplice wireframe? Nella risposta cifz c'è una buona descrizione del post-processo dello spazio dello schermo, ma dalla tua domanda è difficile determinare se è applicabile.
Andreas,

Bene, con bordo intendo le "pieghe" e le "creste" che formano i solidi. Un wireframe mostrerebbe tutte le facce triangolari, che non è quello che voglio.
enne87,

Ok, esattamente quello che ho chiesto :-) Genererei un wireframe da quelle pieghe e creste. La parte difficile è ancora determinare cos'è una piega / cresta. Hai idea di come farlo?
Andreas,

1
I programmi cad non lo fanno principalmente con uno shader. Al contrario, conoscono i bordi rigidi del modello e tracciano una linea sopra la mesh che contiene le informazioni.
joojaa,

joojaa hai ulteriori informazioni su questa tecnica? Cosa si fa in caso di superfici a forma libera a doppia curva? Inoltre, non sono sicuro di cosa succede quando hai un cono o un cilindro che viene tagliato / tagliato da qualcosa di libero. stackoverflow.com/questions/43795262/...
Dusan Bosnjak 'Pailhead'

Risposte:


18

Generalmente il rilevamento dei bordi si riduce per rilevare aree dell'immagine con un valore di gradiente elevato.

Nel nostro caso possiamo vedere grossolanamente il gradiente come la derivata della funzione immagine, quindi l'ampiezza del gradiente ti dà informazioni su quanto la tua immagine cambia localmente (rispetto ai pixel / texel vicini).
Ora, come dici tu, un vantaggio è un'indicazione di discontinuità, quindi ora che abbiamo definito il gradiente è chiaro che questa informazione è tutto ciò di cui abbiamo bisogno. Una volta trovato il gradiente di un'immagine, è solo una questione di applicare una soglia per ottenere un valore binario edge / non-edge.

Come trovi questo gradiente è davvero quello che stai chiedendo e devo ancora rispondere :)

Molti modi! Qui un paio :)

Funzioni shader integrate

Sia hlsl che glsl offrono funzioni derivate. In GLSL hai dFdx e dFdy che ti danno rispettivamente informazioni sul gradiente in direzione xey. In genere queste funzioni vengono valutate in un blocco di frammenti 2x2.
A meno che tu non sia interessato in una sola direzione, un buon modo per ottenere un risultato compatto che indica quanto è forte il gradiente nella regione è la larghezza che non ti dà nient'altro che la somma del valore assoluto di dFdy e dFdy.
È probabile che tu sia interessato a un bordo dell'immagine complessiva piuttosto che a un canale specifico, quindi potresti voler trasformare la tua funzione immagine in luminanza. Con questo in mente, quando si tratta di rilevamento dei bordi il tuo shader potrebbe includere qualcosa di simile a:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

Con una soglia alta troverai bordi più grossolani e potresti perdere alcuni, al contrario, con una soglia bassa potresti rilevare bordi falsi. Devi sperimentare per trovare la soglia più adatta alle tue esigenze.

Vale la pena menzionare il motivo per cui queste funzioni funzionano, ma non ho tempo per farlo ora, probabilmente aggiornerò questa risposta in seguito :)

Post-elaborazione dello spazio dello schermo

Potresti diventare più fantasioso di così, ora il campo del rilevamento dei bordi nell'elaborazione delle immagini è immenso. Potrei citarti decine di buoni modi per rilevare il rilevamento dei bordi in base alle tue esigenze, ma manteniamolo semplice per ora, se sei interessato posso citarti più opzioni!

Quindi l'idea sarebbe simile a quella sopra, con la differenza che potresti guardare in un quartiere più ampio e usare una serie di pesi su campioni circostanti se vuoi. In genere, esegui una convoluzione sull'immagine con un kernel che ti dà come risultato una buona informazione sul gradiente.
Una scelta molto comune è il kernel Sobel

                                   inserisci qui la descrizione dell'immagine

Che ti danno rispettivamente gradienti nelle direzioni xey:

                                  Da un vecchio pdf che ho scritto molto tempo fa.

GradientMagnitude=(Gradientx)2+(Gradienty)2

Quindi puoi eseguire la soglia come ho menzionato sopra.

Questo kernel, come puoi vedere, dà più peso al pixel centrale, in modo efficace sta calcolando il gradiente + un po 'di levigatura che aiuta tradizionalmente (spesso l'immagine è sfocata gaussiana per eliminare i piccoli bordi).

Quanto sopra funziona abbastanza bene, ma se non ti piace il livellamento puoi usare i kernel Prewitt:

                                                   inserisci qui la descrizione dell'immagine

(Nota che sono di fretta, scriverò presto il testo formattato corretto invece delle immagini!)

In realtà ci sono molti più kernel e tecniche per trovare il rilevamento dei bordi in un modo di elaborazione delle immagini piuttosto che in grafica in tempo reale, quindi ho escluso metodi più contorti (gioco di parole non inteso) come probabilmente staresti bene con le funzioni dFdx / y .


Spiegazione molto bella cifz, ma cosa succede se nessun gradiente è visibile in una determinata situazione? Ad esempio, non vi è alcuna fonte di luce sul retro del cubo e pertanto non è visibile alcun gradiente. Quindi questo processo di rilevamento dei bordi basato sull'immagine che descrivi non funzionerebbe, vero?
enne87,

Se usi un renderer differito puoi fare lo stesso ma sul buffer normale o di nuovo, se hai un prepass potresti applicare l'algoritmo alla profondità. Tuttavia hai ragione, un approccio allo spazio dello schermo potrebbe non essere l'ideale in tutti i casi :) Quale soluzione è fattibile dipende molto dal tuo budget per questo effetto, da quanto complessa è la tua scena.
cifz,

Potenzialmente se questo è veramente centrale per il tuo gioco, un metodo molto semplice, ma potenzialmente faticoso, sarebbe calcolare un gradiente sulle normali vicine per ciascun vertice (offline al momento del caricamento) e passare questo fattore come attributo di vertice aggiuntivo.
cifz,

Grazie ancora cifz per il vostro aiuto. Bene, quello che voglio ottenere è sviluppare una soluzione CAD / CAM che assomigli a questa: youtube.com/watch?v=-qTJZtYUDB4 E vorrei davvero sapere come sono riusciti a rendere tutti i bordi neri, le pieghe e creste. cifz, pensi che come specialisti della programmazione grafica abbiano raggiunto questo aspetto usando uno dei tuoi approcci spaziali? O forse calcolando un gradiente sulle normali vicine? Voglio davvero sapere come si può fare. Grazie ancora!
enne87,

1
Non è necessario pagare nessuno, basta iniziare a leggere alcuni tutorial online, acquistare alcuni libri e studiare sodo :) Alla fine sarà molto gratificante!
cifz,

3

Nel caso in cui qualcuno abbia anche bisogno di rilevare i bordi: ecco un bell'articolo su come visualizzare un wireframe e questo articolo spiega come mostrare solo i bordi.

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.