Casino online









Mercato forex






B17. Un primo rendering della tecnica


Ora che abbiamo finito di creare le maps necessario, si può iniziare a visualizzare l'acqua.


Alcune modifiche al vecchio codice
Prima di continuare, è necessario apportare alcune modifiche al vecchio codice. Per prima cosa, bisogna parametrizzare le procedure di disegno. Mi spiego meglio. Fino ad ora si è sempre dichiarato il metodo di disegno (DrawQualcosa) senza parametri, e si sono usate le variabili globali dichiarate in testa al sorgente. Ora non è più possibile, perchè nell'usare la nuova tecnica dell'acqua avremo bisogno di entrambe le matrici View e ReflectionView, che nel codice precedente si scambiavano l'un l'altra. Modificate il codice in modo da far accettare ai metodi Draw una matrice View che indica quale matrice di vista usare, o ricopiate il codice provvisto in fondo alla pagina alla fine del capitolo. Inoltre, sarà necessario avere ReflectionView come variabile globale.
In seconda istanza, dovremo impostare i buffer di vertici e di indici per il terreno nella procedura DrawTerrain, poiché useremo altri vertici per disegnare l'acqua. Modificate come segue:
    Private Sub DrawTerrain(ByVal View As Matrix)
        Shader.CurrentTechnique = Shader.Techniques("MultiTextured")

        Shader.Parameters("View").SetValue(View)
        Shader.Parameters("Projection").SetValue(ProjectionMatrix)
        Shader.Parameters("World").SetValue( _
            Matrix.CreateTranslation( _
            New Vector3(-TerrainWidth / 2, 0, TerrainLength / 2)))

        Dim LightDirection As New Vector3(27.73, -17.67, 6.14)
        LightDirection.Normalize()
        Shader.Parameters("LightEnabled").SetValue(True)
        Shader.Parameters("LightDirection").SetValue(LightDirection)
        Shader.Parameters("AmbientFactor").SetValue(0.3F)

        Shader.Parameters("Texture0").SetValue(Sand)
        Shader.Parameters("Texture1").SetValue(Grass)
        Shader.Parameters("Texture2").SetValue(Rock)
        Shader.Parameters("Texture3").SetValue(Snow)

        'Imposta i buffer
        Me.GraphicsDevice.Indices = IBuffer
        Me.GraphicsDevice.Vertices(0).SetSource( _
            VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)

        Shader.Begin()
        For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
            Pass.Begin()
            With Me.GraphicsDevice
                .VertexDeclaration = VDeclaration
                Me.GraphicsDevice.DrawIndexedPrimitives( _
                    PrimitiveType.TriangleList, 0, 0, _
                    Vertices.Length, 0, Indices.Length / 3)
            End With
            Pass.End()
        Next
        Shader.End()
    End Sub
    
    '...
    
    Protected Overrides Sub Draw(ByVal gameTime As GameTime)
        Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)

        DrawRefractionMap()
        DrawReflectionMap()

        DrawSky(ViewMatrix)
        DrawTerrain(ViewMatrix)
        DrawWater(gameTime.TotalGameTime.Seconds * 10)

        MyBase.Draw(gameTime)
    End Sub 


Impostare la superficie
Per disegnare la superficie dell'acqua, che è un quadrato, occorrono 2 triangoli: in questo caso, sarebbe inutile definire degli indici data l'infima quantità di dati con cui dobbiamo lavorare. Useremo, perciò solamente 6 vertici, tre per ogni triangolo. Prima dichiariamo le nuove variabili:
    Private WaterVertices() As VertexPositionTexture
    Private WaterVBuffer As VertexBuffer
    Private WDeclaration As VertexDeclaration 
Poi il nuovo metodo:
    Private Sub SetWaterVertices()
        ReDim WaterVertices(5)

        'Rappresentà metà del lato del quadrato
        'che delimita la superficie dell'acqua. Avremo
        'quindi un quadrato di 800 unità per lato,
        'con il centro nell'origine. Lo so, forse è
        'un po' eccessivo, ma non si sa mai...
        Dim HalfSize As Int16 = 400

        'Definisce i vertici dei due triangoli
        WaterVertices(0).Position = New Vector3(-HalfSize, WaterHeight, HalfSize)
        WaterVertices(1).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
        WaterVertices(2).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
        WaterVertices(3).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
        WaterVertices(4).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
        WaterVertices(5).Position = New Vector3(HalfSize, WaterHeight, -HalfSize)

        'E le loro coordinate texture
        WaterVertices(0).TextureCoordinate = New Vector2(0, 1)
        WaterVertices(1).TextureCoordinate = New Vector2(0, 0)
        WaterVertices(2).TextureCoordinate = New Vector2(1, 1)
        WaterVertices(3).TextureCoordinate = New Vector2(1, 1)
        WaterVertices(4).TextureCoordinate = New Vector2(0, 0)
        WaterVertices(5).TextureCoordinate = New Vector2(1, 0)

        'Carica il buffer
        WaterVBuffer = New VertexBuffer(Me.GraphicsDevice, _
            WaterVertices.Length * VertexPositionTexture.SizeInBytes, _
            BufferUsage.WriteOnly)
        WaterVBuffer.SetData(WaterVertices)

        'Crea la nuova dichiarazione
        WDeclaration = New VertexDeclaration(Me.GraphicsDevice, _ 
            VertexPositionTexture.VertexElements)
    End Sub 


La tecnica dell'acqua in HLSL
È venuto il momento di mettere le mani sul file shader. Fino ad ora ho fornito i sorgenti hlsl direttamente all'inizio del tutorial, ma da ora in poi, dato che ho esposto in breve il linguaggio, riporterò il codice passo passo esattamente come ho sempre fatto con XNA.
Aprite il file SimpleShader.fx nella cartella bin/Debug del progetto, con il blocco note o qualsiasi altro editor di testo, e portatevi in fondo al file. Iniziamo una nuova tecnica:
//--------------------------------------------
//Tecnica 5 : Acqua
//-------------------------------------------- 
Per prima cosa, bisogna dichiarare i parametri aggiuntivi che essa necessita per funzionare. Potete dichiararli qui alla fine o all'inizio, fa lo stesso:
//La matrice di vista speculare. Servirà per i calcoli
float4x4 ReflectionView;
//E le due maps
Texture ReflectionMap;
Texture RefractionMap;

//I sampler associati alle texture
sampler ReflectionSampler = sampler_state
{
    texture = <ReflectionMap>;
    Magfilter = Linear;
    Minfilter = Linear;
    Mipfilter = Linear;
    AddressU = Mirror;
    AddressV = Mirror;
};

sampler RefractionSampler = sampler_state
{
    texture = <RefractionMap>;
    Magfilter = Linear;
    Minfilter = Linear;
    Mipfilter = Linear;
    AddressU = Mirror;
    AddressV = Mirror;
}; 
Quindi definiamo le strutture usate per passare dati dal vertex shader al pixel shader:
//I dati elaborati dal vertex shader conterranno solo due membri:
//la posizione del pixel sullo schermo e la sua relativa posizione
//sulla reflection map. Non servirà nient'altro 
struct WaterVertexToPixel
{
    float4 Position           : POSITION;
    float4 ReflectionPos      : TEXCOORD1;
};

//Come sempre, il pixel shader restituisce in output una struttura
//contenente solo il colore finale del pixel
struct WaterPixelToFrame
{
    float4 Color : COLOR0;
}; 
Quindi scriviamo gli shader veri e propri:
//Il tipo di vertici usato è VertexPositionTexture, perciò
//i parametri del vertex shader saranno solo una posizione
//e una coppia di coordinate texture
WaterVertexToPixel WaterVertexShader(float4 inPos : POSITION, 
    float2 inTex: TEXCOORD)
{    
    WaterVertexToPixel Output = (WaterVertexToPixel)0;

    //Calcola la matrice risultante dal prodotto di World, View
    //e Projection. Questa, moltiplicata per inPos fornirà
    //la posizione del pixel sullo schermo, in accordo con la
    //matrice di vista originaria
    float4x4 ViewProjection = mul(View, Projection);
    float4x4 WorldViewProjection = mul(World, ViewProjection);
    
    //Calcola la matrice risultante dal prodotto di World,
    //ReflectionView e Projection. Questa, moltiplicata per inPos
    //fornirà la posizione del pixel sulla reflection map,
    //in accordo con la vista offerta dalla telecamera ribaltata
    float4x4 ReflectionViewProjection = mul(ReflectionView, Projection);
    float4x4 WorldReflectionViewProjection = 
        mul(World, ReflectionViewProjection);

    //Mette in Output la posizione del pixel sullo schermo e
    //sulla reflection map
    Output.Position = mul(inPos, WorldViewProjection);
    Output.ReflectionPos = mul(inPos, WorldReflectionViewProjection);

    return Output;
}

WaterPixelToFrame WaterPixelShader(WaterVertexToPixel inPixel)
{
    WaterPixelToFrame Output = (WaterPixelToFrame)0;        
    
    //Calcola le coordinate texture giuste. Ricordate che il prodotto
    //di una matrice combinata con un Vector4 restituisce un
    //Vector4 in cui le singole coordinate sono espresse in
    //coordinate normalizzate per il device grafico, e perciò
    //sia X che Y possono assumere valori tra -1 e 1. Tuttavia,
    //per indicare un pixel in una texture servono coordinate comprese
    //tra 0 e 1 (altrimeti si andrebbe fuori da bordi). L'operazione
    //matematica che "trasforma" un numero tra -1 e 1 in uno
    //proporzionalmente coordispondente tra 0 e 1 è per
    //l'appunto:
    //b = (a + 1) / 2 = a / 2 + 0.5
    float2 TexCoords;
    TexCoords.x = (inPixel.ReflectionPos.x / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
    //Qui c'è un meno perchè la coordinata Y cresce scendendo
    //verso il basso, mentre il risultato del prodotto matriciale prevede
    //una Y che decresce verso il basso
    TexCoords.y = -(inPixel.ReflectionPos.y / inPixel.ReflectionPos.w) / 2.0f + 0.5f;    

    //Preleva il colore dalla reflecion map a queste coordinate
    Output.Color = tex2D(ReflectionSampler, TexCoords);    
    
    return Output;
}

//Dichiara la tecnica
technique Water
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 WaterVertexShader();
        PixelShader = compile ps_2_0 WaterPixelShader();
    }
} 
e il metodo di disegno in XNA:
    Private Sub DrawWater(ByVal Time As Single)
        Shader.CurrentTechnique = Shader.Techniques("Water")

        Shader.Parameters("View").SetValue(ViewMatrix)
        Shader.Parameters("Projection").SetValue(ProjectionMatrix)
        Shader.Parameters("World").SetValue(Matrix.Identity)
        Shader.Parameters("ReflectionView").SetValue(ReflectionViewMatrix)
        Shader.Parameters("RefractionMap").SetValue(RefractionMap)
        Shader.Parameters("ReflectionMap").SetValue(ReflectionMap)

        'Attiva la CullMode = None solo per questa azione
        Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
        Me.GraphicsDevice.RenderState.CullMode = CullMode.None

        'Imposta i buffer
        Me.GraphicsDevice.Vertices(0).SetSource( _
            WaterVBuffer, 0, VertexPositionTexture.SizeInBytes)

        Shader.Begin()
        For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
            Pass.Begin()
            With Me.GraphicsDevice
                .VertexDeclaration = WDeclaration
                Me.GraphicsDevice.DrawPrimitives( _ 
                    PrimitiveType.TriangleList, 0, WaterVertices.Length / 3)
            End With
            Pass.End()
        Next
        Shader.End()

        Me.GraphicsDevice.RenderState.CullMode = Prev
    End Sub 
Dopo averlo inserito in Draw, come utlima istruzione, potrete avere come risultato questo:


Vedete l'acqua? No, è solo uno specchio







 

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