B4. Gli Indici
Fin'ora ci siamo limitati a disegnare un solo triangolo. Proviamo ad aumentare un po' il numero.
Quattro triangoli, dodici vertici
Ora che abbiamo finito di sondare le matrici, ripartiamo dai triangoli e, anziché disegnarne uno solo, definiamo 12 vertici, per 4 triangoli,
in modo da formare due quadrati adiacenti:
E dovreste ottenere questo risltato:Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.InputImports Microsoft.Xna.Framework.GraphicsPublic Class GameInherits Microsoft.Xna.Framework.GamePrivate AppPathAs String = _My .Application.Info.DirectoryPathPrivate GraphicsAs GraphicsDeviceManagerPrivate ShaderAs EffectPrivate Vertices(11)As VertexPositionColorPrivate VDeclarationAs VertexDeclarationPrivate ViewMatrixAs MatrixPrivate ProjectionMatrixAs MatrixPrivate WorldMatrixAs MatrixPrivate DX, DZAs Single Private AngleAs Single Private Function GetEffect(ByVal FileNameAs String )As EffectDim CompEffectAs CompiledEffect = _ Effect.CompileEffectFromFile(FileName, _ Nothing, Nothing, _ CompilerOptions.None, _ TargetPlatform.Windows)Return New Effect(Me .GraphicsDevice, _ CompEffect.GetEffectCode, _ CompilerOptions.None,Nothing )End Function Private Sub SetVertices() 'Per questo esempio, disegneremo solo i contorni dei 'triangoli, tutti in bianco. Perciò imposteremo 'il colore di tutti i vertici alla fine 'Primo triangolo Vertices(0).Position =New Vector3(0, 0, 0) Vertices(1).Position =New Vector3(1, 0, -1) Vertices(2).Position =New Vector3(0, 0, -1) 'Secondo triangolo Vertices(3).Position =New Vector3(0, 0, 0) Vertices(4).Position =New Vector3(1, 0, -1) Vertices(5).Position =New Vector3(1, 0, 0) 'Terzo triangolo Vertices(6).Position =New Vector3(1, 0, 0) Vertices(7).Position =New Vector3(1, 0, -1) Vertices(8).Position =New Vector3(2, 0, -1) 'Quarto triangolo Vertices(9).Position =New Vector3(1, 0, 0) Vertices(10).Position =New Vector3(2, 0, -1) Vertices(11).Position =New Vector3(2, 0, 0) 'Imposta il colore di tutti For IAs Byte = 0To 11 Vertices(I).Color = Color.WhiteNext 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() Shader = GetEffect(AppPath & "\SimpleShader.fx") SetVertices() 'Per questo esempi ci spostiamo un po' più in alto 'e inoltre guardiamo i triangoli di sbieco ViewMatrix = Matrix.CreateLookAt( _New Vector3(-3, 6, 3), _New Vector3(0, 0, 0), _New Vector3(0, 1, 0)) ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _ MathHelper.PiOver4, _Me .GraphicsDevice.Viewport.AspectRatio, _ 1, 100)MyBase .LoadContent()End Sub Protected Overrides Sub UnloadContent()MyBase .UnloadContent()End Sub Protected Overrides Sub Update(ByVal GameTimeAs GameTime)MyBase .Update(GameTime)End Sub Protected Overrides Sub Draw(ByVal gameTimeAs GameTime)Me .Graphics.GraphicsDevice.Clear(Color.CornflowerBlue) Shader.CurrentTechnique = Shader.Techniques("Colored") Shader.Parameters("View").SetValue(ViewMatrix) Shader.Parameters("Projection").SetValue(ProjectionMatrix) 'Per ora teniamo l'identità Shader.Parameters("World").SetValue(Matrix.Identity)Me .GraphicsDevice.RenderState.CullMode = CullMode.None 'Ora vogliamo vedere solo i bordi dei triangoli. Questo 'si fa attivando l'opzione wireframe ("telaio di fili", 'o, più liberamente, "fil di ferro") Me .GraphicsDevice.RenderState.FillMode = FillMode.WireFrame Shader.Begin()For Each PassAs EffectPassIn 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
I quattro triangoli
Prima di procedere vorrei spiegare il primo parametro di DrawUserPrimitives, che non avevo spiegato nei tutorial precedenti. In sostanza è un enumeratore che descrive come devono essere trattati i vertici per disegnare i triangoli, e può assumere questi valori:
- LineList : non disegna triangoli, bensì linee (segmenti) semplici. Ogni coppia di vertici definisce due linee
- LineStrip : disegna sempre delle linee, ma tutte le linee disegnate risultano unite l'una con l'altra, in quanto il vertice finale di una coincide con quello iniziale dell'altra
- PointList : ogni vertice corrisponde a un singolo punto
- TriangleList : disegna dei triangoli isolati. Ogni gruppo di tre vertici definisce un triangolo
- TringleStrip : disegna dei triangoli connessi, dove un vertice è sempre in comune con un altro triangolo
- TriangleFan : disegna dei triangoli strettamente connessi, dove due vertici sono sempre in comune con un altro triangolo
Esempi di primitive
Bene, ora andiamo avanti: 6 triangoli, 18 vertici; 10 triangoli, 30 vertici; 100 triangoli, 300 vertici... Anche se forse è un po' eccessivo. Dobbiamo trovare una soluzione di qualche tipo per ridurre il numero dei vertici, se ne esiste una...
Uso degli indici
La soluzione c'è e va sotto il nome di Indici. Infatti, avrete notato che per dichiarare dei triangoli così attaccati,
abbiamo utilizzato più volte le stesse coordinate. Quindi, se riuscissimo a eliminare i doppioni avremmo bisogno di solo 6 vertici
per definire 4 triangoli, ossia la metà di quelli che usavamo prima. Per far questo, si assegna un numero intero (indice)
ad ogni vertice e invece di dire da quali vertici è formato un triangolo, diciamo da quali indici sono contrassegnati i
suoi vertici. Ad esempio, nel caso di prima possiamo prendere questa figura:
L'indice di ogni vertice è semplicemente il suo
indice nell'array
Allora il primo triangolo sarà formato dai vertici 1, 0, 2; il secondo da 2, 0, 3, eccetera... (notare che li ho dichiarati in senso orario stavolta). Allora iniziamo a scrivere il codice, modificando i vecchi metodi:
Potete anche provare a togliere questa riga di codice:Public Class Game '... 'Ora ci servono solo 6 vertici Private Vertices(5)As VertexPositionColor 'Ma dato che dovremo pur sempre definire tre 'punti per ogni triangolo, saranno necessari 'sempre 12 indici Private Indices(11)As Int32Private Sub SetVertices() 'Imposta i sei vertici Vertices(0).Position =New Vector3(0, 0, -1) Vertices(1).Position =New Vector3(0, 0, 0) Vertices(2).Position =New Vector3(1, 0, 0) Vertices(3).Position =New Vector3(1, 0, -1) Vertices(4).Position =New Vector3(2, 0, -1) Vertices(5).Position =New Vector3(2, 0, 0) 'Imposta il colore di tutti For IAs Byte = 0To 5 Vertices(I).Color = Color.WhiteNext 'Primo triangolo Indices(0) = 1 Indices(1) = 0 Indices(2) = 2 'Secondo triangolo Indices(3) = 2 Indices(4) = 0 Indices(5) = 3 'Terzo triangolo Indices(6) = 2 Indices(7) = 3 Indices(8) = 4 'Quarto triangolo Indices(9) = 2 Indices(10) = 4 Indices(11) = 5 VDeclaration =New VertexDeclaration(Me .GraphicsDevice, _ VertexPositionColor.VertexElements)End Sub '... Protected Overrides Sub Draw(ByVal gameTimeAs GameTime)Me .Graphics.GraphicsDevice.Clear(Color.CornflowerBlue) Shader.CurrentTechnique = Shader.Techniques("Colored") Shader.Parameters("View").SetValue(ViewMatrix) Shader.Parameters("Projection").SetValue(ProjectionMatrix) 'Per ora teniamo l'identità Shader.Parameters("World").SetValue(Matrix.Identity)Me .GraphicsDevice.RenderState.CullMode = CullMode.NoneMe .GraphicsDevice.RenderState.FillMode = FillMode.WireFrame Shader.Begin()For Each PassAs EffectPassIn Shader.CurrentTechnique.Passes Pass.Begin()With Me .GraphicsDevice .VertexDeclaration = VDeclaration 'Dato che ora usiamo gli indici, dobbiamo cambiare 'metodo: sostituiamo quello vecchio con questo. 'I primi tre parametri sono uguali. Il quarto 'definisce quanti vertici siano da considerare (noi 'li prendiamo tutti). Il quinto specifica l'array 'da cui estrarre gli indici (Indices), mentre il 'sesto imposta l'offset, che è sempre 0. 'L'ultimo richiede il numero di triangoli da disegnare: 'dato che ogni triangolo è determinato da tre 'indici, il numero di triangoli sarà '1/3 di quello degli indici. .DrawUserIndexedPrimitives(PrimitiveType.TriangleList, _ Vertices, 0, Vertices.Length, _ Indices, 0, Indices.Length / 3)End With Pass.End()Next Shader.End()MyBase .Draw(gameTime)End Sub End Class
e vedrete che il risultato è lo stesso: infatti, nella definizione degli indici, ho di proposito messo tutti i numeri in senso orario, in modo che vengano visualizzati dalla telecamera anche senza l'opzione CullMode=None.Me .GraphicsDevice.RenderState.CullMode = CullMode.None
Adesso, se anche volessimo cambiare un vertice, potremmo anche stare tranquilli che verrebbe tutto renderizzato nel migliore dei modi. Ad esempio, alziamone uno (l'ultimo) di un'unità. Senza cambiare niente otteniamo un bel risultato:
Un vertice alzato
Il nostro obbiettivo è disegnare un intero paesaggio usando queste tecniche, ma farlo a mano è un po' (molto) noioso. Nel prossimo tutorial cercheremo di automatizzare il tutto.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



