Σάββατο 25 Δεκεμβρίου 2010

XNA - Περιστροφές και Μετατοπίσεις

Μάθημα 6ο

  • Για ευκολία, ορίζουμε το παρακάτω τρίγωνο
    vertices[0].Position = new Vector3(0f, 0f, 0f);
    vertices[1].Position = new Vector3(10, 10f, 0f);
    vertices[2].Position = new Vector3(20f, 0f, 0f);
  • Το τρίγωνο μας κάθεται ακίνητο εκεί που το δημιουργήσαμε, και ο μόνος τρόπος να κινηθεί στην οθόνη είναι να μεταφέρουμε την κάμερα. Τώρα θα μπορέσουμε να μετατοπίσουμε και να περιστρέψουμε το ίδιο το τρίγωνο, και αυτό θα μπορεί να γίνει και σε πραγματικό χρόνο. Ξεκινάμε ορίζοντας μια μεταβλητή angle στην αρχή στης κλάσης μας:
    float angle = 0f;
    Τώρα θέλουμε σε κάθε καρέ (60 καρέ το δευτερόλεπτο) το angle να αυξάνεται. Επομένως ένα πολύ καλό μέρος να τοποθετήσουμε αυτήν την αύξηση είναι στην μέθοδο Update:

    angle += 0.005f;
    Το μόνο που χρειαζόμαστε πια είναι να περιστρέψουμε τις world coordinates. Αυτό θα γίνει με μια μήτρα μετασχηματισμού. Ευτυχώς δεν χρειάζεται να ορίσουμε εμείς αυτήν την μήτρα, το XNA θα ασχοληθεί με αυτήν όταν του δώσουμε τον άξονα περιστροφής και την γωνία περιστροφής. Προσθέτουμε λοιπόν τον παρακάτω κώδικα στην κλάση Draw πριν το effect.Begin():

    Matrix worldMatrix = Matrix.CreateRotationY(3 * angle);
    myEffect.Parameters["xWorld"].SetValue(worldMatrix);
    Η τελευταία γραμμή αντικαθιστά μια άλλη γραμμή που είχαμε ήδη ορίσει, και αντί για worldMatrix είχε παράμετρο το "Matrix.Identity". Αυτός ο μετασχηματισμός αφορά τις συντεταγμένες του κόσμου, οπότε ότι και να σχεδιάσουμε θα αρχίσει να περιστρέφεται μαζί με τα υπόλοιπα αντικείμενα.

    Όπως φαίνεται, δημιουργούμε μια μήτρα worldMatrix η οποία περιέχει μια περιστροφή γύρω από τον άξονα Y. Αυτή την μήτρα την δίνουμε στις παραμέτρους της effect πριν αυτή ξεκινήσει να σχεδιάζει.

  • Όπως φαίνεται, το τρίγωνο περιστρέφεται γύρω από το σημείο (0,0,0) διότι από εκεί περνάει ο άξονας Y. Τι θα γίνει όμως αν θέλουμε να περιστρέψουμε το τρίγωνο γύρω από το κέντρο του?

    Μια καλή λύση θα ήταν να μετατοπίσουμε πρώτα το τρίγωνο ώστε να βρίσκεται στο κέντρο του κόσμου μας, και μετά να το περιστρέψουμε. Για να γίνει αυτό πρέπει να πολλαπλασιάσουμε την μήτρα περιστροφής με μια μήτρα μετατόπισης. Στην μέθοδο Draw γράφουμε:

    Matrix worldMatrix = Matrix.CreateTranslation(-10f,0, 0) * Matrix.CreateRotationY(angle); 
    αντικαθιστώντας τον προηγούμενο κώδικα που υπήρχε για τον ορισμό του worldMatrix.

    Στην πραγματικότητα δεν πειράζουμε τις συντεταγμένες του τριγώνου, αλλά το πως αυτές προβάλλονται στην οθόνη μας. Η περιστροφή και η μετατόπιση εφαρμόζονται στα ήδη υπάρχοντα σημεία του τριγώνου, τα οποία έχουν μείνει ανέπαφα.

    Προσοχή διότι η σειρά με την οποία πολλαπλασιάζονται οι μήτρες έχει σημασία. Αν πρώτα περιστραφεί και μετά μετατοπιστεί θα έχουμε άλλο αποτέλεσμα. (θα περιστρέφεται γύρω από ένα ακίνητο σημείο στην γωνία).

  • Ένα τελευταίο ζήτημα είναι η περιστροφή γύρω από δικό μας άξονα. Πρώτα φτιάχνουμε ένα διάνυσμα που θα καθορίζει τον άξονα περιστροφής. Έπειτα κανονικοποιούμε το διάνυσμα ώστε να έχει μέτρο 1. Τέλος αλλάζουμε για τελευταία φορά το worldMatrix, οπότε στην μέθοδο Draw γράφουμε:

    Vector3 rotAxis = new Vector3(3, 1, 2); 
    rotAxis.Normalize();
    Matrix worldMatrix = Matrix.CreateTranslation(-10.0f, 0, 0) * Matrix.CreateFromAxisAngle(rotAxis, angle);
    Η CreateFromAxisAngle δημιουργεί μια μήτρα περιστροφής με βάση ένα κανονικοποιημένο διάνυσμα και μια γωνία. Για αυτό τον λόγο έπρεπε να κανονικοποιήσουμε το διάνυσμα.

    Επειδή είναι πιο ωραίο να μπορεί κανείς να δει δύο τρίγωνα μαζί, ο παρακάτω κώδικας θα περιέχει μια ελαφριά παραλλαγή των όσων κάναμε εδώ.

