Questo post-processo permette di distorcere un'immagine sorgente sulla base delle normali per-pixel calcolate a partire da una geometria. È utile nel rendering di effetti speciali di distorsione come bolle in scene subacquee o di materiali trasparenti che generano rifrazione soprattutto nel deferred-shading renderizzando gli oggetti trasparenti all'ultimo step; nel secondo caso il risultato ottenuto non è ovviamente fisicamente corretto, tuttavia l'individuazione di una corretta rifrazione rispetto ad una generata senza basarsi su leggi fisiche è motlo difficile specialmente se la geometria utilizzata è complessa.
Per realizzare questo effetto è necessario renderizzare su un render-target le normali per-pixel della geometria, che verrà utilizzata come distorsione o come oggetto trasparente, in view-space; ovvero è necessario trasformare la normale in ingresso al Vertex Shader con la matrice View della telecamera in uso.
1 float4x4 matViewProjection;
2 float4x4 matView;
3
4 struct VS_OUTPUT
5 {
6 float4 Position : POSITION;
7 float3 Normal : TEXCOORD0;
8 };
9
10 VS_OUTPUT vs_main(float4 Position : POSITION,
11 float3 Normal : NORMAL)
12 {
13 VS_OUTPUT Output;
14
15 // Projezione i screen-space
16 Output.Position = mul(Position, matViewProjection);
17 // Passaggio della normale in VIEW-SPACE al pixel shader
18 Output.Normal = mul(Normal, matView);
19
20 return Output;
21 }
Visualizzazione del render-target delle normali:

In questo modo le componenti x ed y del vettore normale rappresenteranno direttamente la quantità di spostamento sul piano dello schermo e potranno essere utilizzate come valori da aggiungere alle coordinate di texture durante il recupero del texel dall'immagine sorgente. È importante sottolineare che, qualora la geometria non copra tutta la superficie del render-target, prima di eseguire il rendering dell'oggetto, sarà necessario cancellarla con il colore (0.5, 0.5, 1.0) che corrisponde, una volta espanso, alla normale (0.0, 0.0, 1.0) che non produce distorsione.
1 // Sampler dell'immagine sorgente
2 sampler2D sourceSampler;
3 // Sampler delle normali per-pixel
4 sampler2D normalSampler;
5
6 // Intensità della distorsione
7 uniform float warpStrength;
8
9 float4 ps_main(float2 TexCoords : TEXCOORD0) : COLOR0
10 {
11 // Recupero le informazioni dal render-target
12 float3 normal = tex2D(normalSampler, TexCoords) * 2.0 - 1.0;
13
14 // Spostamento delle coordinate di texture secondo
15 // le componenti x e y della normale del pixel corrente
16 // ridimensionate secondo l'intensità della distorsione
17 TexCoords += normal.xy * warpStrength;
18
19 float4 color = tex2D(sourceSampler, TexCoords);
20
21 return color;
22 }
Risultato dell'implementazione:

Se utilizzato per la realizzazione della pseudo-rifrazione-riflessione di un oggetto trasparente, per rendere più credibile l'effetto, sarà utile modificare lo shader precedente con il calcolo dei vettori di riflessione e rifrazione rispetto alla normale perpendicolare al piano dello schermo (0.0, 0.0, -1.0).
1 // Sampler dell'immagine sorgente
2 sampler2D sourceSampler;
3 // Sampler delle normali per-pixel
4 sampler2D normalSampler;
5
6 // Intensità della distorsione per la rifrazione/riflessione
7 uniform float refractStrength;
8
9 // Indice di rifrazione
10 uniform float IOR;
11
12 // Normale in screen-space
13 const float3 screenNormal = float3(0.0, 0.0, -1.0);
14
15 float4 ps_main(float2 TexCoords : TEXCOORD0) : COLOR0
16 {
17 // Recupero le informazioni dal render-target
18 float4 bgColor = tex2D(sourceSampler, TexCoords);
19 float4 normal = tex2D(normalSampler, TexCoords);
20
21 // Se all'interno della geometria
22 if (normal.w > 0.0)
23 {
24 // Espando la normale
25 normal.xyz = normal.xyz * 2.0 - 1.0;
26
27 // Calcolo la componente fresnel in modo molto approssimativo
28 float fres = 1 - saturate(dot(normal.xyz, screenNormal));
29
30 // Calcolo i vettore riflesso e rifratto rispetto alla normale
31 float3 reflVec = reflect(screenNormal, normal);
32 float3 refrVec = refract(screenNormal, normal, IOR);
33
34 // Inverto l'asse verticale perchè le coordinate di texture
35 // incrementano dall'alto verso il basso
36 reflVec.y *= -1;
37 refrVec.y *= -1;
38
39 // Spostamento delle coordinate di texture secondo
40 // le componenti x e y dei vettori di riflessione e rifrazione
41 // ridimensionate secondo l'intensità della distorsione
42 // Recupero il texel secondo la distorsione
43 float4 reflColor = tex2D(sourceSampler, TexCoords + reflVec.xy * refractStrength);
44 float4 refrColor = tex2D(sourceSampler, TexCoords + refrVec.xy * refractStrength);
45
46 // Interpolazine tra riflesso e rifrazione
47 return lerp(refrColor, reflColor, fres);
48 }
49
50 return bgColor;
51 }
Risultato dell'implementazione:
