4.7.1. Materiali doppia faccia

0
Il tuo voto: Nessuno

Soprattutto nelle applicazioni real-time, l'utilizzo di materiali doppia faccia è utile nell'approssimazioni di un oggetto con spessore molto limitato (in genere meno di un centimetro) come: fogli di carta e di plastica, pannelli in metallo, ecc...

La soluzione presentata fa leva sulla semantica VFACE disponibile dagli Shader Model 3.0 in poi che, se applicata ad un parametro di tipo float in ingresso ad un Pixel Shader, indica l'orientamento del triangolo di cui il pixel processato fa parte. Questo parametro può prendere due valori: 1 e -1 rispettivamente quando il triangolo è front-face rispetto al punto di vista e back-face rispetto al punto di vista.

Grazie a questa informazione supplementare sarà possibile renderizzare in un solo passaggio (quindi con l'invio della geometria alla GPU una sola volta) due differenti materiali: uno per l'interno ed uno per l'esterno della geometria.

Questo approccio prevede però il dimezzarsi del numero di samplers per materiale; nonstante ciò sarà possibile realizzare un dignitoso per-pixel normal mapping e specificare valori indipendenti per le componenti diffusa e speculare.

Vertex Shader:

  1 uniform float4x4 World;
  2 uniform float4x4 WorldView;
  3 uniform float4x4 Proj;
  4 
  5 // Posizione della telecamera in World-Space
  6 uniform float4 eyePos;
  7 // Posizione della sorgente luminosa omnidirezionale
  8 uniform float3 lightPos;
  9 
 10 // Struttura per gli elementi in uscita dal Vertex Shader
 11 struct VS_OUT
 12 {
 13     float4 oPos : POSITION;
 14     float2 oTexCoords : TEXCOORD0;
 15     float3 oLightVec : TEXCOORD1;
 16     float3 oViewVec : TEXCOORD2;
 17 };
 18 
 19 // Dichiarazione della funzione del Vertex Shader con elementi del vertice
 20 // inseriti direttamente nella lista dei parametri.
 21 VS_OUT mainVS(
 22     in float3 Pos  : POSITION,
 23     in float3 Normal : NORMAL,
 24     in float3 Tangent : TANGENT,
 25     in float3 Binormal : BINORMAL,
 26     in float2 TexCoords : TEXCOORD0)
 27 {
 28     // Dichiarazione della struttura per i dati in uscita
 29     VS_OUT Out;
 30 
 31     // Trasformazione del vertice da object-space a world-space
 32     float4 p = mul(float4(Pos, 1.0), World);
 33 
 34     // Passaggio della posizione trasformata in screen space...
 35     Out.oPos = mul(mul(p, WorldView), Proj);
 36     // ...e delle coordinate di textures al Pixel Shader
 37     Out.oTexCoords = TexCoords * float2(1, .5);
 38 
 39     // Calcolo della matrice di rotazione per la trasformazione da
 40     // world-space a tangent-space
 41     float3x3 objToTangentSpace;
 42     objToTangentSpace[0] = mul(float4(Tangent, 0.0), World);
 43     objToTangentSpace[1] = mul(float4(Binormal, 0.0), World);
 44     objToTangentSpace[2] = mul(float4(Normal, 0.0), World);
 45 
 46     // Calcolo dei vettori che vanno al vertice alla sorgente luminosa e
 47     // dal vertice alla posizione della telecamera.
 48     // Trasformazione dei vettori in tangent-space.
 49     float3 LightVec = lightPos - p;
 50     Out.oLightVec = mul(objToTangentSpace, LightVec);
 51     Out.oViewVec = mul(objToTangentSpace, (eyePos - p));
 52 
 53     return Out;
 54 }

Pixel Shader:

  1 // Colore ambientale
  2 uniform float4 ambientColor;
  3 
  4 // Textures del materiale front-face
  5 sampler2D frontDiffuseSampler;
  6 sampler2D frontNormalSampler;
  7 
  8 // Proprietà del materiale front-face
  9 uniform float4 frontDiffuseColor;
 10 uniform float4 frontSpecularColor;
 11 uniform float frontSpecularExponent;
 12 
 13 // Textures del materiale back-face
 14 sampler2D backDiffuseSampler;
 15 sampler2D backNormalSampler;
 16 
 17 // Proprietà del materiale back-face
 18 uniform float4 backDiffuseColor;
 19 uniform float4 backSpecularColor;
 20 uniform float backSpecularExponent;
 21 
 22 // Dichiarazione del Pixel Shader per materiali doppia faccia
 23 float4 litTexturedPS(
 24     float Side : VFACE,
 25     float2 TexCoord : TEXCOORD0,
 26     float3 LightVec : TEXCOORD1,
 27     float3 ViewVec : TEXCOORD2) : COLOR0
 28 {
 29     // Normalizzazione dei vettori provenienti dal Vertex Shader
 30     LightVec = normalize(LightVec);
 31     ViewVec = normalize(ViewVec);
 32 
 33     float4 ambient, diffuse, specular;
 34     float3 normal;
 35     float exponent;
 36 
 37     // Recupero di tutti i texel delle varie componenti del materiale e
 38     // modulazione con i rispettivi colori secondo la superficie visibile
 39     if (Side > 0.0)
 40     {
 41         // front-face
 42         ambient = tex2D(frontDiffuseSampler, TexCoord) * ambientColor;
 43         diffuse = tex2D(frontDiffuseSampler, TexCoord) * frontDiffuseColor;
 44         specular = frontSpecularColor;
 45         exponent = frontSpecularExponent;
 46 
 47         normal = tex2D(frontNormalSampler, TexCoord).xyz * 2.0 - 1.0;
 48 
 49     }
 50     else
 51     {
 52         // back-face
 53         ambient = tex2D(backDiffuseSampler, TexCoord) * ambientColor;
 54         diffuse = tex2D(backDiffuseSampler, TexCoord) * backDiffuseColor;
 55         normal = tex2D(backNormalSampler, TexCoord).xyz * 2.0 - 1.0;
 56         specular = backSpecularColor;
 57         exponent = backSpecularExponent;
 58 
 59         // Per le parti back-face è necessario invertire il verso della normale
 60         // in modo da eseguire correttamente l'illuminazione
 61         normal = -normal;
 62     }
 63 
 64     // Calcolo della componente di illuminazione diffusa
 65     float diff = saturate(dot(normal, LightVec));
 66     // Calcolo della componente dell'hot-spot speculare
 67     float spec = pow(saturate(dot(reflect(-ViewVec, normal), LightVec)), exponent);
 68 
 69     // Calcolo del colore definitivo come somma delle componenti
 70     return (ambient + diffuse * diff + specular * spec);
 71 }

Come visibile dal codice il cuore dell'implementazione si trova nel Pixel Shader dove è presente una gestione particolare della legge di lambert, invertendo la normale per le parti back-face, dovuta alla necessità di illuminare correttamente anche l'interno dell'oggetto. Secondo la variabile Side sono inoltre selezionate le caratteristiche del materiale corretto.

Quello visibile in figura è un toro geometrico annodato tagliato dal piano di clipping della telecamera.