Casino online









Mercato forex






B24. Immersione!


Durante le ultime lezioni avrete sicuramente notato, girovagando per il paesaggio, che se si va sott'acqua e si guarda verso l'alto si ottiene una veduta non molto bella:


Il cielo nero

Per risolvere questo inghippo bisogna apportare alcune piccole modifiche sia al codice sorgente XNA sia a quello HLSL. Per prima cosa, inseriamo una nuova variabile globale in testa alla classe Game, IsUnderwater, che determinerà quando siamo sott'acqua e quando non lo siamo più. Essa viene impostata nel metodo UpdatePosition:
    Private Sub UpdatePosition(ByVal Motion As Vector3)
        Dim Rotation As Matrix = _
            Matrix.CreateRotationX(UpDownRotation) * _
            Matrix.CreateRotationY(LeftRightRotation)
        Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)

        CameraPosition += RotMotion * MoveSpeed

        Dim Y As Single
        If (Math.Abs(CameraPosition.X) < Me.TerrainWidth / 2) And _
            (Math.Abs(CameraPosition.Z) < Me.TerrainLength / 2) Then
            Dim X1, X2, Z1, Z2 As Int32

            With CameraPosition
                X1 = CInt(Math.Floor(.X))
                X2 = CInt(Math.Ceiling(.X))
                Z1 = CInt(Math.Floor(.Z))
                Z2 = CInt(Math.Ceiling(.Z))
            End With

            If X1 <> X2 And Z1 <> Z2 Then
                Dim Q11, Q12, Q21, Q22 As Single

                Q11 = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
                Q12 = HeightData(X1 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
                Q21 = HeightData(X2 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
                Q22 = HeightData(X2 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)

                Dim X, Z As Single

                X = CameraPosition.X
                Z = CameraPosition.Z

                Y = (Q11 * (X2 - X) * (Z2 - Z) + _
                    Q21 * (X - X1) * (Z2 - Z) + _
                    Q12 * (X2 - X) * (Z - Z1) + _
                    Q22 * (X - X1) * (Z - Z1)) / ((X2 - X1) * (Z2 - Z1))
            Else
                Y = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
            End If
        End If

        If (IsJumping) And (CameraPosition.Y + JumpingYVelocity) > Y + 0.5 Then
            CameraPosition.Y += JumpingYVelocity
            JumpingYVelocity = JumpingYInitial - Gravity * JumpTime
        Else
            CameraPosition.Y = Y + 0.5
            IsJumping = False
        End If

        'L'espressione (CameraPosition.Y < WaterHeight) restituisce
        'True se la telecamera è sotto la superficie dell'acqua, 
        'altrimenti False
        IsUnderwater = (CameraPosition.Y < WaterHeight)

        UpdateView()
    End Sub 
Altra importante procedura da modificare è DrawRefractionMap, poiché essa taglia abitualmente tutto quello che si trova sopra la superficie dell'acqua. Ma se noi ci troviamo sotto di essa, dovremo vedere proprio ciò che viene eliminato: in sostanza, dobbiamo ribaltare il piano di sezione quando IsUnderwater = True:
    Private Sub DrawRefractionMap()
        'Tutto quello che si è detto prima si ottiene semplicemente
        'ponendo IsUnderwater come quarto parametro della funzione
        'CreatePlane. Infatti quando quel parametro è True il piano
        'viene ribaltato. La modifica che avevamo fatto tempo addietro si
        'rivela ora molto utile
        Dim RefractionPlane As Plane = _
            CreatePlane(WaterHeight, ViewMatrix, New Vector3(0, -1, 0), _ 
            IsUnderwater)
        Me.GraphicsDevice.ClipPlanes(0).Plane = RefractionPlane
        Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
        Me.GraphicsDevice.SetRenderTarget(0, RefractionTarget)
        Me.GraphicsDevice.Clear(ClearOptions.Target Or _ 
            ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
        'Altra cosa importantissima: quando siamo sul terreno, la refraction
        'map ci permette solo di vedere sotto la superficie dell'acqua e
        'quindi non c'è bisogno di disegnare il cielo. Ma se si
        'guarda verso l'alto, allora è necessario reintrodurlo
        If IsUnderwater Then
            DrawSky(ViewMatrix)
        End If
        DrawTerrain(ViewMatrix)
        Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False

        Me.GraphicsDevice.SetRenderTarget(0, Nothing)
        RefractionMap = RefractionTarget.GetTexture()
        Me.GraphicsDevice.Clear(Color.CornflowerBlue)
    End Sub 
Per far funzionare le modifiche apportate, bisogna però modificare anche il codice HLSL. Per prima cosa introduciamo una nuova variabile globale Underwater di tipo boolean, sotto la prima lista di parametri:
float4x4 View;
float4x4 Projection;
float4x4 World;
float3 LightDirection;
float AmbientFactor;
bool LightEnabled;
//Determina se ci si trova sott'acqua
bool Underwater; 
Spostiamoci nel Pixel Shader della tecnica Water:
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;
    float4 ReflectionColor = tex2D(ReflectionSampler, NewTexCoords);    
    
    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);
    
    //Se ci si trova sott'acqua, imposta ReflectionColor = RefractionColor.
    //Questo avviene perchè la reflection map quando ci si
    //trova sott'acqua non esiste: primo, perchè non c'è
    //riflessione; secondo, perchè non abbiamo modificato
    //il codice XNA opportunamente. Questo significa che si vede solo
    //ciò che si trova sopra la superficie, come ci dice la
    //refraction map
    if(Underwater)
      ReflectionColor = RefractionColor;
     
    float3 Vector = normalize(CameraPosition - inPixel.PointPosition);
    float3 Normal = float3(0, 1, 0);
    float  FresnelFactor = dot(Vector, Normal);    
    float4 Result = lerp(ReflectionColor, RefractionColor, FresnelFactor);
     
    float4 WaterColor = float4(0.35f, 0.35f, 0.55f, 0.9f);
     
    Output.Color = lerp(Result, WaterColor, 0.15f);
    
    Normal = (BumpColor.rbg - 0.5f) * 2.0f;
    float3 ReflectionVector = -reflect(LightDirection, Normal);
    float  Specular = dot(normalize(ReflectionVector), normalize(Vector));
    Specular = pow(Specular, 256);        
    Output.Color.rgb += Specular;
     
    return Output;
} 
Ricordatevi di aggiungere questa riga:
Shader.Parameters("Underwater").SetValue(IsUnderwater) 
nel metodo DrawWater.
A questo punto, otterrete un risultato simile:


Il cielo visto da sott'acqua

Per raffinare ulteriormente il fondale, potremmo introdurre un effetto nebbia artigianale, che faccia sfumare verso un azzurrino le parti più lontane. Per far questo spostate il parametro CameraPosition all'inizio del file, sotto Underwater, poi modificate come segue:
struct MultiVertexToPixel
{
    float4 Position         : POSITION;    
    float4 Color            : COLOR0;
    float3 Normal           : TEXCOORD0;
    float2 TextureCoords    : TEXCOORD1;
    float4 LightDirection   : TEXCOORD2;
    float4 TextureWeights   : TEXCOORD3;
    float Depth             : TEXCOORD4;
    //Contiene la posizione 3D del punto
    float4 StartPos         : TEXCOORD5;
};

//...

MultiVertexToPixel MultiTexturedVertexShader(float4 inPos : POSITION, 
    float3 inNormal: NORMAL, float4 inTexCoords: TEXCOORD0, 
    float4 inTexWeights: TEXCOORD1)
{    
    MultiVertexToPixel Output = (MultiVertexToPixel)0;
	
    float4x4 ViewProjection = mul(View, Projection);
    float4x4 WorldViewProjection = mul(World, ViewProjection);
    
    //Calcola la posizione 3D del punto
    Output.StartPos = mul(inPos, World);
    Output.Position = mul(inPos, WorldViewProjection);
    Output.Normal = mul(normalize(inNormal), World);
    Output.TextureCoords = inTexCoords;
    Output.LightDirection.xyz = -LightDirection;
    Output.LightDirection.w = 1;    
    Output.TextureWeights = inTexWeights;
    Output.Depth = Output.Position.z / Output.Position.w;
    
    return Output;    
}

MultiPixelToFrame MultiTexturedPixelShader(MultiVertexToPixel inPixel)
{
    MultiPixelToFrame Output = (MultiPixelToFrame)0;        
    
    float lightingFactor = 1;
    if (LightEnabled)
        lightingFactor = saturate(saturate(dot(inPixel.Normal, inPixel.LightDirection)) + AmbientFactor);
        
    float blendDistance = 0.99f;
    float blendWidth = 0.005f;
    float blendFactor = clamp((inPixel.Depth - blendDistance) / blendWidth, 0, 1);
        
    float4 farColor;
    farColor = tex2D(TextureSampler0, inPixel.TextureCoords) * inPixel.TextureWeights.x;
    farColor += tex2D(TextureSampler1, inPixel.TextureCoords) * inPixel.TextureWeights.y;
    farColor += tex2D(TextureSampler2, inPixel.TextureCoords) * inPixel.TextureWeights.z;
    farColor += tex2D(TextureSampler3, inPixel.TextureCoords) * inPixel.TextureWeights.w;
    
    float4 nearColor;
    float2 nearTextureCoords = inPixel.TextureCoords * 3;
    nearColor = tex2D(TextureSampler0, nearTextureCoords) * inPixel.TextureWeights.x;
    nearColor += tex2D(TextureSampler1, nearTextureCoords) * inPixel.TextureWeights.y;
    nearColor += tex2D(TextureSampler2, nearTextureCoords) * inPixel.TextureWeights.z;
    nearColor += tex2D(TextureSampler3, nearTextureCoords) * inPixel.TextureWeights.w;

    Output.Color = lerp(nearColor, farColor, blendFactor);
    Output.Color *= lightingFactor;
    
    if(Underwater)
      {
        //Crea il colore azzurrino/bluastro/grigio che caratterizza 
        //il fondale
        float4 WaterColor = float4(0.592f, 0.714f, 0.835f, 0.9f);
        //Calcola la distanza tra il punto e la telecamera, usando 
        //la funzione predefinita distance
        float Distance = distance(inPixel.StartPos, CameraPosition);
        //Esegue un'interpolazione lineare tra il colore ottenuto prima
        //e quello del fondale, secondo un fattore Distance / 30.0f, che
        //viene ristretto dalla funzione clamp tra 0.2f e 0.7f. Questa
        //restrizione di campo implica due cose:
        //1. Anche il terreno vicino all'osservatore è sempre un 
        //po' sfumato
        //2. Le parti più lontane non diventano azzurre, ma mentengono
        //un elevato coefficiente di sfumature che permette comunque 
        //di riconoscere la texture della sabbia
        Output.Color = lerp(Output.Color, WaterColor, clamp(Distance / 30.0f, 0.2f, 0.7f));
      }
    
    return Output;
} 
Il risultato è questo:


Il cielo visto da sott'acqua

Niente male per ora!








 

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