B3. Trasformazioni del mondo 3D
Spesso è utile modificare la posizione di tutti gli oggetti della scena per dare particolari effetti. Questo viene fatto manipolando la matrice dedicata al mondo, che, nel tutorial precedente, abbiamo chiamato World.
Traslazioni
Ora proveremo a spostare il triangolo disegnato poco fa, eseguendo una traslazione sulla matrice World, che però non è stata
ancora dichiarata. Allora, sotto a View e Projection, mettiamo anche World:
Ora vorremmo che, premendo i pulsanti direzionali il triangolo si sposti nelle relative direzioni. Per far questo c'è bisogno di memorizzare di quanto bisogna traslare, quindi servono altre due variabili: una per lo spostamento su X e una per quello su Z (ricordate che noi stiamo guardando dall'alto dritto verso il piano xz). Inoltre bisogna scrivere del codice nel metodo Update che controlli i tasti premuti, proprio come si faceva nella sezione A col 2D, e inoltre modificare Draw:Private ViewMatrixAs MatrixPrivate ProjectionMatrixAs MatrixPrivate WorldMatrixAs Matrix
Public Class Game '... Private DX, DZAs Single '... Protected Overrides Sub Update(ByVal GameTimeAs GameTime)Dim KeyStateAs KeyboardState = Keyboard.GetState 'Controlla i tasti premuti e modifica gli spostamenti di 'conseguenza. 'Ho messo 0.01 perchè il triangolo è molto 'piccolo: infatti i suoi cateti misurano solo un'unità. If KeyState.IsKeyDown(Keys.Up)Then DZ -= 0.01End If If KeyState.IsKeyDown(Keys.Down)Then DZ += 0.01End If If KeyState.IsKeyDown(Keys.Left)Then DX -= 0.01End If If KeyState.IsKeyDown(Keys.Right)Then DX += 0.01End If MyBase .Update(GameTime)End Sub '... Protected Overrides Sub Draw(ByVal gameTimeAs GameTime)Me .Graphics.GraphicsDevice.Clear(Color.CornflowerBlue) Shader.Parameters("View").SetValue(ViewMatrix) Shader.Parameters("Projection").SetValue(ProjectionMatrix) 'Ora la matrice World non sarà più inutile come 'prima. Dato che dobbiamo spostare il triangolo, creeremo 'una matrice di traslazione con la funzione statica 'Matrix.CreateTranslation. Essa accetta un unico parametro 'di tipo Vector3. In questo caso, siccome lo spostamento 'avviene sul piano xz, la coordinata y sarà nulla. Shader.Parameters("World").SetValue( _ Matrix.CreateTranslation(New Vector3(DX, 0, DZ)))Me .GraphicsDevice.RenderState.CullMode = CullMode.None 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
Rotazioni
È anche possibile fare ruotare tutto. Le rotazioni avvengono sempre intorno al punto di origine (0,0,0) o, se si tratta di rotazioni
su un solo asse, attorno al corrispondente asse cartesiano. Tutti gli angoli devono essere specificati in radianti. Per chi non
conoscesse cos'è un radiante, lo spiego brevemente.In trigonometria, si è soliti misurare tutti gli angoli in radianti poichè i valori così ottenuti sono particolarmente comodi per l'uso di funzioni quali seno, coseno, tangente, eccetera... Basti sapere che un angolo di 180 gradi equivale a un angolo di π radianti, perciò è possibile eseguire delle conversioni con una semplice proporzione: 180 : π = x(gradi) : y(radianti). In particolare, ci sono alcuni angoli usati molto di frequente, che sono:
- 30° = π / 6
- 45° = π / 4
- 60° = π / 3
- 90° = π / 2
Ora dovreste vedere il triangolo che ruota attorno al vertice rosso e, nel contempo, può ancora essere spostato con le frecce direzionali. A questo punto è necessario introdurre una essenziale chiarificazione.Public Class Game '... 'L'angolo di rotazione Private AngleAs Single '... Protected Overrides Sub Update(ByVal GameTimeAs GameTime)Dim KeyStateAs KeyboardState = Keyboard.GetStateIf KeyState.IsKeyDown(Keys.Up)Then DZ -= 0.01End If If KeyState.IsKeyDown(Keys.Down)Then DZ += 0.01End If If KeyState.IsKeyDown(Keys.Left)Then DX -= 0.01End If If KeyState.IsKeyDown(Keys.Right)Then DX += 0.01End If 'Aumentiamo automaticamente l'angolo. Con questo valore, 'il triangolo dovrebbe eseguire una rotazione di 180° 'in un secondo. Infatti la funzione Update viene 'chiamata circa 60 volte al secondo Angle += MathHelper.Pi / 60MyBase .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) 'Trasliamo e ruotiamo insieme. La composizione di due 'trasformazioni si effettua mediante moltiplicazione 'delle due matrici Shader.Parameters("World").SetValue( _ Matrix.CreateRotationY(Angle) * _ Matrix.CreateTranslation(New Vector3(DX, 0, DZ)))Me .GraphicsDevice.RenderState.CullMode = CullMode.None 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
Nei commenti del codice ho scritto che la composizione di due trasformazioni avviene mediante prodotto delle matrici che rapresentano tali trasformazioni. Non è necessario sapere come viene eseguita l'operazione, ma è importante essere al corrente del fatto che il prodotto matriciale NON è commutativo. In pratica, scrivere Rotazione*Traslazione e Traslazione*Rotazione dà due risultati diversi. Infatti, provate a scambiare le due operazioni: se il triangolo si trova esattamente al centro dello schermo, non c'è differenza, ma se viene traslato, non ruoterà più intorno al suo vertice rosso, ma descriverà una circonferenza intorno all'origine degli assi. Potete vedere la differenza in questi due video:
- Video 1 : Rotazione * Traslazione
Shader.Parameters("World").SetValue( _ Matrix.CreateRotationY(Angle) * _ Matrix.CreateTranslation(New Vector3(DX, 0, DZ))) - Video 2 : Traslazione * Rotazione
Shader.Parameters("World").SetValue( _ Matrix.CreateTranslation(New Vector3(DX, 0, DZ)) * _ Matrix.CreateRotationY(Angle))
Similitudini
La similitudine è la definizione "matematica" della più comune scala. È possibile ridurre o ingrandire tutto il mondo
o anche solo un particolare oggetto, proprio come abbiamo fatto con traslazioni e rotazioni. Non scriverò un nuovo codice anche per
le trasformazioni di scala, poiché mi sembra di aver già chiarito tutti i punti chiave delle trasformazioni mediante matrici.
L'unica cosa che c'è da sapere è che la funzione usata per creare un fattore di scala è Matrix.CreateScale(X, Y, Z), dove
X, Y e Z sono i diversi fattori di zoom per ogni asse. Infatti è possibile "allungare" o "ridurre" qualsiasi oggetto 3D anche in una
sola direzione, come se si trovasse su un foglio di gomma tirato alle estremità.Per questi tutorial metterò, alla fine del capitolo, il codice completo degli esempi.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



