Che cos'è fwidth e come funziona?


18

La documentazione di OpenGL afferma che fwidth returns the sum of the absolute value of derivatives in x and y.

Cosa significa questo in termini meno matematici e c'è un modo per visualizzarlo?

Sulla base della mia comprensione della funzione, fwidth(p)ha accesso al valore di pin pixel vicini. Come funziona sulla GPU senza influire drasticamente sulle prestazioni e funziona in modo affidabile e uniforme su tutti i pixel?

Risposte:


18

Pixel derivati screen-space fanno drasticamente le prestazioni impatto, ma hanno impatto sulle prestazioni se li usate o no, in modo da un certo punto di vista sono liberi!

Ogni GPU nella storia recente racchiude un quadruplo di quattro pixel e li mette nello stesso warp / wavefront, il che significa essenzialmente che sono in esecuzione uno accanto all'altro sulla GPU, quindi accedere ai valori da essi è molto economico. Poiché i warp / wavefronts vengono eseguiti in sequenza, anche gli altri pixel si troveranno esattamente nello stesso posto nello shader in cui ci si trova, quindi il valore di pper quei pixel sarà semplicemente inserito in un registro che ti aspetta. Questi altri tre pixel verranno sempre eseguiti, anche se i loro risultati verranno eliminati. Quindi un triangolo che copre un singolo pixel ombreggerà sempre quattro pixel e getterà via i risultati di tre di essi, solo in modo che queste funzioni derivate funzionino!

Questo è considerato un costo accettabile (per l'hardware corrente) perché non sono solo funzioni come quelle fwidthche usano questi derivati: lo fa anche ogni singolo campione di trama, al fine di scegliere da quale mipmap della trama leggere. Considera: se sei molto vicino a una superficie, la coordinata UV che stai usando per campionare la trama avrà una derivata molto piccola nello spazio dello schermo, il che significa che devi usare una mipmap più grande, e se sei più lontano la coordinata UV avrà una derivata più grande nello spazio dello schermo, il che significa che è necessario utilizzare una mipmap più piccola.

Per quanto riguarda ciò che significa in termini meno matematici: fwidthè equivalente a abs(dFdx(p)) + abs(dFdy(p)). dFdx(p)è semplicemente la differenza tra il valore di pat pixel x + 1 e il valore di pat pixel x, e similmente per dFdy(p).


Quindi, se dFdx(p) = p(x1) - p(x), allora x1può essere uno (x+1)o (x-1), a seconda della posizione del pixel xnel quad. In entrambi i casi, x1deve trovarsi nello stesso ordito / fronte d'onda di x. Ho ragione?
ApoorvaJ,

3
@ApoorvaJ In sostanza dFdxviene calcolato lo stesso valore per ciascuno dei 2 pixel vicini nella griglia 2x2. E questo valore viene appena calcolato usando la differenza tra i due valori vicini, se questo è p(x+1)-p(x)o p(x)-p(x-1)dipende solo dalla tua nozione di ciò che xè esattamente qui. Il risultato è lo stesso comunque. Quindi sì, hai ragione.
Chris dice di reintegrare Monica il

@ChristianRau Questo risponde alla mia domanda. Grazie.
ApoorvaJ,

11

In termini interamente tecnici, fwidth(p)è definito come

fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))

E dFdx(p)/ dFdy(p)sono le derivate parziali del valore prispetto alle dimensioni dello schermo xe y. Quindi indicano come psi comporta il valore di quando si passa di un pixel a destra ( x) o di un pixel in alto ( y).

Ora, come possono essere calcolati praticamente? Bene, se conosci i valori dei pixel vicini per p, puoi semplicemente calcolare quei derivati ​​come differenze finite dirette come approssimazione per i loro derivati ​​matematici effettivi (che potrebbero non avere affatto una soluzione analitica esatta):

dFdx(p) := p(x+1) - p(x)

Ma ovviamente ora potresti chiederti, come facciamo a conoscere i valori di p(che potrebbero essere qualsiasi valore calcolato arbitrariamente all'interno del programma shader) per i pixel vicini? Come possiamo calcolare quei valori senza incorrere in grandi spese generali eseguendo il calcolo dell'intero shader due (o tre) volte?

Bene, sai cosa, quei valori vicini vengono calcolati comunque, poiché per il pixel vicino esegui anche uno shader di frammenti. Quindi tutto ciò di cui hai bisogno è l'accesso a questo richiamo dello shader di frammenti vicini quando eseguito per il pixel vicino. Ma è ancora più facile, perché anche quei valori vicini vengono calcolati nello stesso momento.

I rasterizzatori moderni chiamano shader di frammenti in tessere più grandi di più di un pixel adiacente. Al minimo quelli sarebbero una griglia di pixel 2x2. E per ciascuno di questi blocchi di pixel viene richiamato lo shader di frammenti per ciascun pixel e tali invocazioni vengono eseguite in una sequenza di blocchi perfettamente parallela in modo che tutti i calcoli vengano eseguiti nello stesso identico ordine e nello stesso momento per ciascuno di quei pixel nel blocco (che è anche il motivo per cui la ramificazione nello shader di frammenti, sebbene non mortale, dovrebbe essere evitata se possibile, poiché ogni invocazione di un blocco dovrebbe esplorare ogni ramo che è preso da almeno una delle invocazioni, anche se si butta via i risultati in seguito, come indicato anche nelle risposte a questa domanda correlata). Quindi, in qualsiasi momento, uno shader di frammenti ha teoricamente accesso ai valori dello shader di frammento dei pixel vicini. E mentre non si ha accesso diretto a quei valori, si ha accesso a valori calcolati da loro, come le funzioni derivate dFdx, dFdy, fwidth, ...

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.