Το 6ο μάθημα έφτασε στο τέλος του.



Το τρίγωνο περιστρέφεται σε έναν καινούργιο άξονα

Ασχολίες για εξάσκηση:
  1. Δοκιμάστε να αλλάξετε τους άξονες περιστροφής
  2. Αλλάξτε την μετατόπιση των συντεταγμένων
  3. Δοκιμάστε να αλλάξετε την σειρά πολλαπλασιασμού στις μήτρες
Πηγές: Riemer's Tutorial

_________________________________________

Απλοποιημένος κώδικας (χωρίς περιττά σχόλια)





using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace SandboxGame
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        GraphicsDevice Gdevice;

        Effect myEffect;

        VertexPositionColor[] vertices;

        VertexDeclaration myVertexDeclaration;

        Matrix viewMatrix;
        Matrix projectionMatrix;

        private void SetUpCamera()
        {
            viewMatrix = Matrix.CreateLookAt(new Vector3(0f, 50.0f, 50.0f), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Gdevice.Viewport.AspectRatio, 1.0f, 300.0f);
        }

        private void SetUpVertices()
        {
            myVertexDeclaration = new VertexDeclaration(Gdevice, VertexPositionColor.VertexElements);
            vertices = new VertexPositionColor[6];
            vertices[0].Position = new Vector3(0f, 0f, 0f);
            vertices[1].Position = new Vector3(10, 10f, 0f);
            vertices[2].Position = new Vector3(20f, 0f, 0f);

            vertices[0].Color = Color.Blue;
            vertices[1].Color = Color.Green;
            vertices[2].Color = Color.Yellow;

            vertices[3].Position = new Vector3(0f, 0f, 0f);
            vertices[4].Position = new Vector3(10, -10f, 0f);
            vertices[5].Position = new Vector3(20f, 0f, 0f);

            vertices[3].Color = Color.Blue;
            vertices[4].Color = Color.Red;
            vertices[5].Color = Color.Yellow;
        }

        // Constructor
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        // Init
        protected override void Initialize()
        {
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredBackBufferWidth = 800;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();
            Window.Title = "Το αλμυρό φυστίκι";

            base.Initialize();
        }

        // Load Content
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Gdevice = graphics.GraphicsDevice;
            myEffect = Content.Load("effects");

            SetUpVertices();
            SetUpCamera();
        }

        protected override void UnloadContent()
        {

        }

        // Update (60 times per second)
        protected override void Update(GameTime gameTime)
        {
            angle += 0.005f;

            base.Update(gameTime);
        }

        float angle = 0f;

        // Draw
        protected override void Draw(GameTime gameTime)
        {
            Gdevice.Clear(Color.CornflowerBlue);

            Gdevice.RenderState.CullMode = CullMode.None;

            myEffect.Parameters["xView"].SetValue(viewMatrix);
            myEffect.Parameters["xProjection"].SetValue(projectionMatrix);

            // define worldMatrix
            Vector3 rotAxis = new Vector3(3 , 1, 2);
            rotAxis.Normalize();
            Matrix worldMatrix = Matrix.CreateTranslation(-10f,0, 0) * Matrix.CreateFromAxisAngle(rotAxis,3*angle);

            myEffect.Parameters["xWorld"].SetValue(worldMatrix);
            // worldMatrix used

            myEffect.CurrentTechnique = myEffect.Techniques["Colored"];

            myEffect.Begin();

            foreach (EffectPass pass in myEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                Gdevice.VertexDeclaration = myVertexDeclaration;
                Gdevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, 2);

                pass.End();
            }
            myEffect.End();

            base.Draw(gameTime);
        }
    }
}

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου