INDICE: it.Of Effects and Shaders
Precedente: Le Coordinate della Texture
Prossimo: Textures Multiple
Partendo dalla dimensione originale di una texture (Altezza/Larghezza, WIDTH/HEIGHT) si può calcolare la distanza tra la coordinata di un pixel e quella di un pixel adiacente in orizzontale nell'unità di misura basata sulla texture. Posto, ad esempio, che coordinata 1 = Larghezza (WIDTH) possiamo dire che la distanza tra un dato pixel ed un pixel adiacente sia 1 / WIDTH (pensate a più textures con dimensioni diverse). La dimensione di un pixel in relazione alla texture può essere indicata così:
float2 pixelSize = 1 / float2(WIDTH/HEIGHT)
Usando pixelSize.xy si può accedere quindi ai pixel adiacenti aggiungendo/sottraendo questo valore alle coordinate del pixel.
Un esempio potrebbe essere il filtro per l'individuazione dei bordi in cui per ogni pixel prendiamo i suoi adiacenti a destra e sinistra, calcoliamo quanto differiscano tra di loro e controlliamo se il valore assoluto del risultato sia maggiore di un certo limite. Se lo è, il pixel viene colorato di bianco, altrimenti di nero.
float2 PixelSize; float Threshold = 0.2; float4 PS(vs2ps In): COLOR { //l'offset delle coordinata delle texture con la coordinata verticale impostata a 0 float2 off = float2(PixelSize.x, 0); //campiona i pixels adiacenti a sinistra e destra float4 left = tex2D(Samp, In.TexCd - off); float4 right = tex2D(Samp, In.TexCd + off); if (abs(ConvertToGray(left).x - ConvertToGray(right).x) > Threshold) return 1; else return float4(0, 0, 0, 1); }
Ci sono due cose da segnalare nel codice qui sopra:
Per migliorare l'individuazione dei bordi è necessario eseguire l'operazione anche in verticale. Così ora campioniamo anche i pixel in alto ed in basso:
//'offset delle coordinata delle texture con la coordinata orizzontale impostata a 0 off = float2(0, PixelSize.y); //campiona i pixels adiancenti in alto ed in basso float4 upper = tex2D(Samp, In.TexCd - off); float4 lower = tex2D(Samp, In.TexCd + off);
a questo punto dobbiamo tenere in considerazione questi pixel assieme ai pixel a destra ed a sinistra prima di calcolare il colore finale
if (abs(ConvertToGray(left) - ConvertToGray(right)).x > Threshold || abs(ConvertToGray(upper) - ConvertToGray(lower)).x > Threshold) ...
dove ||, nel linguaggio hlsl, è la booleana OR, vale a dire che coloriamo il pixel di bianco se la differenza tra i pixels a destre e sinistra supera una certa soglia OR questo succede tra i pixels in alto ed in basso.
Anche una sfocatura può essere realizzata in questa maniera. Migliore deve essere il risultato, maggiore deve essere la quantità di pixels adiacenti presi in considerazione. Dato che il numero dei campioni per ogni pixel di una texture è limitato a 16, una buona sfocatura si ottiene eseguendola in due passaggi. Prima orizzontalmente, poi si prende il risultato e si ripete la sfocatura in verticale. Cominciamo con la sfocatura orizzontale.
L'idea è quella di campionare una serie di pixels adiacenti, sommare i loro colori, mentre si valuta il peso della loro influenza in rapporto alla loro distanza dal pixel scelto. Il pixel "target" ha quindi il maggior peso, mentre i 2 pixel all'estrema destra e all'estrema sinistra hanno un peso minore. Il risultato deve quindi essere riportato nell'intervallo 0...1 facendo la media di tutti i valori sommati.
float2 PixelSize; float4 PSHorizontalBlur(vs2ps In): COLOR { float4 sum = 0; int weightSum = 0; //i pesi dei pixels adiacenti int weights[15] = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1}; //prendiamo 15 campioni for (int i = 0; i < 15; i++) { //7 a sinistra, il "target", 7 a destra float2 cord = float2(In.TexCd.x + PixelSize.x * (i-7), In.TexCd.y); //i campioni vengono pesati in base alla relazione con il pixel "target" sum += tex2D(Samp, cord) * weights[i]; //mentre il loop prosegue, sommiamo i vari pesi weightSum += weights[i]; } sum /= weightSum; return float4(sum.rgb, 1); }
Per effettuare quest'operazione in verticale, dovremmo duplicare la funzione PS(), chiamarla PSVerticalBlur() e cambiare il codice così che calcoli la sfocatura in verticale:
float4 PSVerticalBlur(vs2ps In): COLOR { ... //7 sopra, il "target" e 7 sotto float2 cord = float2(In.TexCd.x, In.TexCd.y + PixelSize.y * (i-7)); ... }
Ora che esistono due funzioni pixelshader sono necessarie anche due tecniche:
technique THorizontalBlur { pass P0 { PixelShader = compile ps_2_0 PSHorizontalBlur(); } } technique TVerticalBlur { pass P0 { PixelShader = compile ps_2_0 PSVerticalBlur(); } }
In questo modo si possono usare le due tecniche alternativamente, ma come detto prima, per un risultato migliore si dovrebbe applicare il risultato della sfocatura orizzontale su quella verticale. Ci saranno altri tutorial, dopo il prossimo capitolo, sugli effetti con più passaggi (MultiPass Effects).
Queste sono le basi per l'elaborazione delle immagini basata su pixels adiacenti, ma c'è tutta una serie di filtri simili comunemente chiamati filtri di convoluzione/kernel. Con quello che dovresti aver imparato finora, puoi essere in grado di proseguire in questa direzione da solo/a. Vedi qui e qua per dettagli ed esempi.
anonymous user login
~11h ago
~7d ago
~7d ago
~8d ago
~21d ago
~1mth ago
~1mth ago
~1mth ago
~1mth ago
~1mth ago