9.6. Desaturazione in base alla distanza

5
Il tuo voto: Nessuno Media: 5 (1 vote)

Questo tipo di post-processo è utilizzato soprattutto nel rendering di grandi spazi aperti; simula il fenomeno per cui i colori perdono la loro brillantezza e saturazione con l'aumentare della distanza dall'osservatore. Questo fenomeno è dovuto al fatto che non tutti i spettri della luce solare sono assorbiti e rifratti dall'atmosfera nello stesso modo producendo un decadimento maggiore per le tonalità di colore rosso-arancio.

Anche se di semplice implementazione (in questo caso non fisicamente corretto), aumenta notevolmente il realismo della scena, in special modo se la desaturazione avviene verso un colore in tonalità di azzurro (per i concetti espressi sopra); perchè l'effetto risulti realistico l'intensità della desaturazione deve essere sempre molto contenuta.

L'effetto è diviso in due parti: la prima costruisce una depth-map renderizzando la scena dal punto di vista dell'osservatore, la seconda utilizza questa depth-map per campionare la distanza dei pixels del viewport corrente e determinare la loro desaturazione.

  1 float4x4 ViewProj;
  2 float4x4 View;
  3 
  4 struct VS_OUTPUT
  5 {
  6     float4 Position : POSITION0;
  7     float2 TexCoords : TEXCOORD0;
  8     float3 Normal : TEXCOORD1;
  9     float Depth : TEXCOORD2;
 10 };
 11 
 12 VS_OUTPUT vs_main(float4 Position : POSITION0,
 13                     float3 Normal : NORMAL,
 14                     float2 TexCoords : TEXCOORD0)
 15 {
 16     VS_OUTPUT Output;
 17 
 18     // Proiezione a schermo dei vertici del modello
 19     Output.Position = mul(Position, ViewProj);
 20     // Calcolo della distanza (z in view-space)
 21     Output.Depth = mul(Position, View).z;
 22 
 23     // Passo coordinate di texture e normale al Pixel Shader
 24     Output.TexCoords = TexCoords;
 25     Output.Normal = Normal;
 26 
 27     return Output;
 28 }

Quello precedente è il Vertex Shader che si occupa di renderizzare la scena e passare la distanza dall'osservatore (linea 21) al Pixel Shader; il Pixel Shader è stato omesso dato che riporta semplicemente la distanza nel render-target prestabilito.

Quello successivo è invece il codice del Pixel Shader utilizzato nel rendering del rettangolo a tutto schermo che realizza l'effetto.

  1 // Sampler dell'immagine sorgente e della depth map
  2 sampler2D sceneSampler;
  3 sampler2D depthSampler;
  4 
  5 // Intensità della desaturazione
  6 uniform float desatStrength;
  7 // Colore di desaturazione
  8 uniform float4 desatColor;
  9 
 10 // Costanti per l'influenza delle singole componenti
 11 const float4 luminanceConstants = float4(0.2125, 0.7154, 0.0721, 0.0000);
 12 
 13 float4 ps_main(float2 TexCoords : TEXCOORD0) : COLOR0
 14 {
 15     // Recupero le informazioni dai render-targets per il pixel corrente
 16     float4 color = tex2D(sceneSampler, TexCoords);
 17     float depth = tex2D(depthSampler, TexCoords).r;
 18 
 19     // Calcolo intensità corrispondente
 20     float intensity = dot(color, luminanceConstants);
 21     // Calcolo la quantità di desaturazione
 22     float desat = saturate(depth * desatStrength);
 23 
 24     // Interpolazione tra colore ed intensità equivalente
 25     return lerp(color, intensity * desatColor, desat);
 26 }

Risultato dell'implementazione: