Casino online









Mercato forex






B2. Triangoli e matrici


Il progetto iniziale è un semplicissimo sorgente, quasi vuoto. Qui potete scaricare il progetto dal quale inizieremo.


In principio fu il triangolo
Come forse saprete, ogni modello tridimensionale, anche quello che all'apparenza sembra liscio e curvo, è costituito da un gran numero di triangoli, ossia la figura geometrica più semplice in assoluto. Proprio per questa sua natura particolare, il triangolo viene chiamato primitiva: ogni cosa che andremo a disegnare sarà costituita da triangoli. Dato che ogni primitiva ha tre lati e tre vertici, saranno necessari almeno tre punti per determinare un triangolo: tali punto sono chiamati, banalmente, vertici. Un vertice non indica soltanto una posizione nello spazio 3D, ma il più delle volte porta con sé altre informazioni molto utili. Nel caso più semplice, queste informazioni rappresentano il colore del vertice. Disegnando in questo modo, il triangolo finale sarà composto da tre diversi colori, che si mischiano gli uni con gli altri all'interno della sua superficie. Ecco un piccolo esempio:
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports Microsoft.Xna.Framework.Graphics

Public Class Game
    Inherits Microsoft.Xna.Framework.Game

    'Definiamo questa variabile in modo da non dover
    'scrivere troppo in seguito. Contiene il percorso
    'della cartella del programma
    Private AppPath As String = _
        My.Application.Info.DirectoryPath

    Private Graphics As GraphicsDeviceManager

    'Questo oggetto rappresenta l'effetto che useremo per
    'disegnare il triangolo
    Private Shader As Effect
    'Questo array contiene tutti i vertici necessari a disegnare
    'un triangolo. Bisogna notare che il tipo usato ci dice
    'quali informazioni possiamo includere in ogni elemento
    'dell'array. In questo caso la struttura VertexPositionColor
    'indica che ogni vertice (vertex) avrà una posizione 
    '(position) e un colore (color):
    Private Vertices(2) As VertexPositionColor
    'Questa variabile è molto importante, perchè comunica
    'alla GPU quali sono i dati in ingresso, qual è la loro
    'funzione e quanta memoria utilizzare
    Private VDeclaration As VertexDeclaration

    'Dichiariamo questa funzione subito. In poche parole, serve
    'per caricare uno shader da un file *.fx
    Private Function GetEffect(ByVal FileName As String) As Effect
        Dim CompEffect As CompiledEffect = _
            Effect.CompileEffectFromFile(FileName, _
            Nothing, Nothing, _
            CompilerOptions.None, _
            TargetPlatform.Windows)

        Return New Effect(Me.GraphicsDevice, _
            CompEffect.GetEffectCode, _
            CompilerOptions.None, Nothing)
    End Function

    'Questa procedura imposta i campi di ogni vertice
    Private Sub SetVertices()
        'Primo vertice, rosso, in posizione (0,0,0)
        Vertices(0).Position = New Vector3(0, 0, 0)
        Vertices(0).Color = Color.Red

        'Secondo vertice, giallo, in posizione (1,0,-1)
        'Ricordate che y indica lo spostamento alto-basso
        'e z avanti-indietro. -1 significa che il vertice
        'è leggermente spostato in avanti rispetto
        'all'asse x
        Vertices(1).Position = New Vector3(1, 0, -1)
        Vertices(1).Color = Color.Yellow

        'Terzo vertice, blu, in posizione (0,0,-1)
        Vertices(2).Position = New Vector3(0, 0, -1)
        Vertices(2).Color = Color.Blue

        'Inizializza l'oggetto VertexDeclaration. In questo
        'caso esso dice alla GPU che i dati in input sono
        'di tipo VertexPositionColor. Vedremo cosa significhi
        'il campo VertexElements in seguito
        VDeclaration = New VertexDeclaration(Me.GraphicsDevice, _
            VertexPositionColor.VertexElements)
    End Sub

    Sub New()
        Me.Graphics = New GraphicsDeviceManager(Me)
        Me.Content.RootDirectory = "content"
    End Sub

    Protected Overrides Sub Initialize()
        MyBase.Initialize()
    End Sub

    Protected Overrides Sub LoadContent()
        'Carica l'effetto dalla cartella del programma
        Shader = GetEffect(AppPath & "\SimpleShader.fx")
        'Carica i vertici
        SetVertices()

        MyBase.LoadContent()
    End Sub

    Protected Overrides Sub UnloadContent()
        MyBase.UnloadContent()
    End Sub

    Protected Overrides Sub Update(ByVal GameTime As GameTime)
        MyBase.Update(GameTime)
    End Sub

    Protected Overrides Sub Draw(ByVal gameTime As GameTime)
        Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)

        'Imposta la tecnica da usare. Niente di speciale per
        'ora. La tecnica "Colored" semplicemente prende la
        'posizione e il colore di un vertice e li disegna
        'sullo schermo
        Shader.CurrentTechnique = Shader.Techniques("Colored")

        'Questa impostare serve per visualizzare il triangolo. Infatti,
        'per risparmiare memoria, la GPU visualizza solo i triangoli 
        'rivolti verso la telecamera (ossia i cui vertici sono 
        'definiti in senso orario).
        'Dato che non abbiamo controllato se i vertici sono 
        'definiti in questo modo, diciamo semplicemente di disegnare
        'tutti i triangoli esistenti, tanto ce ne sarà solo uno.
        'Ricordatevi dell'opzione CullMode, può tornare utile
        'per scovare difetti nel posizionamento
        Me.GraphicsDevice.RenderState.CullMode = CullMode.None

        'Inizia l'effetto
        Shader.Begin()
        'Altra cosa importante. Ogni tecnica può essere
        'divisa in più parti, da applicare in successione.
        'Anche se noi useremo sempre una sola parte (passo),
        'usiamo un ciclo For per essicurare la massima
        'compatibilità
        For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
            'Inizia il passo
            Pass.Begin()
            With Me.GraphicsDevice
                'Passa la dichiarazione dei vertici al device grafico
                .VertexDeclaration = VDeclaration
                
                'Disegna la primitiva sullo schermo. Il primo parametro
                'verrà analizzato in seguito. Il secondo parametro 
                'specifica l'array da cui attingere i dati dei vertici. 
                'Il terzo indica l'offset da cui iniziare (0, perchè 
                'iniziamo dal primo vertice). Il quarto indica quanti 
                'triangoli disegnare. Dato che un triangolo è 
                'definito da tre vertici, il numero di triangoli sarà 
                '1/3 di quello dei vertici.
                .DrawUserPrimitives(PrimitiveType.TriangleList, _ 
                    Vertices, 0, Vertices.Length / 3)
            End With
            'Termina il passo
            Pass.End()
        Next
        'Finisce l'effetto
        Shader.End()

        MyBase.Draw(gameTime)
    End Sub
End Class 
Fate correre il tutto et voilà! Emh... un bellissimo schermo azzurro. E il triangolo dove è finito?


Usare la telecamera
Ah, ecco dov'era l'errore! Non abbiamo specificato nessun punto di vista, quindi è come se non stessimo guardando. Per specificare tutte le impostazioni relative alla telecamera e molto altro ancora si usano le matrici. In queste circostanze, una matrice non è solo un array a due dimensioni, come potrete aver pensato, ma si tratta di un vero e proprio strumento matematico, a dir la verità molto potente. Non è essenziale conoscere il funzionamento intrinseco delle matrici per quanto riguarda l'algebra: basti sapere che una matrice rappresenta, grossomodo, una tabella contenente valori numerici, sulla quale possono essere eseguite operazioni matematiche. Se ce ne sarà bisogno, specificherò io stesso le proprietà che si devono conoscere, tuttavia nessuno vieta di approfondire l'argomento.
Come dicevo, la telecamera viene direzionata e governata da matrici, in genere due: la matrice di vista (View) e quella di proiezione (Projection). La prima specifica la posizione dell'osservatore (in questo caso, l'utente) e il punto verso il quale l'osservatore sta guardando; la seconda, invece, ci dice quanta parte di "mondo" possiamo vedere, quanto lontano e quanto vicino possiamo mettere a fuoco gli oggetti. Fortunatamente, esiste già la classe Matrix, che rappresenta la matrice, e tale classe espone già una marea di funzioni che possiamo usare per creare facilmente matrici per tutti gli usi possibili. Potremmo dividere i tipi di matrici in due gruppi: matrici di impostazione e matrici di trasformazione. Ora useremo quelle appartenenti al primo gruppo.
Nel codice di prima, aggiungiamo il codice necessario:
Public Class Game
    '...
    
    'Matrice di vista
    Private ViewMatrix As Matrix
    'Matrice di proiezione
    Private ProjectionMatrix As Matrix
    
    '...
    
    Protected Overrides Sub LoadContent()
        Shader = GetEffect(AppPath & "\SimpleShader.fx")
        SetVertices()

        'Imposta ViewMatrix con la funzione Matrix.CreateLookAt, che ha
        'il precipuo scopo di creare una matrica di vista.

        'Il primo argomento è la posizione dell'oservatore. Dato che il
        'triangolo che abbiamo tracciato giace su un solo piano (xz),
        'posizioniamo la telecamera 3 unità al di sopra di esso.

        'Il secondo argomento è il punto da guardare. In questo esempio
        'fisseremo l'origine degli assi e, secondo la definizione di
        'vertici che abbiamo dato, dovremmo vedere il vertice rosso
        'esattamente al centro dello schermo.

        'Il terzo parametro è un vettore speciale, che definisce la
        'direzione "in alto" rispetto all'osservatore (serve per 
        'controllare la giusta angolazione). Dato che noi stiamo 
        'guardando dall'asse Y direttamente verso il centro del 
        'sistema cartesiano, abbiamo gli occhi paralleli al piano xz. 
        'Quindi, per noi, il vettore "in alto" corrisponde a un 
        'vettore parallelo a xz che si diriga nel verso negativo 
        'di z. Se non avete capito, guardate lo schema a seguire.
        ViewMatrix = Matrix.CreateLookAt( _
            New Vector3(0, 3, 0), _
            New Vector3(0, 0, 0), _
            New Vector3(0, 0, -1))

        'Imposta ProjectionMatrix con la funzione
        'Matrix.CreatePerspectiveFieldOfView.

        'Il primo argomento specifica quale porzione di mondo possiamo
        'osservare. Questo parametro consiste in un angolo, misurato
        'in radianti, che dice qual è l'ampiezza del nostro campo
        'visivo. Normalmente, un uomo ha un campo visivo di 45°-60°, ma
        'esistono animali, come il camaleonte, che riescono ad arrivare
        'molto oltre, fino a quasi 180°. Noi specificheremo 45°, che in
        'radianti corrisponde a π/4. La classe MathHelper è
        'una specie di ampliamento di System.Math ed espone come costante
        'anche il valore di cui abbiamo bisogno.

        'Il secondo argomento specifica qual è la proporzione tra
        'larghezza e altezza dello schermo. Non abbiamo bisogno di
        'inventarci niente, poiché la proprietà Viewport.AspectRatio
        'definisce già questo valore.

        'Il terzo argomento è detto Near Clipping Plane e
        'costituisce la distanza minima per far sì che un oggetto
        'venga renderizzato. In pratica, tutti gli oggetti che distano
        'dal nostro punto di vista meno di una unità non verranno
        'disegnati sullo schermo.

        'Il quarto argomento è detto Far Clipping Plane e
        'costituisce la distanza massima per far sì che un oggetto
        'venga renderizzato. In pratica, tutti gli oggetti che distano
        'dal nostro punto di vista più di 100 unità non verranno
        'disegnati sullo schermo.
        ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
            MathHelper.PiOver4, _
            Me.GraphicsDevice.Viewport.AspectRatio, _
            1, 100)

        MyBase.LoadContent()
    End Sub
    
    Protected Overrides Sub Draw(ByVal gameTime As GameTime)
        Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)

        'Imposta la tecnica da usare. Niente di speciale per
        'ora. La tecnica "Colored" semplicemente prende la
        'posizione e il colore di un vertice e li disegna
        'sullo schermo
        Shader.CurrentTechnique = Shader.Techniques("Colored")
        'Per far sì che il triangolo venga davvero disegnato,
        'è necessario passare come parametri le matrici alla
        'tecnica che si sta utilizzando, proprio come si farebbe
        'con un metodo in vb.net. La proprietà Effect.Parameters
        'è un dizionario che contiene i parametri della
        'tecnica corrente. Vedremo più in là questo
        'meccanismo nel dettaglio.
        Shader.Parameters("View").SetValue(ViewMatrix)
        Shader.Parameters("Projection").SetValue(ProjectionMatrix)
        'Ah, ultima cosa. Bisogna anche specificare una matrice che
        'dica quali sono le trasformazioni del "mondo 3D". Questo tipo
        'di matrice appartiene alle matrici di trasformazione. Dato
        'che per ora non ne abbiamo bisogno, inseriamo come
        'parametro l'Identità, uno speciale tipo
        'di matrice che costituisce l'elemento neutro del
        'prodotto matriciale, proprio come l'uno nel prodotto
        'tra numeri. In breve, questa matrice non influenza
        'in nessun modo il triangolo.
        Shader.Parameters("World").SetValue(Matrix.Identity)

        Me.GraphicsDevice.RenderState.CullMode = CullMode.None

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

        MyBase.Draw(gameTime)
    End Sub
End Class 



Schema riassuntivo

E il fantastico risultato (il vertice rosso è al centro come ci aspettavamo):


Schema riassuntivo






 

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