Casino online









Mercato forex






B19. L'effetto Fresnel


Ora è venuto il momento di conferire trasparenza all'acqua, e renderla più realistica. Per far questo, aggiungeremo alla superficie perfettamente riflettente che abbiamo appena creato gli effetti derivanti dalla rifrazione, usando, quindi, anche la Refraction Map. Tuttavia, per miscelare i due elementi, bisogna prima sapere come viene matematicamente attuato il calcolo del coefficiente di Fresnel, che determina quanta riflessione e quanta rifrazione ci siano in ogni punto della superficie.
L'unico fattore che influisce su tale coefficiente è l'angolazione con cui la telecamera osserva l'acqua: più ci si avvicina alla normale (sguardo a 90° sulla superficie) e maggiore sarà il contributo della rifrazione, fino a diventare il 100% in corrispondenza del vettore; più la direzione dello sguardo si avvicina ad essere parallela alla superficie, e maggiore sarà il contributo della riflessione, fino a diventare il 100% quando si guarda dritto verso l'orizzonte. In conclusione, la composizione dei due fenomeni può essere schematicamente riassunta così:


Componenti dell'effetto Fresnel

Nello schema, la freccia blu indica la direzione con la quale si sta osservando, e il raggio verticale superiore simbolizza la normale della superficie. Dato che la normale è sempre un vettore di lunghezza unitaria, la circonferenza tracciata ha anch'essa raggio unitario, ed è perciò una circonferenza goniometrica, dove le componente della rifrazione coincide perfettamente con il seno dell'angolo θ segnalato in figura. Con una semplice formula di trigonometria, però, possiamo provare che sinθ = cos(θ - π/2).


Dimostrazione

In questa figura più geometricizzata, abbiamo che OB è il vettore normale di modulo 1, e AO è un vettore anch'esso di modulo 1, il cui verso coincide con quello del vettore telecamera-origine. Quindi, il coseno di θ - π/2 coincide con il coseno dell'angolo AOB, che è l'angolo compreso tra i due vettori. Qual è quella operazione che, nel caso di vettori di lunghezza unitaria, restituisce il coseno dell'angolo fra essi compreso? Il prodotto scalare, ancora una volta: la funzione dot. Anche in questo caso, come nel calcolo della luce, useremo dot() per calcolare le componenti dell'effetto fresnel.


Modificare il codice HLSL
Dato che abbiamo bisogno di un nuovo parametro (la posizione della telecamera) e due nuovi membri della struttura WaterVertexToPixel (ossia la posizione del punto nello spazio e le coordinate texture della refraction map), modifichiamo il codice di conseguenza:
//...
float3 CameraPosition;

//...

struct WaterVertexToPixel
{
    float4 Position           : POSITION;
    float4 ReflectionPos      : TEXCOORD1;
    float2 BumpPos            : TEXCOORD2;
    float4 RefractionPos      : TEXCOORD3;
    float4 PointPosition      : TEXCOORD4;
};

//...

WaterVertexToPixel WaterVertexShader(float4 inPos : POSITION, 
    float2 inTex: TEXCOORD)
{    
    WaterVertexToPixel Output = (WaterVertexToPixel)0;

    float4x4 ViewProjection = mul(View, Projection);
    float4x4 WorldViewProjection = mul(World, ViewProjection);
    
    float4x4 ReflectionViewProjection = mul(ReflectionView, Projection);
    float4x4 WorldReflectionViewProjection = mul(World, ReflectionViewProjection);

    Output.Position = mul(inPos, WorldViewProjection);
    Output.ReflectionPos = mul(inPos, WorldReflectionViewProjection);
    Output.RefractionPos = mul(inPos, WorldViewProjection);
    Output.BumpPos = inTex / WaveLength;
    //Calcola la posizione del punto dello spazio. Viene applicata solo
    //la matrice World perchè abbiamo bisogno della sua posizione
    //3D per poter calcolarne il prodotto scalare
    Output.PointPosition = mul(inPos, World);

    return Output;
}

WaterPixelToFrame WaterPixelShader(WaterVertexToPixel inPixel)
{
    WaterPixelToFrame Output = (WaterPixelToFrame)0;        
    
    float2 TexCoords;
    TexCoords.x = (inPixel.ReflectionPos.x / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
    TexCoords.y = -(inPixel.ReflectionPos.y / inPixel.ReflectionPos.w) / 2.0f + 0.5f; 
       
    float4 BumpColor = tex2D(BumpSampler, inPixel.BumpPos);
    float2 Perturbation = WaveHeight * (BumpColor.rg - 0.5f) * 2.0f;
    float2 NewTexCoords = TexCoords + Perturbation;
    //Quello che prima era direttamente il colore di output, ora
    //è immagazzinato in una variabile temporanea, per
    //essere ripreso in seguito
    float4 ReflectionColor = tex2D(ReflectionSampler, NewTexCoords);    
    
    //Calcola le coordinate texture della rifrazione, con lo stesso
    //metodo usato per quelle della riflessione
    float2 RefractionTexCoords;
    RefractionTexCoords.x = (inPixel.RefractionPos.x / inPixel.RefractionPos.w) / 2.0f + 0.5f;
    RefractionTexCoords.y = -(inPixel.RefractionPos.y / inPixel.RefractionPos.w) / 2.0f + 0.5f;
        
    float2 RefractionNewTexCoords = RefractionTexCoords + Perturbation;    
    float4 RefractionColor = tex2D(RefractionSampler, RefractionNewTexCoords);
     
    //Ottiene il vettore telecamera-punto, dove il punto rappresenta
    //l'origine nello schema precedente. Il risultato viene quindi
    //normalizzato per ridurre il sue modulo a 1
    float3 Vector = normalize(CameraPosition - inPixel.PointPosition);
    //Crea un vettore normale
    float3 Normal = float3(0, 1, 0);
    //Calcola il coefficiente di Fresnel con il sopracitato prodotto
    //scalare tra i due vettori
    float  FresnelFactor = dot(Vector, Normal);
    //Esegue un'interpolazione lineare tra il colore di riflessione
    //e quello di rifrazione per ottenere il colore del pixel
    float4 Result = lerp(ReflectionColor, RefractionColor, FresnelFactor);
    
    //Questa variabile rappresenta un colore azzurro-grigiastro, che
    //possiamo assimilare al "colore intrinseco" dell'acqua. Infatti
    //anche guardando attraverso la sua superficie, niente appare
    //esattamente del proprio colore.
    float4 WaterColor = float4(0.3f, 0.3f, 0.5f, 1.0f);
    
    //Aggiunge all'effetto Fresnel quell'azzurrino dell'acqua,
    //con un'interpolazione del 20%
    Output.Color = lerp(Result, WaterColor, 0.2f);
    
    return Output;
}  
Non dimenticatevi di passare il nuovo parametro posizione telecamera nel metodo DrawWater:
Shader.Parameters("CameraPosition").SetValue(CameraPosition)  
E otterrete un risultato simile a questo:


Effetto Fresnel: ho ridotto un po' la lunghezza d'onda







 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.