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 p
rispetto alle dimensioni dello schermo x
e y
. Quindi indicano come p
si 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
, ...
dFdx(p) = p(x1) - p(x)
, allorax1
può essere uno(x+1)
o(x-1)
, a seconda della posizione del pixelx
nel quad. In entrambi i casi,x1
deve trovarsi nello stesso ordito / fronte d'onda dix
. Ho ragione?